<commit_before>(function() {
	var Utilities = {
		random: function(start, end) {
			var range = end - start;
			var randomNumber = Math.random() * range + start;
			return randomNumber;
		},

		minMax: function(value, min, max) {
			if (typeof min !== "undefined") {
				value = value < min ? min : value;
			}
			if (typeof max !== "undefined") {
				value = value > max ? max : value;
			}
			return value;
		},

		roundTo: function(num, decimalPlaces) {
			return Math.round(num * 10 * decimalPlaces) / (10 * decimalPlaces)
		},

		copyObject: function(originalObject) {
			return JSON.parse(JSON.stringify(originalObject));
		}
	};

	var CONSTS = {
		TREE_COUNT: 9
	};

	var ATTRIBUTES = {
		length: {
			initialValue: 65,
			varyBy: 10,
			min: 25,
			max: 100
		},
		divergence: {
			initialValue: 35,
			varyBy: 30,
			min: 5,
			max: 70
		},
		reduction: {
			initialValue: 0.625,
			varyBy: 0.3,
			min: 0.5,
			max: 0.75
		},
		lineWidth: {
			initialValue: 6,
			varyBy: 0
		},
		branchings: {
			initialValue: 0,
			varyBy: 2,
			min: 0,
			max: 8,
			round: true
		}
	};

	var TreeModel = function(parent) {
		var self = this;
		var attributeNamesArray = [];

		parent = parent || {attributes:{}};
		this.attributes = {};

		// Takes an optional parent - to allow for re-initialization.
		this.initialize = function(parentTree) {
			parent = parentTree || parent;
			for (var attributeName in ATTRIBUTES) {
				attributeNamesArray.push(attributeName);
				self.attributes[attributeName] = parent.attributes[attributeName] || ATTRIBUTES[attributeName].initialValue;
			}
			self.randomize();
		};

		this.randomize = function() {
			// Selecting a random attribute
			var attributeToRandomize = attributeNamesArray[Math.floor(Math.random()*attributeNamesArray.length)];
			var attribute = ATTRIBUTES[attributeToRandomize];
			var varyBy = attribute.varyBy;

			// Adding/subtracting a random amount from that attribute
			var newValue = self.attributes[attributeToRandomize] + Utilities.random(-1*varyBy, varyBy);

			// Ensuring that the value is within the bounds, and properly rounded.
			newValue = Utilities.minMax(newValue, attribute.min, attribute.max);
			if (attribute.round) {
				newValue = Math.round(newValue);
			} else {
				// Otherwise, just round to max 3 decimal places.
				newValue = Utilities.roundTo(newValue, 2)
			}

			self.attributes[attributeToRandomize] = newValue;
		};

		this.getAttributes = function() {
			return Utilities.copyObject(self.attributes);
		};

		this.setAttributes = function(attributes) {
			if (Array.isArray(attributes)) {
				for (var i = 0; i < attributes.length; i++) {
					self.attributes[attributeNamesArray[i]] = parseFloat(attributes[i]);
				}
			} else {
				for (var attributeName in attributes) {
					self.attributes[attributeName] = parseFloat(attributes[attributeName]);
				}
			}
		}

		this.initialize();
	};

	/* Adapted from http://thecodeplayer.com/walkthrough/create-binary-trees-using-javascript-and-html5-canvas?s=rl */
	var TreeView = function(canvas) {
		var ctx = canvas.getContext("2d");
		//Lets resize the canvas to occupy the full page
		var W = canvas.offsetWidth;
		var H = canvas.offsetHeight;
		canvas.width = W;
		canvas.height = H;

		//Some variables
		var length, divergence, reduction, lineWidth, branchings, start_points = [];

		this.draw = function(attributes) {
			//clearing the canvas
			ctx.clearRect (0, 0, W, H);

			//Lets draw the trunk of the tree
			//length of the trunk - 100-150
			length = attributes.length;
			//angle at which branches will diverge - 10-60
			divergence = attributes.divergence;
			//Every branch will be 0.75times of the previous one - 0.5-0.75
			reduction = attributes.reduction;
			//width of the branch/trunk
			lineWidth = attributes.lineWidth;
			//number of branchings
			branchings = attributes.branchings;
			branchings ? branchings < 0 : 0;

			//This is the end point of the trunk, from where branches will diverge
			var trunk = {x: W/2, y: length + 5, angle: 90};
			//It becomes the start point for branches
			start_points = []; //empty the start points on every init();
			start_points.push(trunk);

			//Y coordinates go positive downwards, hence they are inverted by deducting it
			//from the canvas height = H
			ctx.beginPath();
			ctx.moveTo(trunk.x, H - 5);
			ctx.lineTo(trunk.x, H - trunk.y);
			ctx.strokeStyle = "brown";
			ctx.lineWidth = lineWidth;
			ctx.stroke();

			branches();
		}

		//Lets draw the branches now
		function branches(callNum) {
			callNum = callNum || 0;

			//reducing lineWidth and length
			length = length * reduction;
			lineWidth = lineWidth * reduction;
			ctx.lineWidth = lineWidth;

			var new_start_points = [];
			ctx.beginPath();
			for(var i = 0; i < start_points.length; i++) {
				var sp = start_points[i];
				//2 branches will come out of every start point. Hence there will be
				//2 end points. There is a difference in the divergence.
				var ep1 = get_endpoint(sp.x, sp.y, sp.angle + divergence, length);
				var ep2 = get_endpoint(sp.x, sp.y, sp.angle - divergence, length);

				//drawing the branches now
				ctx.moveTo(sp.x, H - sp.y);
				ctx.lineTo(ep1.x, H - ep1.y);
				ctx.moveTo(sp.x, H - sp.y);
				ctx.lineTo(ep2.x, H - ep2.y);

				//Time to make this function recursive to draw more branches
				ep1.angle = sp.angle + divergence;
				ep2.angle = sp.angle - divergence;

				new_start_points.push(ep1);
				new_start_points.push(ep2);
			}
			//Lets add some more color
			if (length < 10) {
				ctx.strokeStyle = "green";
			} else {
				ctx.strokeStyle = "brown";
			}
			ctx.stroke();
			start_points = new_start_points;
			//recursive call
			if (callNum < branchings) {
				//setTimeout(function(){
					branches(callNum+1);
				//}, 50);
			}
		}

		function get_endpoint(x, y, a, length) {
			//This function will calculate the end points based on simple vectors
			//http://physics.about.com/od/mathematics/a/VectorMath.htm
			//You can read about basic vectors from this link
			var epx = x + length * Math.cos(a * Math.PI/180);
			var epy = y + length * Math.sin(a * Math.PI/180);
			return {x: epx, y: epy};
		}

		this.setRoot = function() {
			canvas.classList.add("root");
		}

		this.unsetRoot = function() {
			canvas.classList.remove("root");
		}

		this.onClick = function(callback) {
			canvas.addEventListener("click", function() {
				callback();
			});
		}
	}

	var TreeController = (function() {
		var root;
		var trees;
		var treeCanvases = document.querySelectorAll(".tree");
		var attributeHashes = [];
		var ignoreHash;

		function getAttributesHash(attributes) {
			var hash = "";
			for (var attributeName in attributes) {
				hash += attributes[attributeName] + "_";
			}
			return hash.substring(0, hash.length - 1);
		}

		function setupTree(index, parent) {
			if (!trees[index]) {
				tree = trees[index] =  {};
				tree.model = new TreeModel(parent);
				tree.view = new TreeView(treeCanvases[index]);
				tree.view.onClick(function() {
					spawnChildren(index);
				});
				tree.index = index;
			} else {
				tree = trees[index];
				tree.model.initialize(parent);
			}
			tree.view.draw(tree.model.getAttributes());
			attributeHashes[index] = getAttributesHash(tree.model.getAttributes());
			return tree;
		}

		function spawnChildren(index) {
			setRoot(index)
			for (var i = 0; i < trees.length; i++) {
				if (i === index) {
					continue;
				}
				setupTree(i, root.model);
			}

			// Set a flag, so the hash watched doesn't fire.
			ignoreHash = true;
			hasher.setHash(index + ":" + attributeHashes.join(","));
			setTimeout(function(){
				ignoreHash = false;
			}, 50);
		}

		function setRoot(index) {
			if (root && root.view) {
				root.view.unsetRoot();
			}
			//if (trees[index] && trees[index].view)
			root = trees[index];
			root.view.setRoot();
		}

		// function to read the hash value and use it as the tree attributes.
		function readHash(hash) {
			if (ignoreHash || hash === "") {
				return;
			}
			var split = hash.split(":");
			setRoot(split[0]);
			var attributeSets = split[1].split(",");
			trees.forEach(function(tree, index) {
				tree.model.setAttributes(attributeSets[index].split("_"))
				tree.view.draw(tree.model.getAttributes())
			});
		}

		function init() {
			setup();

			hasher.initialized.add(readHash); //add initialized listener (to grab initial value in case it is already set)
			hasher.changed.add(readHash);
			hasher.init(); //initialize hasher (start listening for history changes)
		}

		function setup() {
			trees = new Array(CONSTS.TREE_COUNT);
			root = setupTree(0);
			spawnChildren(0);
		}

		document.getElementById("reset").addEventListener("click", function() {
			setup();
		});

		return {
			init: init
		}
	})();

	TreeController.init();
})();<commit_msg>Cleanup and adding reset button<commit_after>(function() {
	var Utilities = {
		random: function(start, end) {
			var range = end - start;
			var randomNumber = Math.random() * range + start;
			return randomNumber;
		},

		minMax: function(value, min, max) {
			if (typeof min !== "undefined") {
				value = value < min ? min : value;
			}
			if (typeof max !== "undefined") {
				value = value > max ? max : value;
			}
			return value;
		},

		roundTo: function(num, decimalPlaces) {
			return Math.round(num * 10 * decimalPlaces) / (10 * decimalPlaces)
		},

		copyObject: function(originalObject) {
			return JSON.parse(JSON.stringify(originalObject));
		}
	};

	var CONSTS = {
		TREE_COUNT: 9
	};

	var ATTRIBUTES = {
		length: {
			initialValue: 65,
			varyBy: 10,
			min: 25,
			max: 100
		},
		divergence: {
			initialValue: 35,
			varyBy: 30,
			min: 5,
			max: 70
		},
		reduction: {
			initialValue: 0.625,
			varyBy: 0.3,
			min: 0.5,
			max: 0.75
		},
		lineWidth: {
			initialValue: 6,
			varyBy: 0
		},
		branchings: {
			initialValue: 0,
			varyBy: 2,
			min: 0,
			max: 8,
			round: true
		}
	};

	var TreeModel = function(parent) {
		var self = this;
		var attributeNamesArray = [];

		parent = parent || {attributes:{}};
		this.attributes = {};

		// Takes an optional parent - to allow for re-initialization.
		this.initialize = function(parentTree) {
			parent = parentTree || parent;
			for (var attributeName in ATTRIBUTES) {
				attributeNamesArray.push(attributeName);
				self.attributes[attributeName] = parent.attributes[attributeName] || ATTRIBUTES[attributeName].initialValue;
			}
			self.randomize();
		};

		this.randomize = function() {
			// Selecting a random attribute
			var attributeToRandomize = attributeNamesArray[Math.floor(Math.random()*attributeNamesArray.length)];
			var attribute = ATTRIBUTES[attributeToRandomize];
			var varyBy = attribute.varyBy;

			// Adding/subtracting a random amount from that attribute
			var newValue = self.attributes[attributeToRandomize] + Utilities.random(-1*varyBy, varyBy);

			// Ensuring that the value is within the bounds, and properly rounded.
			newValue = Utilities.minMax(newValue, attribute.min, attribute.max);
			if (attribute.round) {
				newValue = Math.round(newValue);
			} else {
				// Otherwise, just round to max 3 decimal places.
				newValue = Utilities.roundTo(newValue, 2)
			}

			self.attributes[attributeToRandomize] = newValue;
		};

		this.getAttributes = function() {
			return Utilities.copyObject(self.attributes);
		};

		this.setAttributes = function(attributes) {
			if (Array.isArray(attributes)) {
				for (var i = 0; i < attributes.length; i++) {
					self.attributes[attributeNamesArray[i]] = parseFloat(attributes[i]);
				}
			} else {
				for (var attributeName in attributes) {
					self.attributes[attributeName] = parseFloat(attributes[attributeName]);
				}
			}
		}

		this.initialize();
	};

	/* Adapted from http://thecodeplayer.com/walkthrough/create-binary-trees-using-javascript-and-html5-canvas?s=rl */
	var TreeView = function(canvas) {
		var ctx = canvas.getContext("2d");
		//Lets resize the canvas to occupy the full page
		var W = canvas.offsetWidth;
		var H = canvas.offsetHeight;
		canvas.width = W;
		canvas.height = H;

		//Some variables
		var length, divergence, reduction, lineWidth, branchings, start_points = [];

		this.draw = function(attributes) {
			//clearing the canvas
			ctx.clearRect (0, 0, W, H);

			//Lets draw the trunk of the tree
			//length of the trunk - 100-150
			length = attributes.length;
			//angle at which branches will diverge - 10-60
			divergence = attributes.divergence;
			//Every branch will be 0.75times of the previous one - 0.5-0.75
			reduction = attributes.reduction;
			//width of the branch/trunk
			lineWidth = attributes.lineWidth;
			//number of branchings
			branchings = attributes.branchings;
			branchings ? branchings < 0 : 0;

			//This is the end point of the trunk, from where branches will diverge
			var trunk = {x: W/2, y: length + 5, angle: 90};
			//It becomes the start point for branches
			start_points = []; //empty the start points on every init();
			start_points.push(trunk);

			//Y coordinates go positive downwards, hence they are inverted by deducting it
			//from the canvas height = H
			ctx.beginPath();
			ctx.moveTo(trunk.x, H - 5);
			ctx.lineTo(trunk.x, H - trunk.y);
			ctx.strokeStyle = "brown";
			ctx.lineWidth = lineWidth;
			ctx.stroke();

			branches();
		}

		//Lets draw the branches now
		function branches(callNum) {
			callNum = callNum || 0;

			//reducing lineWidth and length
			length = length * reduction;
			lineWidth = lineWidth * reduction;
			ctx.lineWidth = lineWidth;

			var new_start_points = [];
			ctx.beginPath();
			for(var i = 0; i < start_points.length; i++) {
				var sp = start_points[i];
				//2 branches will come out of every start point. Hence there will be
				//2 end points. There is a difference in the divergence.
				var ep1 = get_endpoint(sp.x, sp.y, sp.angle + divergence, length);
				var ep2 = get_endpoint(sp.x, sp.y, sp.angle - divergence, length);

				//drawing the branches now
				ctx.moveTo(sp.x, H - sp.y);
				ctx.lineTo(ep1.x, H - ep1.y);
				ctx.moveTo(sp.x, H - sp.y);
				ctx.lineTo(ep2.x, H - ep2.y);

				//Time to make this function recursive to draw more branches
				ep1.angle = sp.angle + divergence;
				ep2.angle = sp.angle - divergence;

				new_start_points.push(ep1);
				new_start_points.push(ep2);
			}
			//Lets add some more color
			if (length < 10) {
				ctx.strokeStyle = "green";
			} else {
				ctx.strokeStyle = "brown";
			}
			ctx.stroke();
			start_points = new_start_points;
			//recursive call
			if (callNum < branchings) {
				// Using timeout to see the tree growing.
				setTimeout(function(){
					branches(callNum+1);
				}, 50);
			}
		}

		function get_endpoint(x, y, a, length) {
			//This function will calculate the end points based on simple vectors
			//http://physics.about.com/od/mathematics/a/VectorMath.htm
			//You can read about basic vectors from this link
			var epx = x + length * Math.cos(a * Math.PI/180);
			var epy = y + length * Math.sin(a * Math.PI/180);
			return {x: epx, y: epy};
		}

		this.setRoot = function() {
			canvas.classList.add("root");
		}

		this.unsetRoot = function() {
			canvas.classList.remove("root");
		}

		this.onClick = function(callback) {
			canvas.addEventListener("click", function() {
				callback();
			});
		}
	}

	var TreeController = (function() {
		var root;
		var trees;
		var treeCanvases = document.querySelectorAll(".tree");
		var attributeHashes = [];
		var ignoreHash;

		function getAttributesHash(attributes) {
			var hash = "";
			for (var attributeName in attributes) {
				hash += attributes[attributeName] + "_";
			}
			return hash.substring(0, hash.length - 1);
		}

		function setupTree(index, parent) {
			if (!trees[index]) {
				tree = trees[index] =  {};
				tree.model = new TreeModel(parent);
				tree.view = new TreeView(treeCanvases[index]);
				tree.view.onClick(function() {
					spawnChildren(index);
				});
				tree.index = index;
			} else {
				tree = trees[index];
				tree.model.initialize(parent);
			}
			tree.view.draw(tree.model.getAttributes());
			attributeHashes[index] = getAttributesHash(tree.model.getAttributes());
			return tree;
		}

		function spawnChildren(index) {
			setRoot(index)
			for (var i = 0; i < trees.length; i++) {
				if (i === index) {
					continue;
				}
				setupTree(i, root.model);
			}

			// Set a flag, so the hash watched doesn't fire.
			ignoreHash = true;
			hasher.setHash(index + ":" + attributeHashes.join(","));
			setTimeout(function(){
				ignoreHash = false;
			}, 50);
		}

		function setRoot(index) {
			if (root && root.view) {
				root.view.unsetRoot();
			}
			//if (trees[index] && trees[index].view)
			root = trees[index];
			root.view.setRoot();
		}

		// function to read the hash value and use it as the tree attributes.
		function readHash(hash) {
			if (ignoreHash || hash === "") {
				return;
			}
			var split = hash.split(":");
			setRoot(split[0]);
			var attributeSets = split[1].split(",");
			trees.forEach(function(tree, index) {
				tree.model.setAttributes(attributeSets[index].split("_"))
				tree.view.draw(tree.model.getAttributes())
			});
		}

		function init() {
			setup();

			hasher.initialized.add(readHash); //add initialized listener (to grab initial value in case it is already set)
			hasher.changed.add(readHash);
			hasher.init(); //initialize hasher (start listening for history changes)
		}

		function setup() {
			trees = new Array(CONSTS.TREE_COUNT);
			root = setupTree(0);
			spawnChildren(0);
		}

		document.getElementById("reset").addEventListener("click", function() {
			setup();
		});

		return {
			init: init
		}
	})();

	TreeController.init();
})();<|endoftext|>


<commit_before>/**
 * Superclass for all games.
 * Holds shared logic for mode and round handling. Also some graphic setups.
 * Also see subclass NumberGame.
 *
 * SETUP THESE IN THE SUBCLASS:
 * They are called with two parameters (ifFirstTime, triesSoFar).
 * modeIntro:      Introduce the game, call nextRound to start next mode.
 * modePlayerDo:   Player only
 * modePlayerShow: Player is showing the TA
 * modeAgentTry:   TA is guessing and the player is helping out
 * modeAgentDo:    TA only
 * modeOutro:      The game is finished, celebrate!
 *
 * VARIABLES THE SUBCLASS CAN USE:
 * Add game objects to:     this.gameGroup
 * Add buttons and HUD to:  this.hudGroup
 * Use agent with:          this.agent (default visibility = false)
 *
 * FUNCTIONS THE SUBCLASS CAN USE:
 * Start game:              this.startGame()
 * Disable/Enable input:    this.disable(true/false) - default = true = disabled (publishes disabled event)
 * Run next round:          this.nextRound() - will change mode automatically when needed
 * Add water to the can:    this.addWater(fromX, fromY) - Adds a water drop to the can
 */
function Subgame () {}

/*
 * Phaser state function.
 * Publishes subgameStarted event.
 */
Subgame.prototype.init = function (options) {
	/* "Private" variables */
	var _this = this; // Event subscriptions does not have access to this
	this._token = options.token || Date.now();
	this._modes = options.mode || [
		GLOBAL.MODE.intro,
		GLOBAL.MODE.playerDo,
		GLOBAL.MODE.playerShow,
		GLOBAL.MODE.agentTry,
		// GLOBAL.MODE.agentDo,
		GLOBAL.MODE.outro
	];
	this._mode = null;
	this._pendingMode = null;
	this._first = true;
	/* Keep track of how many rounds that have been played */
	this._counter = new Counter(1, true);
	/* When enough rounds have been played, trigger a mode change */
	this._counter.onMax = function () {
		_this._nextMode();
	};
	this._currentTries = 0;
	this._totalTries = 0;

	/* Public variables */
	this.currentMode = null; // The current mode running

	/* Setup game objects */
	this.gameGroup = this.add.group();
	this.agent = player.createAgent();
	this.agent.visible = false;
	this.gameGroup.add(this.agent);

	this.hudGroup = this.add.group();

	var disabler = new Cover('#ffffff', 0);
	this.world.add(disabler);
	this.disable = function (value) {
		if (disabler.visible !== value) {
			EventSystem.publish(GLOBAL.EVENT.disabled, [value]);
		}
		disabler.visible = value;
	};

	/* Setup menu objects */
	this._menuGroup = this.add.group();
	this._menuGroup.visible = false;
	this._waterCan = new WaterCan();
	this._menuGroup.add(this._waterCan);
	this._menuGroup.add(new Menu());
};

/* Phaser state function */
Subgame.prototype.shutdown = onShutDown;

/*MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM*/
/*                            Private functions                              */
/*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*/

/** Change to the next mode in the queue. */
Subgame.prototype._nextMode = function () {
	var newMode = this._modes.shift();
	this._decideMode(newMode);
	this._pendingMode = newMode;
	this._first = true;
};

/**
 * Translate from integer to mode function
 * @param {number}
 */
Subgame.prototype._decideMode = function (mode) {
	if (mode === GLOBAL.MODE.intro) {
		this._mode = this.modeIntro;
	} else if (mode === GLOBAL.MODE.playerDo) {
		this._mode = this.modePlayerDo;
	} else if (mode === GLOBAL.MODE.playerShow) {
		this._mode = this.modePlayerShow;
	} else if (mode === GLOBAL.MODE.agentTry) {
		this._mode = this.modeAgentTry;
	} else if (mode === GLOBAL.MODE.agentDo) {
		this._mode = this.modeAgentDo;
	} else if (mode === GLOBAL.MODE.outro) {
		this._mode = this.modeOutro;
	} else {
		this._mode = this.endGame;
	}
};

/** Skip the current mode. */
Subgame.prototype._skipMode = function () {
	this._nextMode();
	this.nextRound();
};

/*MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM*/
/*                            Public functions                               */
/*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*/

/**
 * Calls the current mode function. It will be called with two parameters:
 * 1) If it is the first time on this mode.
 * 2) How many tries that have been made on the current number.
 * Publishes modeChange event (first time a mode runs).
 */
Subgame.prototype.nextRound = function () {
	// Special case: intro and outro only have one round
	if ((this.currentMode === GLOBAL.MODE.intro ||
		this.currentMode === GLOBAL.MODE.outro) &&
		this.currentMode === this._pendingMode) {
		this._nextMode();
	}

	// Publish event when it it is the first time it runs
	if (this._first) {
		EventSystem.publish(GLOBAL.EVENT.modeChange, [this._pendingMode]);
	}

	// Run mode and update properties
	this.currentMode = this._pendingMode;
	this._mode(this._first, this._currentTries);
	this._first = false;
};

/**
 * Adds water to the water can.
 * Water will only be added in modes playerShow, agentTry and agentDo.
 * @param {number} The x position where the drop will begin.
 * @param {number} The y position where the drop will begin.
 * @param {boolean} Override mode restrictions and force drop to be added.
 * @return {Object} The drop animation from x, y to water can.
 */
Subgame.prototype.addWater = function (x, y) {
	var drop = this.add.sprite(x, y, 'objects', 'drop', this._menuGroup);
	drop.anchor.set(0.5);
	drop.scale.set(0.7, 0);

	// Show drop
	return new TimelineMax().to(drop.scale, 1.5, { y: 0.7, ease:Elastic.easeOut })
		// Move drop
		.to(drop, 1.5, { x: this._waterCan.x + 50, y: this._waterCan.y + 30, ease:Power2.easeOut })
		// Hide drop and add water
		.to(drop, 0.5, { height: 0,
			onStart: function () { player.water++; },
			onComplete: function () { drop.destroy(); }
		});
};

/** Start the game! */
Subgame.prototype.startGame = function () {
	/* Send event that subgame is started. */
	EventSystem.publish(GLOBAL.EVENT.subgameStarted, [game.state.current, this._token]);

	var _this = this;
	this.sound.whenSoundsDecoded(function () {
		_this._menuGroup.visible = true;
		_this._nextMode();
		_this.nextRound();
	});
};

/** End the game. */
Subgame.prototype.endGame = function () {
	this.state.start(GLOBAL.STATE.garden);
};

/* The following functions should be overshadowed in the game object. */
Subgame.prototype.modeIntro      = Subgame.prototype._skipMode;
Subgame.prototype.modePlayerDo   = Subgame.prototype._skipMode;
Subgame.prototype.modePlayerShow = Subgame.prototype._skipMode;
Subgame.prototype.modeAgentTry   = Subgame.prototype._skipMode;
Subgame.prototype.modeAgentDo    = Subgame.prototype._skipMode;
Subgame.prototype.modeOutro      = Subgame.prototype._skipMode;<commit_msg>Accidental commit cleanup<commit_after>/**
 * Superclass for all games.
 * Holds shared logic for mode and round handling. Also some graphic setups.
 * Also see subclass NumberGame.
 *
 * SETUP THESE IN THE SUBCLASS:
 * They are called with two parameters (ifFirstTime, triesSoFar).
 * modeIntro:      Introduce the game, call nextRound to start next mode.
 * modePlayerDo:   Player only
 * modePlayerShow: Player is showing the TA
 * modeAgentTry:   TA is guessing and the player is helping out
 * modeAgentDo:    TA only
 * modeOutro:      The game is finished, celebrate!
 *
 * VARIABLES THE SUBCLASS CAN USE:
 * Add game objects to:     this.gameGroup
 * Add buttons and HUD to:  this.hudGroup
 * Use agent with:          this.agent (default visibility = false)
 *
 * FUNCTIONS THE SUBCLASS CAN USE:
 * Start game:              this.startGame()
 * Disable/Enable input:    this.disable(true/false) - default = true = disabled (publishes disabled event)
 * Run next round:          this.nextRound() - will change mode automatically when needed
 * Add water to the can:    this.addWater(fromX, fromY) - Adds a water drop to the can
 */
function Subgame () {}

/*
 * Phaser state function.
 * Publishes subgameStarted event.
 */
Subgame.prototype.init = function (options) {
	/* "Private" variables */
	var _this = this; // Event subscriptions does not have access to this
	this._token = options.token || Date.now();
	this._modes = options.mode || [
		GLOBAL.MODE.intro,
		GLOBAL.MODE.playerDo,
		GLOBAL.MODE.playerShow,
		GLOBAL.MODE.agentTry,
		// GLOBAL.MODE.agentDo,
		GLOBAL.MODE.outro
	];
	this._mode = null;
	this._pendingMode = null;
	this._first = true;
	/* Keep track of how many rounds that have been played */
	this._counter = new Counter(options.roundsPerMode || 3, true);
	/* When enough rounds have been played, trigger a mode change */
	this._counter.onMax = function () {
		_this._nextMode();
	};
	this._currentTries = 0;
	this._totalTries = 0;

	/* Public variables */
	this.currentMode = null; // The current mode running

	/* Setup game objects */
	this.gameGroup = this.add.group();
	this.agent = player.createAgent();
	this.agent.visible = false;
	this.gameGroup.add(this.agent);

	this.hudGroup = this.add.group();

	var disabler = new Cover('#ffffff', 0);
	this.world.add(disabler);
	this.disable = function (value) {
		if (disabler.visible !== value) {
			EventSystem.publish(GLOBAL.EVENT.disabled, [value]);
		}
		disabler.visible = value;
	};

	/* Setup menu objects */
	this._menuGroup = this.add.group();
	this._menuGroup.visible = false;
	this._waterCan = new WaterCan();
	this._menuGroup.add(this._waterCan);
	this._menuGroup.add(new Menu());
};

/* Phaser state function */
Subgame.prototype.shutdown = onShutDown;

/*MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM*/
/*                            Private functions                              */
/*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*/

/** Change to the next mode in the queue. */
Subgame.prototype._nextMode = function () {
	var newMode = this._modes.shift();
	this._decideMode(newMode);
	this._pendingMode = newMode;
	this._first = true;
};

/**
 * Translate from integer to mode function
 * @param {number}
 */
Subgame.prototype._decideMode = function (mode) {
	if (mode === GLOBAL.MODE.intro) {
		this._mode = this.modeIntro;
	} else if (mode === GLOBAL.MODE.playerDo) {
		this._mode = this.modePlayerDo;
	} else if (mode === GLOBAL.MODE.playerShow) {
		this._mode = this.modePlayerShow;
	} else if (mode === GLOBAL.MODE.agentTry) {
		this._mode = this.modeAgentTry;
	} else if (mode === GLOBAL.MODE.agentDo) {
		this._mode = this.modeAgentDo;
	} else if (mode === GLOBAL.MODE.outro) {
		this._mode = this.modeOutro;
	} else {
		this._mode = this.endGame;
	}
};

/** Skip the current mode. */
Subgame.prototype._skipMode = function () {
	this._nextMode();
	this.nextRound();
};

/*MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM*/
/*                            Public functions                               */
/*WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW*/

/**
 * Calls the current mode function. It will be called with two parameters:
 * 1) If it is the first time on this mode.
 * 2) How many tries that have been made on the current number.
 * Publishes modeChange event (first time a mode runs).
 */
Subgame.prototype.nextRound = function () {
	// Special case: intro and outro only have one round
	if ((this.currentMode === GLOBAL.MODE.intro ||
		this.currentMode === GLOBAL.MODE.outro) &&
		this.currentMode === this._pendingMode) {
		this._nextMode();
	}

	// Publish event when it it is the first time it runs
	if (this._first) {
		EventSystem.publish(GLOBAL.EVENT.modeChange, [this._pendingMode]);
	}

	// Run mode and update properties
	this.currentMode = this._pendingMode;
	this._mode(this._first, this._currentTries);
	this._first = false;
};

/**
 * Adds water to the water can.
 * Water will only be added in modes playerShow, agentTry and agentDo.
 * @param {number} The x position where the drop will begin.
 * @param {number} The y position where the drop will begin.
 * @param {boolean} Override mode restrictions and force drop to be added.
 * @return {Object} The drop animation from x, y to water can.
 */
Subgame.prototype.addWater = function (x, y) {
	var drop = this.add.sprite(x, y, 'objects', 'drop', this._menuGroup);
	drop.anchor.set(0.5);
	drop.scale.set(0.7, 0);

	// Show drop
	return new TimelineMax().to(drop.scale, 1.5, { y: 0.7, ease:Elastic.easeOut })
		// Move drop
		.to(drop, 1.5, { x: this._waterCan.x + 50, y: this._waterCan.y + 30, ease:Power2.easeOut })
		// Hide drop and add water
		.to(drop, 0.5, { height: 0,
			onStart: function () { player.water++; },
			onComplete: function () { drop.destroy(); }
		});
};

/** Start the game! */
Subgame.prototype.startGame = function () {
	/* Send event that subgame is started. */
	EventSystem.publish(GLOBAL.EVENT.subgameStarted, [game.state.current, this._token]);

	var _this = this;
	this.sound.whenSoundsDecoded(function () {
		_this._menuGroup.visible = true;
		_this._nextMode();
		_this.nextRound();
	});
};

/** End the game. */
Subgame.prototype.endGame = function () {
	this.state.start(GLOBAL.STATE.garden);
};

/* The following functions should be overshadowed in the game object. */
Subgame.prototype.modeIntro      = Subgame.prototype._skipMode;
Subgame.prototype.modePlayerDo   = Subgame.prototype._skipMode;
Subgame.prototype.modePlayerShow = Subgame.prototype._skipMode;
Subgame.prototype.modeAgentTry   = Subgame.prototype._skipMode;
Subgame.prototype.modeAgentDo    = Subgame.prototype._skipMode;
Subgame.prototype.modeOutro      = Subgame.prototype._skipMode;<|endoftext|>


<commit_before>$(function() {

  /// GLOBAL VARS BEGIN ///
  var simImages = ["barbie.png", "batman.png", "cowboy.png", "news.png", "pizza.png", "tekpix.png"];
  var positionText = [[395, 55], [130, 338], [125, 62], [135, 365], [370, 355], [368, 70], ];
  var imagePath = "./assets/optimized/";
  var imageInUse;
  /// GLOBAL VARS END ///

  $(document)
    .on("ready", function() {
      console.log("Leonardo Simulator 2000");
      refreshImage();

      $("#tvButton").click(function() {
        var tvButton = $(this);
        refreshImage();
        rotateButton(tvButton);
      });

    });

  function refreshImage() {
    var currentSim = $(".currentSim");
    var newImageId = Math.floor(Math.random() * 5);
    var newImage = simImages[newImageId];
    var newTextPosTop = positionText[newImageId][0];
    var newTextPosLeft = positionText[newImageId][1];
    var simText = $("#simText");

    if(imageInUse != newImage) {
      currentSim.css("background", "url(" + imagePath + newImage + ") no-repeat");
      imageInUse = newImage;
      simText.css({"top" : newTextPosTop, "left" : newTextPosLeft});
    } else {
      refreshImage();
    }
  }

  function rotateButton(button) {
    var tvButton = button;
    var currentDeg = tvButton.attr("rotation");
    var newDeg = parseInt(currentDeg) + 30;
    tvButton.attr("rotation", newDeg);
    tvButton.css("transform", "rotate(" + newDeg + "deg)");
  }

});<commit_msg>Adding text<commit_after>$(function() {

  /// GLOBAL VARS BEGIN ///
  var imagePath = "./assets/optimized/";
  var simImages = ["barbie.png", "batman.png", "cowboy.png", "news.png", "pizza.png", "tekpix.png"];
  var positionText = [[395, 55], [130, 338], [125, 62], [135, 365], [370, 355], [368, 70]];
  var jokes = ["Faz cosplay de android que eu vou fazer cosplay de ios",
               "Vou jogar um dado assistindo porn",
               "Se eu tenho um dinossaurinho de plástico, na verdade é um dinossauro morto",
               "Tá matando a mulher... matando de amor"
              ];
  var imageInUse;
  var jokeInUse;
  /// GLOBAL VARS END ///

  $(document)
    .on("ready", function() {
      console.log("Leonardo Simulator 2000");
      refreshImage();

      $("#tvButton").click(function() {
        var tvButton = $(this);
        refreshImage();
        rotateButton(tvButton);
      });

    });

  function refreshImage() {
    var currentSim = $(".currentSim");
    var newImageId = Math.floor(Math.random() * 5);
    var newImage = simImages[newImageId];
    var newTextPosTop = positionText[newImageId][0];
    var newTextPosLeft = positionText[newImageId][1];
    var simText = $("#simText");

    if(imageInUse != newImage) {
      currentSim.css("background", "url(" + imagePath + newImage + ") no-repeat");
      imageInUse = newImage;

      simText.css({"top" : newTextPosTop, "left" : newTextPosLeft});

      var newJoke = refreshJoke();

      simText.text(newJoke);
    } else {
      refreshImage();
    }
  }

  function rotateButton(button) {
    var tvButton = button;
    var currentDeg = tvButton.attr("rotation");
    var newDeg = parseInt(currentDeg) + 30;
    tvButton.attr("rotation", newDeg);
    tvButton.css("transform", "rotate(" + newDeg + "deg)");
  }

  function refreshJoke() {
    var maxJokeRange = jokes.length;
    var newJokeId = Math.floor(Math.random() * maxJokeRange);
    var newJoke = jokes[newJokeId];

    if(jokeInUse != newJoke) {
      jokeInUse = newJoke;
      return newJoke;
    } else {
      refreshJoke();
    }
  }

});<|endoftext|>


<commit_before>__$__.StorePositions = {
    oldNetwork: {
        nodes: {},
        edges: {},
        _nodesData: {},
        _edgesData: {}
    },
    positionsOfExpectedStructure: undefined,

    // if nodePositions have the position of node.id, set the position at graph.node.
    setPositions: function(graph, positionUpdate = false) {
        for (let i = 0; i < graph.nodes.length; i++) {
            let node = graph.nodes[i];
            let nodeData = __$__.StorePositions.oldNetwork.nodes[node.id];
            let posOfExpStr = __$__.StorePositions.positionsOfExpectedStructure;

            if (!positionUpdate && posOfExpStr && posOfExpStr[node.id]){
                node.x = posOfExpStr[node.id].x;
                node.y = posOfExpStr[node.id].y;
            } else if (nodeData && nodeData.x !== undefined) {
                node.x = nodeData.x;
                node.y = nodeData.y;
                if (node.id.slice(0, 11) !== '__Variable-')
                    node.fixed = true;
            } else {
                node.x = undefined;
                node.y = undefined;
            }
        }
        if (!positionUpdate)
            delete __$__.StorePositions.positionsOfExpectedStructure;
    },

    // register the positions of the nodes to be able to use old network data
    registerPositions: function(positionUpdate = false) {
        let pos = __$__.ObjectGraphNetwork.network.getPositions();

        Object.keys(pos).forEach(function(id) {
            let temp = __$__.StorePositions.oldNetwork.nodes;

            if (temp[id]) {
                temp[id].x = pos[id].x;
                temp[id].y = pos[id].y;
            } else if (!isNaN(pos[id].x) && !isNaN(pos[id].y)) {
                temp[id] = {x: pos[id].x, y: pos[id].y};
            }
        });
        if (positionUpdate) {
            __$__.StorePositions.oldNetwork._nodesData = __$__.ObjectGraphNetwork.nodes._data;
            __$__.StorePositions.oldNetwork._edgesData = __$__.ObjectGraphNetwork.edges._data;
        }
    },

    registerPositionsOfExpectedStructure() {
        __$__.StorePositions.positionsOfExpectedStructure = __$__.Testize.network.network.getPositions();
    },

    updateIDForExpectedStructure(oldID, newID) {
        let posOfExpStr = __$__.StorePositions.positionsOfExpectedStructure;
        if (posOfExpStr && !posOfExpStr[newID]) {
            posOfExpStr[newID] = posOfExpStr[oldID];
            delete posOfExpStr[oldID];
        }
    }
};
<commit_msg>fix bug<commit_after>__$__.StorePositions = {
    oldNetwork: {
        nodes: {},
        edges: {},
        _nodesData: {},
        _edgesData: {}
    },
    positionsOfExpectedStructure: undefined,

    // if nodePositions have the position of node.id, set the position at graph.node.
    setPositions: function(graph, positionUpdate = false) {
        for (let i = 0; i < graph.nodes.length; i++) {
            let node = graph.nodes[i];
            let nodeData = __$__.StorePositions.oldNetwork.nodes[node.id];
            let posOfExpStr = __$__.StorePositions.positionsOfExpectedStructure;

            if (!positionUpdate && posOfExpStr && posOfExpStr[node.id]){
                node.x = posOfExpStr[node.id].x;
                node.y = posOfExpStr[node.id].y;
                if (node.id.slice(0, 11) !== '__Variable-')
                    node.fixed = true;
            } else if (nodeData && nodeData.x !== undefined) {
                node.x = nodeData.x;
                node.y = nodeData.y;
                if (node.id.slice(0, 11) !== '__Variable-')
                    node.fixed = true;
            } else {
                node.x = undefined;
                node.y = undefined;
            }
        }
        // if (!positionUpdate)
        //     delete __$__.StorePositions.positionsOfExpectedStructure;
    },

    // register the positions of the nodes to be able to use old network data
    registerPositions: function(positionUpdate = false) {
        let pos = __$__.ObjectGraphNetwork.network.getPositions();

        Object.keys(pos).forEach(function(id) {
            let temp = __$__.StorePositions.oldNetwork.nodes;

            if (temp[id]) {
                temp[id].x = pos[id].x;
                temp[id].y = pos[id].y;
            } else if (!isNaN(pos[id].x) && !isNaN(pos[id].y)) {
                temp[id] = {x: pos[id].x, y: pos[id].y};
            }
        });
        if (positionUpdate) {
            __$__.StorePositions.oldNetwork._nodesData = __$__.ObjectGraphNetwork.nodes._data;
            __$__.StorePositions.oldNetwork._edgesData = __$__.ObjectGraphNetwork.edges._data;
        }
    },

    registerPositionsOfExpectedStructure() {
        __$__.StorePositions.positionsOfExpectedStructure = __$__.Testize.network.network.getPositions();
    },

    updateIDForExpectedStructure(oldID, newID) {
        let posOfExpStr = __$__.StorePositions.positionsOfExpectedStructure;
        if (posOfExpStr && !posOfExpStr[newID]) {
            posOfExpStr[newID] = posOfExpStr[oldID];
            delete posOfExpStr[oldID];
        }
    }
};
<|endoftext|>


<commit_before>var map;
function initialize() {
  var mapOptions = {
    zoom: 10,
    center: new google.maps.LatLng(34.399, -77.755),
    scaleControl: true,
    zoomControl: true,
    zoomControlOptions: {
        style: google.maps.ZoomControlStyle.SMALL,
        position: google.maps.ControlPosition.RIGHT_TOP
    },
    panControl: false,
    streetViewControl: true,
    streetViewControlOptions: {
    	position: google.maps.ControlPosition.RIGHT_TOP
    }
  };

  google.maps.visualRefresh=true;
  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

  // Load a GeoJSON from the same server as our demo.
  var jsonData = "data/ncVacationData.json";
  map.data.loadGeoJson(jsonData);

  // Add some style
  map.data.setStyle(function(feature) {
  	return({
  		fillColor: feature.getProperty("color")
  	});
  });

  var infoWindow = new google.maps.InfoWindow({
    pixelOffset: new google.maps.Size(0,-25),
  });

  // Set mouseover event for each feature.
  map.data.addListener('click', function(event) {
    fam = event.feature.getProperty("Family");
    name = event.feature.getProperty("Name");
    address = event.feature.getProperty("Address");
    city = event.feature.getProperty("City");
    phone = event.feature.getProperty("Phone");
    web = event.feature.getProperty("Website");

    content = "" + name + "" + "
" + address + "
" + city + "
" + phone + "
" + "";

    infoWindow.setContent(content);

    var anchor = new google.maps.MVCObject();
	anchor.set("position",event.latLng);
	infoWindow.open(map,anchor);
  });

}

google.maps.event.addDomListener(window, 'load', initialize);
<commit_msg>Update main.js<commit_after>var map;
function initialize() {
  var mapOptions = {
    zoom: 10,
    center: new google.maps.LatLng(34.399, -77.755),
    scaleControl: true,
    zoomControl: true,
    zoomControlOptions: {
        style: google.maps.ZoomControlStyle.SMALL,
        position: google.maps.ControlPosition.RIGHT_TOP
    },
    panControl: false,
    streetViewControl: true,
    streetViewControlOptions: {
    	position: google.maps.ControlPosition.RIGHT_TOP
    }
  };

  google.maps.visualRefresh=true;
  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);

  // Load a GeoJSON from the same server as our demo.
  var jsonData = "data/ncVacationData.json";
  map.data.loadGeoJson(jsonData);

  // Add some style
  map.data.setStyle(function(feature) {
  	return({
  		fillColor: feature.getProperty("color")
  	});
  });

  var infoWindow = new google.maps.InfoWindow({
    pixelOffset: new google.maps.Size(0,-25),
  });

  // Set mouseover event for each feature.
  map.data.addListener('click', function(event) {
    fam = event.feature.getProperty("Family");
    name = event.feature.getProperty("Name");
    address = event.feature.getProperty("Address");
    city = event.feature.getProperty("City");
    phone = event.feature.getProperty("Phone");
    web = event.feature.getProperty("Website");

    content = "" + name + "" + "
" + address + "
" + city + "
" + phone + "
" + "Website";

    infoWindow.setContent(content);

    var anchor = new google.maps.MVCObject();
	anchor.set("position",event.latLng);
	infoWindow.open(map,anchor);
  });

}

google.maps.event.addDomListener(window, 'load', initialize);
<|endoftext|>


<commit_before>define([
    'underscore',
    'backbone',
    'lib/rivets-cfg',
    'jquery',

    '../template/beverages.html',

    '../collection/beverages',
    '../i18n/i18n',

    'bootstrap/dist/js/umd/tooltip',
    'bootstrap/dist/js/umd/popover',

    '../../scss/beverages.scss'
], function (_, Backbone, rivets, $, template, Beverages, i18n) {
    'use strict';

    /* === Rivets configuration === */
    rivets.formatters.theineLevel100 = function (value) {
        switch (value) {
            case 'none':
                return 0;
            case 'low':
                return 25;
            case 'medium':
                return 50;
            case 'high':
                return 75;
            case 'coffee':
            case 'unknown':
            default:
                return 100;
        }
    };

    rivets.formatters.minMax = function (value, separator) {
        if (value) {
            if (value.min) {
                return value.max && value.min !== value.max
                    ? value.min + (separator ? separator : '-') + value.max
                    : value.min;
            } else if (value.max) {
                return value.max;
            }
        }
        return '';
    };

    rivets.formatters.showMinMax = function (value) {
        return value && (value.min || value.max);
    };

    /* === Constants & functions === */
    var autoTimeRange = {
        morning: [5, 11],
        daytime: [10, 20],
        evening: [18, 2],
        unknown: [0, 24]
    };
    var isInTimeRange = function(range) {
        var min = autoTimeRange[range][0],
            max = autoTimeRange[range][1],
            time = new Date().getHours();
        return min <= max ? min <= time && time < max : min <= time || time < max;
    };

    /* === Backbone view === */
    return Backbone.View.extend({
        /** {Beverages} The full collection of all available beverages. */
        beverages: null,

        /** {Beverages} */
        filtered: null,

        /** Filters */
        filters: null,

        context: null,

        rview: null,

        events: {
            'click .filters .bev-icon': '_toggleBeverageFilter',
            'click .filters .moment-icon': '_toggleMomentFilter',
            'click .beverage .bev-icon': '_toggleDetail'
        },

        initialize: function (options) {
            this.context = {
                i18n: null,
                ready: false,
                error: false
            };

            var gSheetId;
            if (options) {
                gSheetId = options.gSheetId;
                this.context.i18n = i18n(options.lang);
            } else {
                this.context.i18n = i18n();
            }

            this._initFilters(options.filters);
            if (gSheetId) {
                this._initAndFetchBeverages(gSheetId);
            } else {
                this.context.error = this.context.i18n.error.configuration;
            }
        },

        render: function () {
            // remove any previous binding
            if (this.rview) {
                this.rview.unbind();
            }

            // bind template to context
            this.context.beverages = this.beverages;
            this.context.filtered = this.filtered;
            this.context.filters = this.filters;
            this.rview = rivets.bind(this.$el.html(template), this.context);

            // init tooltips
            this._tooltip();

            // return
            return this;
        },

        _initFilters: function (options) {
            var availableBases = _.keys(this.context.i18n.basis);

            var settings = _.defaults(options, {
                basis: availableBases,
                moments: _.keys(this.context.i18n.moment)
            });

            if (_.contains(settings.basis, 'teas')) {
                settings.basis = settings.basis.concat(_.filter(availableBases, function (basis) {
                    return basis.startsWith('tea-');
                }));
            }

            this.filters = {
                bases: null,
                moments: null
            };

            this.filters.bases = [];
            for (var i = 0; i < availableBases.length; i++) {
                var basis = availableBases[i];
                this.filters.bases.push({
                    key: basis,
                    active: _.indexOf(settings.basis, basis) > -1
                });
            }

            this.filters.moments = [];
            for (var i = 0; i < settings.moments.length; i++) {
                this.filters.moments.push({
                    key: settings.moments[i],
                    active: options.autoTime ? isInTimeRange(settings.moments[i]) : true
                });
            }
        },

        _initAndFetchBeverages: function (gSheetId) {
            this.beverages = new Beverages(null, {
                gSheetId: gSheetId
            });

            this.filtered = new Beverages();

            this.beverages.on('request', function () {
                this.context.ready = false;
            }, this).on('sync', function () {
                this._filterBeverages();
                this.context.ready = true;
                this._tooltip();
            }, this).fetch().error(
                _.bind(function () {
                    this.context.error = this.context.i18n.error.loading;
                }, this)
            );
        },

        _filterBeverages: function () {
            this.filtered.reset(this.beverages.filter(function (beverage) {
                return this._isBasisActive(beverage) && this._isMomentActive(beverage);
            }.bind(this)));
        },

        _isBasisActive: function (beverage) {
            return _.find(this.filters.bases, function (basis) {
                    return beverage.get('basis') === basis.key;
                }
            ).active;
        },

        _isMomentActive: function (beverage) {
            var times = beverage.get('time');
            for (var key in times) { // FIXME see _.keys/keysOwn?
                var time = times[key];
                if (!rivets.formatters.defined(time) && _.findWhere(this.filters.moments, {key: 'unknown'}).active
                    || time && _.findWhere(this.filters.moments, {key: key}).active) {
                    return true;
                }
            }
            return false;
        },

        /**
         * Initializes the tooltips and popovers of the rendered page. This is set only on existing elements: the method
         * should be called again any time a new tooltip trigger element is added to the page.
         */
        _tooltip: function () {
            this.$('[data-toggle="tooltip"]').tooltip();
            this.$('[data-toggle="popover"]').popover();
        },

        _toggleBeverageFilter: function (event) {
            this._toggleFilter(event, this.filters.bases);
        },

        _toggleMomentFilter: function (event) {
            this._toggleFilter(event, this.filters.moments);
        },

        _toggleFilter: function (event, filterCollection) {
            var key = $(event.currentTarget).data('key');
            var filter = _.find(filterCollection, function (basis) {
                return key === basis.key;
            });
            filter.active = !filter.active;
            this._filterBeverages();
        },

        _toggleDetail: function (event) {
            $(event.currentTarget).closest('.beverage').toggleClass('detailed');
        },

        remove: function () {
            if (this.rview) {
                this.rview.unbind();
            }
            return _.bind(Backbone.View.prototype.remove, this)();
        }
    });
});
<commit_msg>[#59] Allow presetting of moments filters<commit_after>define([
    'underscore',
    'backbone',
    'lib/rivets-cfg',
    'jquery',

    '../template/beverages.html',

    '../collection/beverages',
    '../i18n/i18n',

    'bootstrap/dist/js/umd/tooltip',
    'bootstrap/dist/js/umd/popover',

    '../../scss/beverages.scss'
], function (_, Backbone, rivets, $, template, Beverages, i18n) {
    'use strict';

    /* === Rivets configuration === */
    rivets.formatters.theineLevel100 = function (value) {
        switch (value) {
            case 'none':
                return 0;
            case 'low':
                return 25;
            case 'medium':
                return 50;
            case 'high':
                return 75;
            case 'coffee':
            case 'unknown':
            default:
                return 100;
        }
    };

    rivets.formatters.minMax = function (value, separator) {
        if (value) {
            if (value.min) {
                return value.max && value.min !== value.max
                    ? value.min + (separator ? separator : '-') + value.max
                    : value.min;
            } else if (value.max) {
                return value.max;
            }
        }
        return '';
    };

    rivets.formatters.showMinMax = function (value) {
        return value && (value.min || value.max);
    };

    /* === Constants & functions === */
    var autoTimeRange = {
        morning: [5, 11],
        daytime: [10, 20],
        evening: [18, 2],
        unknown: [0, 24]
    };
    var isInTimeRange = function(range) {
        var min = autoTimeRange[range][0],
            max = autoTimeRange[range][1],
            time = new Date().getHours();
        return min <= max ? min <= time && time < max : min <= time || time < max;
    };

    /* === Backbone view === */
    return Backbone.View.extend({
        /** {Beverages} The full collection of all available beverages. */
        beverages: null,

        /** {Beverages} */
        filtered: null,

        /** Filters */
        filters: null,

        context: null,

        rview: null,

        events: {
            'click .filters .bev-icon': '_toggleBeverageFilter',
            'click .filters .moment-icon': '_toggleMomentFilter',
            'click .beverage .bev-icon': '_toggleDetail'
        },

        initialize: function (options) {
            this.context = {
                i18n: null,
                ready: false,
                error: false
            };

            var gSheetId;
            if (options) {
                gSheetId = options.gSheetId;
                this.context.i18n = i18n(options.lang);
            } else {
                this.context.i18n = i18n();
            }

            this._initFilters(options.filters);
            if (gSheetId) {
                this._initAndFetchBeverages(gSheetId);
            } else {
                this.context.error = this.context.i18n.error.configuration;
            }
        },

        render: function () {
            // remove any previous binding
            if (this.rview) {
                this.rview.unbind();
            }

            // bind template to context
            this.context.beverages = this.beverages;
            this.context.filtered = this.filtered;
            this.context.filters = this.filters;
            this.rview = rivets.bind(this.$el.html(template), this.context);

            // init tooltips
            this._tooltip();

            // return
            return this;
        },

        _initFilters: function (options) {
            var availableBases = _.keys(this.context.i18n.basis),
                availableMoments = _.keys(this.context.i18n.moment);

            var settings = _.defaults(options, {
                basis: availableBases
            });
            if (!settings.autoTime && !settings.moments) {
                settings.moments = availableMoments;
            }

            if (_.contains(settings.basis, 'teas')) {
                settings.basis = settings.basis.concat(_.filter(availableBases, function (basis) {
                    return basis.startsWith('tea-');
                }));
            }

            this.filters = {
                bases: null,
                moments: null
            };

            this.filters.bases = [];
            for (var i = 0; i < availableBases.length; i++) {
                var basis = availableBases[i];
                this.filters.bases.push({
                    key: basis,
                    active: _.indexOf(settings.basis, basis) > -1
                });
            }

            this.filters.moments = [];
            for (var i = 0; i < availableMoments.length; i++) {
                var moment = availableMoments[i];
                this.filters.moments.push({
                    key: moment,
                    active: _.indexOf(settings.moments, moment) > -1 || options.autoTime && isInTimeRange(moment)
                });
            }
        },

        _initAndFetchBeverages: function (gSheetId) {
            this.beverages = new Beverages(null, {
                gSheetId: gSheetId
            });

            this.filtered = new Beverages();

            this.beverages.on('request', function () {
                this.context.ready = false;
            }, this).on('sync', function () {
                this._filterBeverages();
                this.context.ready = true;
                this._tooltip();
            }, this).fetch().error(
                _.bind(function () {
                    this.context.error = this.context.i18n.error.loading;
                }, this)
            );
        },

        _filterBeverages: function () {
            this.filtered.reset(this.beverages.filter(function (beverage) {
                return this._isBasisActive(beverage) && this._isMomentActive(beverage);
            }.bind(this)));
        },

        _isBasisActive: function (beverage) {
            return _.find(this.filters.bases, function (basis) {
                    return beverage.get('basis') === basis.key;
                }
            ).active;
        },

        _isMomentActive: function (beverage) {
            var times = beverage.get('time'),
                keys = _.keys(times);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i],
                    time = times[key];
                if (!rivets.formatters.defined(time) && _.findWhere(this.filters.moments, {key: 'unknown'}).active
                    || time && _.findWhere(this.filters.moments, {key: key}).active) {
                    return true;
                }
            }
            return false;
        },

        /**
         * Initializes the tooltips and popovers of the rendered page. This is set only on existing elements: the method
         * should be called again any time a new tooltip trigger element is added to the page.
         */
        _tooltip: function () {
            this.$('[data-toggle="tooltip"]').tooltip();
            this.$('[data-toggle="popover"]').popover();
        },

        _toggleBeverageFilter: function (event) {
            this._toggleFilter(event, this.filters.bases);
        },

        _toggleMomentFilter: function (event) {
            this._toggleFilter(event, this.filters.moments);
        },

        _toggleFilter: function (event, filterCollection) {
            var key = $(event.currentTarget).data('key');
            var filter = _.find(filterCollection, function (basis) {
                return key === basis.key;
            });
            filter.active = !filter.active;
            this._filterBeverages();
        },

        _toggleDetail: function (event) {
            $(event.currentTarget).closest('.beverage').toggleClass('detailed');
        },

        remove: function () {
            if (this.rview) {
                this.rview.unbind();
            }
            return _.bind(Backbone.View.prototype.remove, this)();
        }
    });
});
<|endoftext|>


<commit_before>"use strict";

var git_path = "/home/dan/test.git/.git/";

var fs = require('fs');

var platform = require('git-node-platform');
var jsGit = require('js-git');
var fsDb = require('git-fs-db')(platform);
var pfs = platform.fs;
var db = fsDb(pfs(git_path));

var repo = jsGit(db);

var branches = [];
var branch_hashes = [];
function Branch (name, hash) {
    this.name = name;
    this.hash = hash;
    this.color = getBranchColor();
}

function getBranchColor() {
    switch (branches.length) {
        default:
            return "label-primary";
            break;
        case 1:
            return "label-success";
            break;
        case 2:
            return "label-info";
            break;
        case 3:
            return "label-warning";
            break;
        case 4:
            return "label-danger";
            break;
    }
}

var current_branch = "";

// app
var branch_list = getBranches();
var branches_obj = $('#branches');
for (var i=0; i < branch_list.length; i++) {
    var current_hash = getBranchHash(branch_list[i]);
    branches.push(new Branch(branch_list[i], current_hash));
    branch_hashes.push(current_hash);
    branches_obj.append(
        '<button type="button" class="btn btn-default" id="branch-' + branch_list[i] + '">' + branch_list[i] + '</button>'
    );
}
branches_obj.children('button').each(function () {
    $(this).click(function () {
        loadBranch(this.innerHTML);
    });
});
loadBranch("master");
// /app

function getBranches() {
    return fs.readdirSync(git_path + 'refs/heads/');
}

function getBranchHash(branch) {
    var contents = fs.readFileSync(git_path + 'refs/heads/' + branch, 'utf8');
    return contents.replace(/(\r\n|\n|\r)/gm, "");
}

function loadBranch(branch) {
    selectBranch(branch);
    $('#tree').empty();
    loadCommit(branch);
}

function deselectBranch(branch) {
    var branch_obj = $('#branch-' + branch);
    branch_obj.removeClass('btn-primary');
    branch_obj.addClass('btn-default');
}

function selectBranch(branch) {
    if (current_branch != "") deselectBranch(current_branch);
    current_branch = branch;
    var branch_obj = $('#branch-' + branch);
    branch_obj.removeClass('btn-default');
    branch_obj.addClass('btn-primary');
}

function loadCommit(hashish) {
    repo.loadAs("commit", hashish, onCommit);
}

function onCommit(err, commit, hash) {
    if (err) throw err;
    console.log("COMMIT", hash, commit);

    var buffer = '
';
    var branch = branch_hashes.indexOf(hash);
    console.log(branch_hashes);

    if (branch != -1) buffer += '' + branches[branch].name + ' ';

    buffer += commit.message +
        '  - ' + commit.author.name + ' <' + commit.author.email + '>';

    buffer += '
';

    $('#tree').append(buffer);

    //loadTree(commit.tree);
    if (commit.parents) {
        commit.parents.forEach(loadCommit);
    }
}

function loadTree(hash) {
    repo.loadAs("tree", hash, onTree);
}

function onTree(err, tree, hash) {
    if (err) throw err;
    console.log("TREE", hash, tree);
    tree.forEach(onEntry);
}

function onEntry(entry) {
    repo.loadAs("blob", entry.hash, function (err, blob) {
        if (err) throw err;
        console.log("BLOB", entry.hash, blob);
    });
}<commit_msg>added check for duplicate commits (temp-fix), errors are logged and now thrown now.<commit_after>"use strict";

var git_path = "/home/dan/test.git/.git/";

var fs = require('fs');

var platform = require('git-node-platform');
var jsGit = require('js-git');
var fsDb = require('git-fs-db')(platform);
var pfs = platform.fs;
var db = fsDb(pfs(git_path));

var repo = jsGit(db);

var previous_commit = "";

var branches = [];
var branch_hashes = [];
function Branch (name, hash) {
    this.name = name;
    this.hash = hash;
    this.color = getBranchColor();
}

function getBranchColor() {
    switch (branches.length) {
        default:
            return "label-primary";
            break;
        case 1:
            return "label-success";
            break;
        case 2:
            return "label-info";
            break;
        case 3:
            return "label-warning";
            break;
        case 4:
            return "label-danger";
            break;
    }
}

var current_branch = "";

// app
var branch_list = getBranches();
var branches_obj = $('#branches');
for (var i=0; i < branch_list.length; i++) {
    var current_hash = getBranchHash(branch_list[i]);
    branches.push(new Branch(branch_list[i], current_hash));
    branch_hashes.push(current_hash);
    branches_obj.append(
        '<button type="button" class="btn btn-default" id="branch-' + branch_list[i] + '">' + branch_list[i] + '</button>'
    );
}
branches_obj.children('button').each(function () {
    $(this).click(function () {
        loadBranch(this.innerHTML);
    });
});
loadBranch("master");
// /app

function getBranches() {
    return fs.readdirSync(git_path + 'refs/heads/');
}

function getBranchHash(branch) {
    var contents = fs.readFileSync(git_path + 'refs/heads/' + branch, 'utf8');
    return contents.replace(/(\r\n|\n|\r)/gm, "");
}

function loadBranch(branch) {
    selectBranch(branch);
    $('#tree').empty();
    loadCommit(branch);
}

function deselectBranch(branch) {
    var branch_obj = $('#branch-' + branch);
    branch_obj.removeClass('btn-primary');
    branch_obj.addClass('btn-default');
}

function selectBranch(branch) {
    if (current_branch != "") deselectBranch(current_branch);
    current_branch = branch;
    var branch_obj = $('#branch-' + branch);
    branch_obj.removeClass('btn-default');
    branch_obj.addClass('btn-primary');
}

function loadCommit(hashish) {
    repo.loadAs("commit", hashish, onCommit);
}

function onCommit(err, commit, hash) {
    if (hash == previous_commit) {
        console.log("DUPLICATE COMMIT: " + hash);
        return;
    }
    if (err) {
        console.log("FATAL ERROR: " + err);
        return;
    }
    //console.log("COMMIT", hash, commit);

    var buffer = '
';
    var branch = branch_hashes.indexOf(hash);

    if (branch != -1) buffer += '' + branches[branch].name + ' ';

    buffer += commit.message +
        '  - ' + commit.author.name + ' <' + commit.author.email + '>';

    buffer += '
';

    $('#tree').append(buffer);

    if (commit.parents) {
        commit.parents.forEach(loadCommit);
    }

    previous_commit = hash;
}<|endoftext|>


<commit_before>define([
    'underscore',
    'backbone',
    'lib/rivets-cfg',
    'jquery',

    '../model/beverage/beverages',
    '../model/order/order-summary',

    'i18n',

    '../template/beverages.html',

    'bootstrap/dist/js/umd/tooltip',
    'bootstrap/dist/js/umd/dropdown',

    '../../scss/beverages.scss'
], function (_, Backbone, rivets, $, // eslint-disable-line max-params
             Beverages, Orders,
             i18n, template) {
    'use strict';

    /* === Constants === */
    /* eslint-disable no-magic-numbers */
    /**
     * The value of the progress bar for the theine level (max is 100).
     *
     * The 'unknown' value is 100: the bar must be fully filled and will be styled differently.s
     * @type {Object}
     */
    var THEINE_LEVEL_PC = Object.freeze({
            none: 0,
            low: 25,
            medium: 50,
            high: 75,
            coffee: 100,
            unknown: 100
        }),
        AUTO_TIME_RANGE = Object.freeze({
            morning: [5, 11],
            daytime: [10, 20],
            evening: [18, 2],
            unknown: [0, 24]
        });
    /* eslint-enable no-magic-numbers */ // eslint-disable-line lines-around-comment

    /* === Rivets configuration === */
    rivets.formatters.theineLevel100 = function (value) {
        return THEINE_LEVEL_PC[value || 'unknown'];
    };

    rivets.formatters.minMax = function (value, separator) {
        if (value) {
            if (value.min) {
                return value.max && value.min !== value.max
                    ? value.min + (separator ? separator : '-') + value.max
                    : value.min;
            } else if (value.max) {
                return value.max;
            }
        }
        return '';
    };

    rivets.formatters.showMinMax = function (value) {
        return value && (value.min || value.max);
    };

    /* === Constants & functions === */
    var isInTimeRange = function (range) {
        var min = AUTO_TIME_RANGE[range][0],
            max = AUTO_TIME_RANGE[range][1],
            time = new Date().getHours();
        return min <= max ? min <= time && time < max : min <= time || time < max;
    };

    /* === Backbone view === */
    return Backbone.View.extend({

        /** {Beverages} The full collection of all available beverages. */
        beverages: null,

        /** {Orders} The passed orders. */
        orders: null,

        /** Filters */
        filters: null,

        context: null,

        rview: null,

        events: {
            'change .filters :checkbox': '_filterBeverages',
            'click .beverage .btn-more': '_toggleDetail',
            'click .beverage .btn-pick': '_pick'
        },

        initialize: function (options) {
            this.context = {
                i18n: i18n,
                ready: false,
                error: false
            };

            var gSheetId;
            if (options) {
                gSheetId = options.gSheetId;
                this._initFilters(options.filters);
            }

            if (gSheetId) {
                this._initAndFetchBeverages(gSheetId);
                this.orders = new Orders();
            } else {
                this.context.error = this.context.i18n.error.configuration;
            }
        },

        render: function () {
            // remove any previous binding
            if (this.rview) {
                this.rview.unbind();
            }

            // bind template to context
            this.context.beverages = this.beverages;
            this.context.orders = this.orders;
            this.context.filters = this.filters;
            this.rview = rivets.bind(this.$el.html(template), this.context);

            // init tooltips
            this._tooltip();

            // done, return the result
            return this;
        },

        _initFilters: function (options) {
            var availableBases = _.keys(this.context.i18n.basis),
                availableMoments = _.keys(this.context.i18n.moment);

            var settings = _.defaults(options || {}, {
                basis: availableBases
            });
            if (!settings.autoTime && !settings.moments) {
                settings.moments = availableMoments;
            }

            if (_.contains(settings.basis, 'teas')) {
                settings.basis = settings.basis.concat(_.filter(availableBases, function (basis) {
                    // Ensure the basis begins with 'tea-'
                    // .startsWith in not supported in IE
                    return (/^tea-/).test(basis);
                }));
            }

            this.filters = {
                bases: null,
                moments: null
            };

            this.filters.bases = [];
            for (var iBases = 0; iBases < availableBases.length; iBases++) {
                var basis = availableBases[iBases];
                this.filters.bases.push({
                    key: basis,
                    active: _.indexOf(settings.basis, basis) > -1
                });
            }

            this.filters.moments = [];
            for (var iTimes = 0; iTimes < availableMoments.length; iTimes++) {
                var moment = availableMoments[iTimes];
                this.filters.moments.push({
                    key: moment,
                    active: _.indexOf(settings.moments, moment) > -1 || options.autoTime && isInTimeRange(moment)
                });
            }
        },

        _initAndFetchBeverages: function (gSheetId) {
            this.beverages = new Beverages(null, {
                gSheetId: gSheetId
            });

            this.beverages
                .on('request', function () {
                    this.context.ready = false;
                }, this)
                .on('sync', function () {
                    this._filterBeverages();
                    this.context.ready = true;
                    this._tooltip();
                }, this)
                .fetch({
                    error: _.bind(function () {
                        this.context.error = this.context.i18n.error.loading;
                    }, this)
                });
        },

        _filterBeverages: function () {
            var shown = 0;
            this.beverages.each(function (beverage) {
                beverage._show = this._isBasisActive(beverage) && this._isMomentActive(beverage);
                if (beverage._show) {
                    shown++;
                }
            }.bind(this));
            this.beverages._shown = shown;
        },

        _isBasisActive: function (beverage) {
            return _.find(this.filters.bases, function (basis) {
                return beverage.get('basis') === basis.key;
            }).active;
        },

        _isMomentActive: function (beverage) {
            var times = beverage.get('time'),
                keys = _.keys(times);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i],
                    time = times[key];
                if (!rivets.formatters.defined(time) && _.findWhere(this.filters.moments, {key: 'unknown'}).active
                    || time && _.findWhere(this.filters.moments, {key: key}).active) {
                    return true;
                }
            }
            return false;
        },

        /**
         * Initializes the tooltips and popovers of the rendered page. This is set only on existing elements: the method
         * should be called again any time a new tooltip trigger element is added to the page.
         */
        _tooltip: function () {
            this.$('[data-toggle="tooltip"]').tooltip();
        },

        _toggleDetail: function (event) {
            var clicked = this._getClickedBeverage(event);
            clicked._detailed = !clicked._detailed;
        },

        _pick: function (event) {
            this.orders.order(this._getClickedBeverage(event));
        },

        _getClickedBeverage: function (clickEvent) {
            var id = $(clickEvent.currentTarget).closest('.beverage').data('id');
            return this.beverages.get(id);
        },

        remove: function () {
            if (this.rview) {
                this.rview.unbind();
            }
            return _.bind(Backbone.View.prototype.remove, this)();
        }
    });
});
<commit_msg>Fix filters if the basis is not a registered value<commit_after>define([
    'underscore',
    'backbone',
    'lib/rivets-cfg',
    'jquery',

    '../model/beverage/beverages',
    '../model/order/order-summary',

    'i18n',

    '../template/beverages.html',

    'bootstrap/dist/js/umd/tooltip',
    'bootstrap/dist/js/umd/dropdown',

    '../../scss/beverages.scss'
// eslint-disable-next-line max-params
], function (_, Backbone, rivets, $,
             Beverages, Orders,
             i18n, template) {
    'use strict';

    /* === Constants === */
    /* eslint-disable no-magic-numbers */
    /**
     * The value of the progress bar for the theine level (max is 100).
     *
     * The 'unknown' value is 100: the bar must be fully filled and will be styled differently.s
     * @type {Object}
     */
    var THEINE_LEVEL_PC = Object.freeze({
            none: 0,
            low: 25,
            medium: 50,
            high: 75,
            coffee: 100,
            unknown: 100
        }),
        AUTO_TIME_RANGE = Object.freeze({
            morning: [5, 11],
            daytime: [10, 20],
            evening: [18, 2],
            unknown: [0, 24]
        });
    /* eslint-enable no-magic-numbers */ // eslint-disable-line lines-around-comment

    /* === Rivets configuration === */
    rivets.formatters.theineLevel100 = function (value) {
        return THEINE_LEVEL_PC[value || 'unknown'];
    };

    rivets.formatters.minMax = function (value, separator) {
        if (value) {
            if (value.min) {
                return value.max && value.min !== value.max
                    ? value.min + (separator ? separator : '-') + value.max
                    : value.min;
            } else if (value.max) {
                return value.max;
            }
        }
        return '';
    };

    rivets.formatters.showMinMax = function (value) {
        return value && (value.min || value.max);
    };

    /* === Constants & functions === */
    var isInTimeRange = function (range) {
        var min = AUTO_TIME_RANGE[range][0],
            max = AUTO_TIME_RANGE[range][1],
            time = new Date().getHours();
        return min <= max ? min <= time && time < max : min <= time || time < max;
    };

    /* === Backbone view === */
    return Backbone.View.extend({

        /** {Beverages} The full collection of all available beverages. */
        beverages: null,

        /** {Orders} The passed orders. */
        orders: null,

        /** Filters */
        filters: null,

        context: null,

        rview: null,

        events: {
            'change .filters :checkbox': '_filterBeverages',
            'click .beverage .btn-more': '_toggleDetail',
            'click .beverage .btn-pick': '_pick'
        },

        initialize: function (options) {
            this.context = {
                i18n: i18n,
                ready: false,
                error: false
            };

            var gSheetId;
            if (options) {
                gSheetId = options.gSheetId;
                this._initFilters(options.filters);
            }

            if (gSheetId) {
                this._initAndFetchBeverages(gSheetId);
                this.orders = new Orders();
            } else {
                this.context.error = this.context.i18n.error.configuration;
            }
        },

        render: function () {
            // remove any previous binding
            if (this.rview) {
                this.rview.unbind();
            }

            // bind template to context
            this.context.beverages = this.beverages;
            this.context.orders = this.orders;
            this.context.filters = this.filters;
            this.rview = rivets.bind(this.$el.html(template), this.context);

            // init tooltips
            this._tooltip();

            // done, return the result
            return this;
        },

        _initFilters: function (options) {
            var availableBases = _.keys(this.context.i18n.basis),
                availableMoments = _.keys(this.context.i18n.moment);

            var settings = _.defaults(options || {}, {
                basis: availableBases
            });
            if (!settings.autoTime && !settings.moments) {
                settings.moments = availableMoments;
            }

            if (_.contains(settings.basis, 'teas')) {
                settings.basis = settings.basis.concat(_.filter(availableBases, function (basis) {
                    // Ensure the basis begins with 'tea-'
                    // .startsWith in not supported in IE
                    return (/^tea-/).test(basis);
                }));
            }

            this.filters = {
                bases: null,
                moments: null
            };

            this.filters.bases = [];
            for (var iBases = 0; iBases < availableBases.length; iBases++) {
                var basis = availableBases[iBases];
                this.filters.bases.push({
                    key: basis,
                    active: _.indexOf(settings.basis, basis) > -1
                });
            }

            this.filters.moments = [];
            for (var iTimes = 0; iTimes < availableMoments.length; iTimes++) {
                var moment = availableMoments[iTimes];
                this.filters.moments.push({
                    key: moment,
                    active: _.indexOf(settings.moments, moment) > -1 || options.autoTime && isInTimeRange(moment)
                });
            }
        },

        _initAndFetchBeverages: function (gSheetId) {
            this.beverages = new Beverages(null, {
                gSheetId: gSheetId
            });

            this.beverages
                .on('request', function () {
                    this.context.ready = false;
                }, this)
                .on('sync', function () {
                    this._filterBeverages();
                    this.context.ready = true;
                    this._tooltip();
                }, this)
                .fetch({
                    error: _.bind(function () {
                        this.context.error = this.context.i18n.error.loading;
                    }, this)
                });
        },

        _filterBeverages: function () {
            var shown = 0;
            this.beverages.each(function (beverage) {
                beverage._show = this._isBasisActive(beverage) && this._isMomentActive(beverage);
                if (beverage._show) {
                    shown++;
                }
            }.bind(this));
            this.beverages._shown = shown;
        },

        _isBasisActive: function (beverage) {
            return (_.find(this.filters.bases, function (basis) {
                return beverage.get('basis') === basis.key;
            }) || {active: true}).active;
        },

        _isMomentActive: function (beverage) {
            var times = beverage.get('time'),
                keys = _.keys(times);
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i],
                    time = times[key];
                if (!rivets.formatters.defined(time) && _.findWhere(this.filters.moments, {key: 'unknown'}).active
                    || time && _.findWhere(this.filters.moments, {key: key}).active) {
                    return true;
                }
            }
            return false;
        },

        /**
         * Initializes the tooltips and popovers of the rendered page. This is set only on existing elements: the method
         * should be called again any time a new tooltip trigger element is added to the page.
         */
        _tooltip: function () {
            this.$('[data-toggle="tooltip"]').tooltip();
        },

        _toggleDetail: function (event) {
            var clicked = this._getClickedBeverage(event);
            clicked._detailed = !clicked._detailed;
        },

        _pick: function (event) {
            this.orders.order(this._getClickedBeverage(event));
        },

        _getClickedBeverage: function (clickEvent) {
            var id = $(clickEvent.currentTarget).closest('.beverage').data('id');
            return this.beverages.get(id);
        },

        remove: function () {
            if (this.rview) {
                this.rview.unbind();
            }
            return _.bind(Backbone.View.prototype.remove, this)();
        }
    });
});
<|endoftext|>


<commit_before>$(function() {

  var jenkins_root_url = 'http://ci-legacy.cloudfoundry.org';
  var jenkins_views = [];
  var default_refresh_secs = 60;
  var timer_id = 0;

  // Map Jenkins status colors -> status
  var map_color_to_status = {
    blue: 'Success',
    blue_anime: 'Success',
    yellow: 'Unstable',
    yellow_anime: 'Unstable',
    red: 'Failed',
    red_anime: 'Failed',
    disabled: 'Disabled',
    grey: 'Disabled',
  };

  // Initialize the UI
  $('#jenkins_root_url').text(jenkins_root_url);
  $('#refresh_in_x_secs').text(default_refresh_secs);
  $('#enable_auto_refresh').show();
  $('#disable_auto_refresh').hide();
  refreshViews(jenkins_root_url);

  // Click this button to refetch data from Jenkins
  $('#refresh_now').on('click', function() {
    refreshViews(jenkins_root_url);
  });

  // Click these two buttons to Enable/Disable Auto-Refresh
  $('#enable_auto_refresh, #disable_auto_refresh').on('click', function() {
    $(this).hide();

    if ($(this).attr('id') == 'enable_auto_refresh') {
      refreshViews(jenkins_root_url);

      timer_id = setInterval(function() {
        refreshViews(jenkins_root_url);
      }, default_refresh_secs * 1000);

      $('#disable_auto_refresh').show();
    } else if ($(this).attr('id') == 'disable_auto_refresh') {
      clearInterval(timer_id);

      $('#enable_auto_refresh').show();
    }
  });

  // Target a different Jenkins
  $('#update_jenkins_target').on('click', function() {
    if ($('#jenkins_target').val()) {
      jenkins_root_url = 'http://' + $('#jenkins_target').val();
      refreshViews(jenkins_root_url);
      $('#jenkins_root_url').text(jenkins_root_url);
      $('#jenkins_target').val('');
    }
    return false;
  });

  // Build a human-readable string of the current date/time
  function getDateTime() {
    var now_date = new Date();
    return now_date.toLocaleTimeString() + " - " + now_date.toLocaleDateString();
  }

  // Build a human-readable string of the difference in time
  function getTimeDeltaStr(past_timestamp) {
    var now_ts_normalized = new Date().getTime() / 1000;
    var delta_seconds = now_ts_normalized - past_timestamp;
    var seconds_in_minute = 60;
    var seconds_in_hour = 60 * seconds_in_minute;
    var seconds_in_day = 24 * seconds_in_hour;
    var seconds_in_week = 7 * seconds_in_day;
    var seconds_in_month = 30 * seconds_in_day;

    if (delta_seconds < seconds_in_minute) {
      return '1 min';

    } else if (delta_seconds < seconds_in_hour) {
      return (delta_seconds / seconds_in_minute).toFixed(1) + ' mins';

    } else if (delta_seconds < seconds_in_day) {
      return (delta_seconds / seconds_in_hour).toFixed(1) + ' hours';

    } else if (delta_seconds < seconds_in_week) {
      return (delta_seconds / seconds_in_day).toFixed(1) + ' days';

    } else if (delta_seconds < seconds_in_month) {
      return (delta_seconds / seconds_in_week).toFixed(1) + ' weeks';

    } else if (delta_seconds >= seconds_in_month) {
      return (delta_seconds / seconds_in_week).toFixed(1) + ' months';
    }
  }

  function refreshViews(jenkins_view_url) {
    $('#throbber').show();
    $('div.content_box').fadeOut('slow');
    $('div.content_box').remove();
    $('#updated_at').text( getDateTime() );

    // Define the tree structure of the JSON response from Jenkins
    jenkins_view_url += '/api/json?tree=views[name,url,jobs[name,color,url,lastFailedBuild[timestamp],healthReport[description,score]]]&jsonp=?';

    $.getJSON(jenkins_view_url, function(data) {
      $.each(data.views, function(index, view_data) {

        // To avoid overloading the UI, skip any Jenkins view named 'All'
        if (view_data.name.indexOf('All') >= 0) {
          return true;
        }

        var view_jobs = view_data.jobs;
        var view_jobs_coverage = [];
        var view_jobs_num_failing_tests = 0;
        var view_jobs_num_total_tests = 0;
        var view_jobs_list = [];
        var last_failed_builds = [];

        // Start building the HTML display
        var view_content_box = $('
', {
          class: 'content_box',
        });

        var view_title = '
' + view_data.name + '
';
        view_content_box.append(view_title);

        $.each(view_jobs, function(index, job_data) {

          // Skip disabled jobs + jobs with specified ommitted prefixes
          if ( (job_data.name.indexOf('DELENG') < 0) && (map_color_to_status[job_data.color] != 'Disabled') ) {

            if (job_data.lastFailedBuild) {
              last_failed_builds.push(job_data.lastFailedBuild.timestamp / 1000);
            }

            view_jobs_list.push({
              name: job_data.name,
              status: map_color_to_status[job_data.color],
              url: job_data.url
            });

            $.each(job_data.healthReport, function(index, healthReport_data) {
              // Parse out the code coverage
              var re = /\((\S+)\)/;
              var match = re.exec(healthReport_data.description);
              if (match && match.length > 0) {
                view_jobs_coverage.push(match[1]);
              }

              // Parse out the test results
              var re = /Test Result: (\d+) tests* failing out of a total of (\d+) tests*/;
              var match = re.exec(healthReport_data.description);
              if (match && match.length > 1) {
                view_jobs_num_failing_tests += parseInt(match[1]);
                view_jobs_num_total_tests += parseInt(match[2]);
              }
            });
          }
        });

        // Calculate last failed build
        var last_failed_build_str = (last_failed_builds.length > 0) ? getTimeDeltaStr(_.max(last_failed_builds)) : '???';

        // Calculate average coverage
        var coverage_sum = _.reduce(view_jobs_coverage, function(memo, num) { return memo + parseFloat(num, 10); }, 0);
        var coverage_avg = coverage_sum / view_jobs_coverage.length;
        coverage_avg = isNaN(coverage_avg) ? '???' : coverage_avg.toFixed(2);

        var job_info_data = {
          'Tests failed' : view_jobs_num_failing_tests + " / " + view_jobs_num_total_tests,
          'Coverage' : coverage_avg + " %",
          'Since last job failed' : last_failed_build_str,
          'Test duration' : '?',
        };

        $.each(job_info_data, function(stat_name, stat_value) {
          var job_info_box = $('
', {
            class: 'job_info_box',
          });

          $('
', {
            text: stat_value,
            class: 'job_stat_value',
          }).appendTo(job_info_box);

          $('
', {
            text: stat_name,
            class: 'job_stat_name',
          }).appendTo(job_info_box);

          job_info_box.appendTo(view_content_box);
        });

        var job_boxes_container = $('
', {
          class: 'job_boxes_container clear',
        });

        // Add the job status boxes
        $.each(view_jobs_list, function(index, view_job) {
          $('', {
            class: 'job_status',
            title: view_job.name,
            href: view_job.url,
            target: '_blank',
          })
            .addClass('status-' + view_job.status.toLowerCase())
            .appendTo(job_boxes_container);
        });

        job_boxes_container.appendTo(view_content_box);
        view_content_box.appendTo('div.content_box_container');
      });
    });

    $('#throbber').fadeOut('slow');
  }

});

<commit_msg>Show build status in tooltip<commit_after>$(function() {

  var jenkins_root_url = 'http://ci-legacy.cloudfoundry.org';
  var jenkins_views = [];
  var default_refresh_secs = 60;
  var timer_id = 0;

  // Map Jenkins status colors -> status
  var map_color_to_status = {
    blue: 'Success',
    blue_anime: 'Success',
    yellow: 'Unstable',
    yellow_anime: 'Unstable',
    red: 'Failed',
    red_anime: 'Failed',
    disabled: 'Disabled',
    grey: 'Disabled',
  };

  // Initialize the UI
  $('#jenkins_root_url').text(jenkins_root_url);
  $('#refresh_in_x_secs').text(default_refresh_secs);
  $('#enable_auto_refresh').show();
  $('#disable_auto_refresh').hide();
  refreshViews(jenkins_root_url);

  // Click this button to refetch data from Jenkins
  $('#refresh_now').on('click', function() {
    refreshViews(jenkins_root_url);
  });

  // Click these two buttons to Enable/Disable Auto-Refresh
  $('#enable_auto_refresh, #disable_auto_refresh').on('click', function() {
    $(this).hide();

    if ($(this).attr('id') == 'enable_auto_refresh') {
      refreshViews(jenkins_root_url);

      timer_id = setInterval(function() {
        refreshViews(jenkins_root_url);
      }, default_refresh_secs * 1000);

      $('#disable_auto_refresh').show();
    } else if ($(this).attr('id') == 'disable_auto_refresh') {
      clearInterval(timer_id);

      $('#enable_auto_refresh').show();
    }
  });

  // Target a different Jenkins
  $('#update_jenkins_target').on('click', function() {
    if ($('#jenkins_target').val()) {
      jenkins_root_url = 'http://' + $('#jenkins_target').val();
      refreshViews(jenkins_root_url);
      $('#jenkins_root_url').text(jenkins_root_url);
      $('#jenkins_target').val('');
    }
    return false;
  });

  // Build a human-readable string of the current date/time
  function getDateTime() {
    var now_date = new Date();
    return now_date.toLocaleTimeString() + " - " + now_date.toLocaleDateString();
  }

  // Build a human-readable string of the difference in time
  function getTimeDeltaStr(past_timestamp) {
    var now_ts_normalized = new Date().getTime() / 1000;
    var delta_seconds = now_ts_normalized - past_timestamp;
    var seconds_in_minute = 60;
    var seconds_in_hour = 60 * seconds_in_minute;
    var seconds_in_day = 24 * seconds_in_hour;
    var seconds_in_week = 7 * seconds_in_day;
    var seconds_in_month = 30 * seconds_in_day;

    if (delta_seconds < seconds_in_minute) {
      return '1 min';

    } else if (delta_seconds < seconds_in_hour) {
      return (delta_seconds / seconds_in_minute).toFixed(1) + ' mins';

    } else if (delta_seconds < seconds_in_day) {
      return (delta_seconds / seconds_in_hour).toFixed(1) + ' hours';

    } else if (delta_seconds < seconds_in_week) {
      return (delta_seconds / seconds_in_day).toFixed(1) + ' days';

    } else if (delta_seconds < seconds_in_month) {
      return (delta_seconds / seconds_in_week).toFixed(1) + ' weeks';

    } else if (delta_seconds >= seconds_in_month) {
      return (delta_seconds / seconds_in_week).toFixed(1) + ' months';
    }
  }

  function refreshViews(jenkins_view_url) {
    $('#throbber').show();
    $('div.content_box').fadeOut('slow');
    $('div.content_box').remove();
    $('#updated_at').text( getDateTime() );

    // Define the tree structure of the JSON response from Jenkins
    jenkins_view_url += '/api/json?tree=views[name,url,jobs[name,color,url,lastFailedBuild[timestamp],healthReport[description,score]]]&jsonp=?';

    $.getJSON(jenkins_view_url, function(data) {
      $.each(data.views, function(index, view_data) {

        // To avoid overloading the UI, skip any Jenkins view named 'All'
        if (view_data.name.indexOf('All') >= 0) {
          return true;
        }

        var view_jobs = view_data.jobs;
        var view_jobs_coverage = [];
        var view_jobs_num_failing_tests = 0;
        var view_jobs_num_total_tests = 0;
        var view_jobs_list = [];
        var last_failed_builds = [];

        // Start building the HTML display
        var view_content_box = $('
', {
          class: 'content_box',
        });

        var view_title = '
' + view_data.name + '
';
        view_content_box.append(view_title);

        $.each(view_jobs, function(index, job_data) {

          // Skip disabled jobs + jobs with specified ommitted prefixes
          if ( (job_data.name.indexOf('DELENG') < 0) && (map_color_to_status[job_data.color] != 'Disabled') ) {

            if (job_data.lastFailedBuild) {
              last_failed_builds.push(job_data.lastFailedBuild.timestamp / 1000);
            }

            view_jobs_list.push({
              name: job_data.name,
              status: map_color_to_status[job_data.color],
              url: job_data.url
            });

            $.each(job_data.healthReport, function(index, healthReport_data) {
              // Parse out the code coverage
              var re = /\((\S+)\)/;
              var match = re.exec(healthReport_data.description);
              if (match && match.length > 0) {
                view_jobs_coverage.push(match[1]);
              }

              // Parse out the test results
              var re = /Test Result: (\d+) tests* failing out of a total of (\d+) tests*/;
              var match = re.exec(healthReport_data.description);
              if (match && match.length > 1) {
                view_jobs_num_failing_tests += parseInt(match[1]);
                view_jobs_num_total_tests += parseInt(match[2]);
              }
            });
          }
        });

        // Calculate last failed build
        var last_failed_build_str = (last_failed_builds.length > 0) ? getTimeDeltaStr(_.max(last_failed_builds)) : '???';

        // Calculate average coverage
        var coverage_sum = _.reduce(view_jobs_coverage, function(memo, num) { return memo + parseFloat(num, 10); }, 0);
        var coverage_avg = coverage_sum / view_jobs_coverage.length;
        coverage_avg = isNaN(coverage_avg) ? '???' : coverage_avg.toFixed(2);

        var job_info_data = {
          'Tests failed' : view_jobs_num_failing_tests + " / " + view_jobs_num_total_tests,
          'Coverage' : coverage_avg + " %",
          'Since last job failed' : last_failed_build_str,
          'Test duration' : '?',
        };

        $.each(job_info_data, function(stat_name, stat_value) {
          var job_info_box = $('
', {
            class: 'job_info_box',
          });

          $('
', {
            text: stat_value,
            class: 'job_stat_value',
          }).appendTo(job_info_box);

          $('
', {
            text: stat_name,
            class: 'job_stat_name',
          }).appendTo(job_info_box);

          job_info_box.appendTo(view_content_box);
        });

        var job_boxes_container = $('
', {
          class: 'job_boxes_container clear',
        });

        // Add the job status boxes
        $.each(view_jobs_list, function(index, view_job) {
          $('', {
            class: 'job_status',
            title: view_job.name + ' - ' + view_job.status,
            href: view_job.url,
            target: '_blank',
          })
            .addClass('status-' + view_job.status.toLowerCase())
            .appendTo(job_boxes_container);
        });

        job_boxes_container.appendTo(view_content_box);
        view_content_box.appendTo('div.content_box_container');
      });
    });

    $('#throbber').fadeOut('slow');
  }

});

<|endoftext|>