<commit_before>$(function () {
	// Attach FastClick
	FastClick.attach(document.body);

	// Caching jQuery objects
	var $body = $('body'),
		$current = $('.current'),
		$best = $('.best'),
		$table = $('table'),
		$td = $('td'),
		$repeat = $('.fa-repeat');

	// Scoring system
	var score = 0,
		best = Number.MAX_VALUE,
		scoring = function () {
			score++;
			$current.html(score);
		};
		window.scoreControl = setInterval(scoring, 1000);
	if (Cookies.get('best')) {
		best = parseInt(Cookies.get('best'), 10);
		$best.html(best);
	}

	// Initialize game
	set();
	$body.fadeIn(500);
	$td.on('click', tdClick);
	$repeat.on('click', repeatClick);

	// Functions
	function flip(cell) {
		var $cell = $('.' + cell);
		$cell.css('background-color', ($cell.css('background-color') === 'rgb(255, 255, 255)') ? 'black'  : 'white');
		return flip;
	}
	function set() {
		for (var i = 1;i <= 9;i++) {
			if (Math.random() < 0.5) {
				flip(i);
			}
		}
		if (areAllOff()) {
			set();
		}
	}
	function areAllOff() {
		var allOff = true;
		for (var i = 1;i <= 9;i++) {
			if ($('.' + i).css('background-color') === 'rgb(255, 255, 255)') {
				allOff = false;
				i = 10;
			}
		}
		return allOff;
	}
	function tdClick() {
		switch (parseInt($(this).attr('class'), 10)) {
			case 1:
				flip(1)
					(2)
					(4);
				break;
			case 2:
				flip(1)
					(2)
					(3)
					(5);
				break;
			case 3:
				flip(2)
					(3)
					(6);
				break;
			case 4:
				flip(1)
					(4)
					(5)
					(7);
				break;
			case 5:
				flip(2)
					(4)
					(5)
					(6)
					(8);
				break;
			case 6:
				flip(3)
					(5)
					(6)
					(9);
				break;
			case 7:
				flip(4)
					(7)
					(8);
				break;
			case 8:
				flip(5)
					(7)
					(8)
					(9);
				break;
			case 9:
				flip(6)
					(8)
					(9);
				break;
		}
		if (areAllOff()) {
			clearInterval(scoreControl);
			best = (score < best) ? score : best;
			Cookies.set('best', best);
			$best.html(best);

			$td.off('click');
			$table.fadeOut(500);

			setTimeout(function () {
				$repeat.fadeIn(500, function () {
					$repeat.on('click', repeatClick);
				});
			}, 500);
		}
	}
	function repeatClick() {
		// Don't forget that this is a temporary fix.
		location.reload();

		$current.html(score = 0);
		clearInterval(scoreControl);
		window.scoreControl = setInterval(scoring, 1000);

		$repeat.off('click');
		$repeat.fadeOut(500);
		set();

		setTimeout(function () {
			$table.fadeIn(500, function () {
				$td.on('click', tdClick);
			});
		}, 500);
	}
});
/*
var score = 0;
var score_best = Number.MAX_VALUE;
$(function() {
	FastClick.attach(document.body);
	if (Cookies.get("score_best")) {
		score_best = parseInt(Cookies.get("score_best"), 10);
		$(".score-best").empty();
		$(".score-best").append(score_best);
	}
	//Randomize position
	if (Math.random() >= 0.5) {
		flip(".1");
	}
	if (Math.random() >= 0.5) {
		flip(".2");
	}
	if (Math.random() >= 0.5) {
		flip(".3");
	}
	if (Math.random() >= 0.5) {
		flip(".4");
	}
	if (Math.random() >= 0.5) {
		flip(".5");
	}
	if (Math.random() >= 0.5) {
		flip(".6");
	}
	if (Math.random() >= 0.5) {
		flip(".7");
	}
	if (Math.random() >= 0.5) {
		flip(".8");
	}
	if (Math.random() >= 0.5) {
		flip(".9");
	}
	//Game control
	$("td").click(function() {
		switch ($(this).attr("class")) {
			case "1":
				flip(".1");
				flip(".2");
				flip(".4");
				break;
			case "2":
				flip(".1");
				flip(".2");
				flip(".3");
				flip(".5");
				break;
			case "3":
				flip(".2");
				flip(".3");
				flip(".6");
				break;
			case "4":
				flip(".1");
				flip(".4");
				flip(".5");
				flip(".7");
				break;
			case "5":
				flip(".2");
				flip(".4");
				flip(".5");
				flip(".6");
				flip(".8");
				break;
			case "6":
				flip(".3");
				flip(".5");
				flip(".6");
				flip(".9");
				break;
			case "7":
				flip(".4");
				flip(".7");
				flip(".8");
				break;
			case "8":
				flip(".5");
				flip(".7");
				flip(".8");
				flip(".9");
				break;
			case "9":
				flip(".6");
				flip(".8");
				flip(".9");
				break;
		}
	});
	setTimeout(function() {
		$(".score, #wrapper, .share, table").fadeIn("slow");
		window.scoring = setInterval(function() {
			score += 1;
			$(".score-current").empty();
			$(".score-current").append(score);
		}, 1000);
	}, 2000);
	//Rickroll
	//$("table").click(function(){window.open("https://youtu.be/dQw4w9WgXcQ","_blank");});
});
//Self-explanatory light switching
function flip(light) {
	if ($(light).css("background-color") === "rgb(255, 255, 255)") {
		$(light).css("background-color", "black");
	} else {
		$(light).css("background-color", "white");
	}
	if (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
		setTimeout(function() {
			if (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
				if (score < score_best) {
					score_best = score;
					Cookies.set("score_best", score_best.toString(10), {
						expires: 365
					});
				}
				clearInterval(scoring);
				setTimeout(function() {
					if (Math.random() >= 0.5) {
						flip(".1");
					}
					if (Math.random() >= 0.5) {
						flip(".2");
					}
					if (Math.random() >= 0.5) {
						flip(".3");
					}
					if (Math.random() >= 0.5) {
						flip(".4");
					}
					if (Math.random() >= 0.5) {
						flip(".5");
					}
					if (Math.random() >= 0.5) {
						flip(".6");
					}
					if (Math.random() >= 0.5) {
						flip(".7");
					}
					if (Math.random() >= 0.5) {
						flip(".8");
					}
					if (Math.random() >= 0.5) {
						flip(".9");
					}
				}, 1000);
				//Hide table and best score
				$("table, .score-best").fadeOut("slow");
				setTimeout(function() {
					$(".replay, .share").fadeIn("slow");
				}, 500);
				$(".replay").click(function() {
					if (score < score_best) {
						$(".score-best").empty();
						$(".score-best").append(score);
					}
					if (Cookies.get("score_best")) {
						score_best = parseInt(Cookies.get("score_best"), 10);
						$(".score-best").empty();
						$(".score-best").append(score_best);
					}
					setTimeout(function() {
						score = 0;
						$(".score-current").empty();
						$(".score-current").append(score);
					}, 10);
					setTimeout(function() {
						clearInterval(scoring);
						window.scoring = setInterval(function() {
							score += 1;
							$(".score-current").empty();
							$(".score-current").append(score);
						}, 1000);
					}, 500);
					$(".replay").fadeOut("slow");
					while (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
						//Randomize position
						if (Math.random() >= 0.5) {
							flip(".1");
						}
						if (Math.random() >= 0.5) {
							flip(".2");
						}
						if (Math.random() >= 0.5) {
							flip(".3");
						}
						if (Math.random() >= 0.5) {
							flip(".4");
						}
						if (Math.random() >= 0.5) {
							flip(".5");
						}
						if (Math.random() >= 0.5) {
							flip(".6");
						}
						if (Math.random() >= 0.5) {
							flip(".7");
						}
						if (Math.random() >= 0.5) {
							flip(".8");
						}
						if (Math.random() >= 0.5) {
							flip(".9");
						}
					}
					setTimeout(function() {
						//Fade-in table and best score
						$("table, .score-best").fadeIn("slow");
					}, 500);
				});
			}
		}, 10);
	}
}*/
<commit_msg>Cool fade animations for scores<commit_after>$(function () {
	// Attach FastClick
	FastClick.attach(document.body);

	// Caching jQuery objects
	var $body = $('body'),
		$current = $('.current'),
		$best = $('.best'),
		$table = $('table'),
		$td = $('td'),
		$repeat = $('.fa-repeat');

	// Scoring system
	var score = 0,
		best = Number.MAX_VALUE,
		scoring = function () {
			score++;
			$current.html(score);
		};
		window.scoreControl = setInterval(scoring, 1000);
	if (Cookies.get('best')) {
		best = parseInt(Cookies.get('best'), 10);
		$best.html(best);
	}

	// Initialize game
	set();
	$body.fadeIn(500);
	$td.on('click', tdClick);
	$repeat.on('click', repeatClick);

	// Functions
	function flip(cell) {
		var $cell = $('.' + cell);
		$cell.css('background-color', ($cell.css('background-color') === 'rgb(255, 255, 255)') ? 'black'  : 'white');
		return flip;
	}
	function set() {
		for (var i = 1;i <= 9;i++) {
			if (Math.random() < 0.5) {
				flip(i);
			}
		}
		if (areAllOff()) {
			set();
		}
	}
	function areAllOff() {
		var allOff = true;
		for (var i = 1;i <= 9;i++) {
			if ($('.' + i).css('background-color') === 'rgb(255, 255, 255)') {
				allOff = false;
				i = 10;
			}
		}
		return allOff;
	}
	function tdClick() {
		switch (parseInt($(this).attr('class'), 10)) {
			case 1:
				flip(1)
					(2)
					(4);
				break;
			case 2:
				flip(1)
					(2)
					(3)
					(5);
				break;
			case 3:
				flip(2)
					(3)
					(6);
				break;
			case 4:
				flip(1)
					(4)
					(5)
					(7);
				break;
			case 5:
				flip(2)
					(4)
					(5)
					(6)
					(8);
				break;
			case 6:
				flip(3)
					(5)
					(6)
					(9);
				break;
			case 7:
				flip(4)
					(7)
					(8);
				break;
			case 8:
				flip(5)
					(7)
					(8)
					(9);
				break;
			case 9:
				flip(6)
					(8)
					(9);
				break;
		}
		if (areAllOff()) {
			clearInterval(scoreControl);
			if (score < best) {
				best = score;
				Cookies.set('best', best);
				$best.fadeOut(125, function () {
					$best.html(best);
					$best.fadeIn(125);
				})
			}

			$td.off('click');
			$table.fadeOut(500);

			setTimeout(function () {
				$repeat.fadeIn(500, function () {
					$repeat.on('click', repeatClick);
				});
			}, 500);
		}
	}
	function repeatClick() {
		// Don't forget that this is a temporary fix.
		location.reload();

		$current.fadeOut(125, function () {
			$current.html(score = 0);
			$current.fadeIn(125);
		});
		clearInterval(scoreControl);
		window.scoreControl = setInterval(scoring, 1000);

		$repeat.off('click');
		$repeat.fadeOut(500);
		set();

		setTimeout(function () {
			$table.fadeIn(500, function () {
				$td.on('click', tdClick);
			});
		}, 500);
	}
});
/*
var score = 0;
var score_best = Number.MAX_VALUE;
$(function() {
	FastClick.attach(document.body);
	if (Cookies.get("score_best")) {
		score_best = parseInt(Cookies.get("score_best"), 10);
		$(".score-best").empty();
		$(".score-best").append(score_best);
	}
	//Randomize position
	if (Math.random() >= 0.5) {
		flip(".1");
	}
	if (Math.random() >= 0.5) {
		flip(".2");
	}
	if (Math.random() >= 0.5) {
		flip(".3");
	}
	if (Math.random() >= 0.5) {
		flip(".4");
	}
	if (Math.random() >= 0.5) {
		flip(".5");
	}
	if (Math.random() >= 0.5) {
		flip(".6");
	}
	if (Math.random() >= 0.5) {
		flip(".7");
	}
	if (Math.random() >= 0.5) {
		flip(".8");
	}
	if (Math.random() >= 0.5) {
		flip(".9");
	}
	//Game control
	$("td").click(function() {
		switch ($(this).attr("class")) {
			case "1":
				flip(".1");
				flip(".2");
				flip(".4");
				break;
			case "2":
				flip(".1");
				flip(".2");
				flip(".3");
				flip(".5");
				break;
			case "3":
				flip(".2");
				flip(".3");
				flip(".6");
				break;
			case "4":
				flip(".1");
				flip(".4");
				flip(".5");
				flip(".7");
				break;
			case "5":
				flip(".2");
				flip(".4");
				flip(".5");
				flip(".6");
				flip(".8");
				break;
			case "6":
				flip(".3");
				flip(".5");
				flip(".6");
				flip(".9");
				break;
			case "7":
				flip(".4");
				flip(".7");
				flip(".8");
				break;
			case "8":
				flip(".5");
				flip(".7");
				flip(".8");
				flip(".9");
				break;
			case "9":
				flip(".6");
				flip(".8");
				flip(".9");
				break;
		}
	});
	setTimeout(function() {
		$(".score, #wrapper, .share, table").fadeIn("slow");
		window.scoring = setInterval(function() {
			score += 1;
			$(".score-current").empty();
			$(".score-current").append(score);
		}, 1000);
	}, 2000);
	//Rickroll
	//$("table").click(function(){window.open("https://youtu.be/dQw4w9WgXcQ","_blank");});
});
//Self-explanatory light switching
function flip(light) {
	if ($(light).css("background-color") === "rgb(255, 255, 255)") {
		$(light).css("background-color", "black");
	} else {
		$(light).css("background-color", "white");
	}
	if (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
		setTimeout(function() {
			if (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
				if (score < score_best) {
					score_best = score;
					Cookies.set("score_best", score_best.toString(10), {
						expires: 365
					});
				}
				clearInterval(scoring);
				setTimeout(function() {
					if (Math.random() >= 0.5) {
						flip(".1");
					}
					if (Math.random() >= 0.5) {
						flip(".2");
					}
					if (Math.random() >= 0.5) {
						flip(".3");
					}
					if (Math.random() >= 0.5) {
						flip(".4");
					}
					if (Math.random() >= 0.5) {
						flip(".5");
					}
					if (Math.random() >= 0.5) {
						flip(".6");
					}
					if (Math.random() >= 0.5) {
						flip(".7");
					}
					if (Math.random() >= 0.5) {
						flip(".8");
					}
					if (Math.random() >= 0.5) {
						flip(".9");
					}
				}, 1000);
				//Hide table and best score
				$("table, .score-best").fadeOut("slow");
				setTimeout(function() {
					$(".replay, .share").fadeIn("slow");
				}, 500);
				$(".replay").click(function() {
					if (score < score_best) {
						$(".score-best").empty();
						$(".score-best").append(score);
					}
					if (Cookies.get("score_best")) {
						score_best = parseInt(Cookies.get("score_best"), 10);
						$(".score-best").empty();
						$(".score-best").append(score_best);
					}
					setTimeout(function() {
						score = 0;
						$(".score-current").empty();
						$(".score-current").append(score);
					}, 10);
					setTimeout(function() {
						clearInterval(scoring);
						window.scoring = setInterval(function() {
							score += 1;
							$(".score-current").empty();
							$(".score-current").append(score);
						}, 1000);
					}, 500);
					$(".replay").fadeOut("slow");
					while (($(".1").css("background-color") + $(".2").css("background-color") + $(".3").css("background-color") + $(".4").css("background-color") + $(".5").css("background-color") + $(".6").css("background-color") + $(".7").css("background-color") + $(".8").css("background-color") + $(".9").css("background-color")) === "rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)rgb(0, 0, 0)") {
						//Randomize position
						if (Math.random() >= 0.5) {
							flip(".1");
						}
						if (Math.random() >= 0.5) {
							flip(".2");
						}
						if (Math.random() >= 0.5) {
							flip(".3");
						}
						if (Math.random() >= 0.5) {
							flip(".4");
						}
						if (Math.random() >= 0.5) {
							flip(".5");
						}
						if (Math.random() >= 0.5) {
							flip(".6");
						}
						if (Math.random() >= 0.5) {
							flip(".7");
						}
						if (Math.random() >= 0.5) {
							flip(".8");
						}
						if (Math.random() >= 0.5) {
							flip(".9");
						}
					}
					setTimeout(function() {
						//Fade-in table and best score
						$("table, .score-best").fadeIn("slow");
					}, 500);
				});
			}
		}, 10);
	}
}*/
<|endoftext|>


<commit_before>// Abstracts the idea of a "dirty" (changed but not saved) and "clean" (saved) file
// documentation: http://bililite.com/blog/2014/01/16/new-jquery-plugin-savemonitor/
// Version 1.0
//  depends: jQuery, Promise

// Copyright (c) 2014 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

(function($){

var states = 'clean failed dirty pending ';

$.savemonitor = function(indicator){
	$indicator = $(indicator);
	var savemonitor = new Savemonitor();
	$(savemonitor).on(states, function(event){
		$indicator.removeClass(states).addClass(event.type);
	});
	savemonitor.trigger();
	return savemonitor;
};

$.fn.savemonitor = function(indicator){
	var savemonitor = this.data('savemonitor');
	if (!savemonitor){
		savemonitor = $.savemonitor(indicator);
		this.data('savemonitor', savemonitor);
		this.on('input', savemonitor.setter('dirty'));
	}
	return savemonitor;
};

function Savemonitor(){
	var self = this;
	var state = 'clean'; // make this private!
	self.setter = function(newstate){ // note that this does not set the state, it returns a function to set the state.
		return function(){
			if (states.indexOf(newstate+' ') == -1) return;
			// only flag changes in state
			if (state == newstate) return;
			// if a change happened
			// from the time we started saving to the time that the save completed,  the save is out of date; don't accept a clean or fail
			// So if we are dirty now, we ignore all changes except for the 'start saving' one
			if (state == 'dirty' && newstate != 'pending') return;
			// if it's clean, don't bother signaling another save
			if (state == 'clean' && newstate != 'dirty') return;
			state = newstate;
			self.trigger();
		};
	};
	self.state = function(){
		return state;
	}
	self.trigger = function(){
		$(self).triggerHandler(state); // $().trigger also calls methods with the same name, so we end up in an infinite loop
	}
}

Savemonitor.prototype = {
	clean: function (resolver){
		this.setter('pending')();
		Promise.cast(resolver).then(this.setter('clean'), this.setter('failed'));
	},

	dirty: function (){
		this.setter('dirty');
	},

	on: function(){
		$.fn.on.apply($(this), arguments);
	},

	off: function(){
		$.fn.off.apply($(this), arguments);
	}
};

})(jQuery);<commit_msg>bugfix<commit_after>// Abstracts the idea of a "dirty" (changed but not saved) and "clean" (saved) file
// documentation: http://bililite.com/blog/2014/01/16/new-jquery-plugin-savemonitor/
// Version 1.0
//  depends: jQuery, Promise

// Copyright (c) 2014 Daniel Wachsstock
// MIT license:
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

(function($){

var states = 'clean failed dirty pending ';

$.savemonitor = function(indicator){
	$indicator = $(indicator);
	var savemonitor = new Savemonitor();
	$(savemonitor).on(states, function(event){
		$indicator.removeClass(states).addClass(event.type);
	});
	savemonitor.trigger();
	return savemonitor;
};

$.fn.savemonitor = function(indicator){
	var savemonitor = this.data('savemonitor');
	if (!savemonitor){
		savemonitor = $.savemonitor(indicator);
		this.data('savemonitor', savemonitor);
		this.on('input', savemonitor.setter('dirty'));
	}
	return savemonitor;
};

function Savemonitor(){
	var self = this;
	var state = 'clean'; // make this private!
	self.setter = function(newstate){ // note that this does not set the state, it returns a function to set the state.
		return function(){
			if (states.indexOf(newstate+' ') == -1) return;
			// only flag changes in state
			if (state == newstate) return;
			// if a change happened
			// from the time we started saving to the time that the save completed,  the save is out of date; don't accept a clean or fail
			// So if we are dirty now, we ignore all changes except for the 'start saving' one
			if (state == 'dirty' && newstate != 'pending') return;
			// if it's clean, don't bother signaling another save
			if (state == 'clean' && newstate != 'dirty') return;
			state = newstate;
			self.trigger();
		};
	};
	self.state = function(){
		return state;
	}
	self.trigger = function(){
		$(self).triggerHandler(state); // $().trigger also calls methods with the same name, so we end up in an infinite loop
	}
}

Savemonitor.prototype = {
	clean: function (resolver){
		this.setter('pending')();
		Promise.cast(resolver).then(this.setter('clean'), this.setter('failed'));
	},

	dirty: function (){
		this.setter('dirty')();
	},

	on: function(){
		$.fn.on.apply($(this), arguments);
	},

	off: function(){
		$.fn.off.apply($(this), arguments);
	}
};

})(jQuery);<|endoftext|>


<commit_before>/*******************************************************************************
 * jQuery.splitbutton plugin.
 *
 * Combine two ui-buttons and one ui-menu.
 *
 * @see https://github.com/mar10/jquery-splitbutton
 *
 * Copyright (c) 2013, Martin Wendt (http://wwWendt.de). Licensed MIT.
 */

(function ($) {
    "use strict";
    function getMenuFromEvent(event){
        var menu = $(event.target).closest(":ui-menu"),
            $menu = $(menu);
        return $menu.data("ui-menu") || $menu.data("menu");
    }
    $.widget("ui.splitbutton", {
        options: {
            menu: null,      // selector
            mode: "split",   // 'split' | 'list'
            // Events:
            blur: $.noop,    // dropdown-menu option lost focus
            click: $.noop,   // default-button clicked
            close: $.noop,   // dropdown-menu closed
            focus: $.noop,   // dropdown-menu option focused
            init: $.noop,    // control was initialized
            open: $.noop,    // dropdown-menu opened
            select: $.noop   // dropdown-menu option selected
        },
        _create: function () {
            var self = this;
            // `this.element` is the default-button
            if(this.options.mode === "list"){
                this.element.button({
                    icons: {
                        secondary: "ui-icon-triangle-1-s"
                    }
                }).click($.proxy(this._openMenu, this));
            }else{
                // Append a dropdown-button and wrap both in a new div tag
                // that will be converted into a buttonset
                this.element.wrap($("<span>"));

                this.element.after(
                    $("<button>...</button>").button({
                        text: false,
                        icons: {
                            primary: "ui-icon-triangle-1-s"
                        }
                    }).click($.proxy(this._openMenu, this))
                );
                // Convert both buttons into a buttonset
                this.element.parent().buttonset();
            }
            this.element.click(function(event){
                self._trigger("click", event);
            });
            // Create - but hide - dropdown-menu
            $(this.options.menu)
                .hide()
                .addClass("ui-splitbutton-menu")
                // Create a menu instance
                // Note: the menu may be re-used by other splitbutton instances,
                // so we attach the splitbutton widget in _openMenu()
                .menu({
                    blur: function(event, ui){
                    	// Get splitbutton widget
                    	var widget = getMenuFromEvent(event).controller;

                    	if(widget !== null) {
                    		widget._trigger("blur", event, ui);
                    	} else {
                    		// Menu was closed and splitbutton widget detached
                    	}
                    },
                    focus: function(event, ui){
                        getMenuFromEvent(event).controller._trigger("focus", event, ui);
                    },
                    select: function(event, ui){
//                        var menuId = ui.item.find(">a").attr("href");
                        var widget = getMenuFromEvent(event).controller;

                        // If we have a sub menu, we don't trigger select
                        if($(ui.item[0].lastElementChild).is("ul.ui-menu")) {
                        	return false;
                        }

                        if( widget._trigger("select", event, ui) !== false ){
                            widget._closeMenu.call(widget);
                        }
                    }
                });
            // Register global event handlers that close the dropdown-menu
            $(document).bind("keydown.splitbutton", function(event){
                if( event.which === $.ui.keyCode.ESCAPE ){
                    self._closeMenu();
                }
            }).bind("mousedown.splitbutton", function(event){
                // Close menu when clicked outside menu
                if( $(event.target).closest(".ui-menu-item").length === 0 ){
                    self._closeMenu();
                }
            });
            this._trigger("init");
        },
        /** Return menu widget instance (works on pre and post jQueryUI 1.9). */
        _getMenuWidget: function(){
            var $menu = $(this.options.menu);
            return $menu.data("ui-menu") || $menu.data("menu");
        },
        /** Close dropdown. */
        _closeMenu: function(){
            $(this.options.menu).fadeOut(function() {
            	// Resetting position
            	$(this).css({
            		top: 0,
            		left: 0
                });
            });

            // Trigger blur before splitbutton widget gets detached
            this._trigger("blur");

            this._trigger("close");

            // TODO: setting ui-state-active doesn't work, because .button()
            // plugin removes it on mouseout
//            if(this.options.mode === "list"){
//                this.element.removeClass("ui-state-active");
//            }else{
//                $(this.element).next().removeClass("ui-state-active");
//            }
            // Detach splitbutton widget from menu widget
            this._getMenuWidget().controller = null;
        },
        /** Open dropdown. */
        _openMenu: function(event){
        	// If menu is visible, we don't need to open it again
        	if ($(this.options.menu).is(":visible")) {
        		// TODO: Doesn't work quite right for multiple splitbuttons
        		return;
        	}
            // Click handler for dropdown-button: open dropdown-menu
            if( this._trigger("open", event) === false) {
                return;
            }
            // Attach splitbutton widget to the menu widget.
            this._getMenuWidget().controller = this;

            // TODO: setting ui-state-active doesn't work, because .button()
            // plugin removes it on mouseout
//            if(this.options.mode === "list"){
//                this.element.addClass("ui-state-active");
//            }else{
//                $(this.element).next().addClass("ui-state-active");
//            }
            // Calculate minWidth
            var menuPadding = $(this.options.menu).outerWidth() - $(this.options.menu).width(),
            	minWidth = this.element.outerWidth(true) - menuPadding;

            $(this.options.menu)
                .css({
                    position: "absolute",
                    minWidth: minWidth
                })
                .position({
                    my: "left top",
                    at: "left bottom",
                    of: this.element,
                    collision: "fit"
                })
                .slideDown("fast");
        },
        /**
         * Handle $().splitbutton("option", ...) calls.
         */
        _setOption: function(key, value){
            $.Widget.prototype._setOption.apply(this, arguments);
            // TODO: redraw, if required
        }
    });
} (jQuery));
<commit_msg>Focus first menu item on open<commit_after>/*******************************************************************************
 * jQuery.splitbutton plugin.
 *
 * Combine two ui-buttons and one ui-menu.
 *
 * @see https://github.com/mar10/jquery-splitbutton
 *
 * Copyright (c) 2013, Martin Wendt (http://wwWendt.de). Licensed MIT.
 */

(function ($) {
    "use strict";
    function getMenuFromEvent(event){
        var menu = $(event.target).closest(":ui-menu"),
            $menu = $(menu);
        return $menu.data("ui-menu") || $menu.data("menu");
    }
    $.widget("ui.splitbutton", {
        options: {
            menu: null,      // selector
            mode: "split",   // 'split' | 'list'
            // Events:
            blur: $.noop,    // dropdown-menu option lost focus
            click: $.noop,   // default-button clicked
            close: $.noop,   // dropdown-menu closed
            focus: $.noop,   // dropdown-menu option focused
            init: $.noop,    // control was initialized
            open: $.noop,    // dropdown-menu opened
            select: $.noop   // dropdown-menu option selected
        },
        _create: function () {
            var self = this;
            // `this.element` is the default-button
            if(this.options.mode === "list"){
                this.element.button({
                    icons: {
                        secondary: "ui-icon-triangle-1-s"
                    }
                }).click($.proxy(this._openMenu, this));
            }else{
                // Append a dropdown-button and wrap both in a new div tag
                // that will be converted into a buttonset
                this.element.wrap($("<span>"));

                this.element.after(
                    // Note: button must not be empty, otherwise it is too small
                    $("<button>...</button>").button({
                        text: false,
                        "class": "ui-splitbutton-trigger",
                        icons: {
                            primary: "ui-icon-triangle-1-s"
                        }
                    }).click($.proxy(this._openMenu, this))
                );
                // Convert both buttons into a buttonset
                this.element.parent().buttonset();
            }
            this.element.click(function(event){
                self._trigger("click", event);
            });
            // Create - but hide - dropdown-menu
            $(this.options.menu)
                .hide()
                .addClass("ui-splitbutton-menu")
                // Create a menu instance
                // Note: the menu may be re-used by other splitbutton instances,
                // so we attach the splitbutton widget in _openMenu()
                .menu({
                    blur: function(event, ui){
                    	// Get splitbutton widget
                    	var widget = getMenuFromEvent(event).controller;

                    	if(widget !== null) {
                    		widget._trigger("blur", event, ui);
                    	} else {
                    		// Menu was closed and splitbutton widget detached
                    	}
                    },
                    focus: function(event, ui){
                        getMenuFromEvent(event).controller._trigger("focus", event, ui);
                    },
                    select: function(event, ui){
//                        var menuId = ui.item.find(">a").attr("href");
                        var widget = getMenuFromEvent(event).controller;

                        // If we have a sub menu, we don't trigger select
                        if($(ui.item[0].lastElementChild).is("ul.ui-menu")) {
                        	return false;
                        }

                        if( widget._trigger("select", event, ui) !== false ){
                            widget._closeMenu.call(widget);
                        }
                    }
                });
            // Register global event handlers that close the dropdown-menu
            $(document).bind("keydown.splitbutton", function(event){
                if( event.which === $.ui.keyCode.ESCAPE ){
                    self._getMenuWidget().controller.element.focus();
                    self._closeMenu();
                }
            }).bind("mousedown.splitbutton", function(event){
                // Close menu when clicked outside menu
                if( $(event.target).closest(".ui-menu-item").length === 0 ){
                    self._closeMenu();
                }
            });
            this._trigger("init");
        },
        /** Return menu widget instance (works on pre and post jQueryUI 1.9). */
        _getMenuWidget: function(){
            var $menu = $(this.options.menu);
            return $menu.data("ui-menu") || $menu.data("menu");
        },
        /** Close dropdown. */
        _closeMenu: function(){
            $(this.options.menu).fadeOut(function() {
            	// Resetting position
            	$(this).css({
            		top: 0,
            		left: 0
                });
            });

            // Trigger blur before splitbutton widget gets detached
            this._trigger("blur");

            this._trigger("close");

            // TODO: setting ui-state-active doesn't work, because .button()
            // plugin removes it on mouseout
//            if(this.options.mode === "list"){
//                this.element.removeClass("ui-state-active");
//            }else{
//                $(this.element).next().removeClass("ui-state-active");
//            }
            // Detach splitbutton widget from menu widget
            this._getMenuWidget().controller = null;
        },
        /** Open dropdown. */
        _openMenu: function(event){
        	// If menu is visible, we don't need to open it again
        	if ($(this.options.menu).is(":visible")) {
        		// TODO: Doesn't work quite right for multiple splitbuttons
        		return;
        	}
            // Click handler for dropdown-button: open dropdown-menu
            if( this._trigger("open", event) === false) {
                return;
            }
            // Attach splitbutton widget to the menu widget.
            this._getMenuWidget().controller = this;

            // TODO: setting ui-state-active doesn't work, because .button()
            // plugin removes it on mouseout
//            if(this.options.mode === "list"){
//                this.element.addClass("ui-state-active");
//            }else{
//                $(this.element).next().addClass("ui-state-active");
//            }
            // Calculate minWidth
            var menuPadding = $(this.options.menu).outerWidth() - $(this.options.menu).width(),
            	minWidth = this.element.outerWidth(true) - menuPadding;

            $(this.options.menu)
                .css({
                    position: "absolute",
                    minWidth: minWidth
                })
                .position({
                    my: "left top",
                    at: "left bottom",
                    of: this.element,
                    collision: "fit"
                })
                .slideDown("fast", function(){
                    // Set keyboard focus to first item
                    var $menu = $(this),
                        $first = $menu.find(".ui-menu-item:first");
                    $menu.menu("focus", null, $first);
                    $menu.focus();
                });
        },
        /**
         * Handle $().splitbutton("option", ...) calls.
         */
        _setOption: function(key, value){
            $.Widget.prototype._setOption.apply(this, arguments);
            // TODO: redraw, if required
        }
    });
} (jQuery));
<|endoftext|>


<commit_before>/*!
* StillAlive v1.0
* http://github.com/sidewaysmilk/still-alive
*
* Copyright 2011, Justin Force
* Licensed under the 3-clause BSD License
*/

/*jslint browser: true, indent: 2 */
/*global jQuery */

(function ($) {

  'use strict';

  // default values for optional arguments
  var DEFAULT_WAKEEVENTS = 'mousemove mousedown mouseup keydown keyup',
    DEFAULT_INTERVAL     = 60000,
    DEFAULT_IMMEDIATELY  = true;

  // sugar. Just get the current time in milliseconds
  function getTime() {
    return (new Date()).getTime();
  }

  $.stillAlive = function (callback, interval, immediately, wakeEvents) {

    var args,               // object containing optional arguments
      awake = true,         // awake status. Are we awake?
      lastSeen = getTime(); // the last time the user was seen (a wake event triggered)

    // For named arguments, copy the arguments object and assign its supported
    // properties.
    if (typeof interval === 'object') {
      args = interval;
      interval = args.interval;
      immediately = args.immediately;
      wakeEvents = args.wakeEvents;
    }

    // set default values for optional arguments
    if (interval === undefined) {
      interval = DEFAULT_INTERVAL;
    }
    if (immediately === undefined) {
      immediately = DEFAULT_IMMEDIATELY;
    }
    if (wakeEvents === undefined) {
      wakeEvents = DEFAULT_WAKEEVENTS;
    }

    // true if it's been (1.5 x interval) milliseconds
    function timeToSleep() {
      return (getTime() - lastSeen > (interval + (interval / 2)));
    }

    // set status to awake and update the lastSeen time. If we're waking and
    // we're supposed to run immediately, execute the callback.
    function wake() {
      if (!awake && immediately) {
        callback();
      }
      awake = true;
      lastSeen = getTime();
    }

    // set status to !awake (asleep)
    function sleep() {
      awake = false;
    }

    $(window).bind(wakeEvents, wake);

    // if enough time has passed without a wake event, sleep. If we happen to
    // be awake, execute the callback.
    setInterval(function () {
      if (timeToSleep()) {
        sleep();
      }
      if (awake) {
        callback();
      }
    }, interval);

    // if we're supposed to run immediately, execute the callback once
    if (immediately) {
      callback();
    }
  };

}(jQuery));

<commit_msg>Fix typo in comment<commit_after>/*!
* StillAlive v1.0
* http://github.com/sidewaysmilk/still-alive
*
* Copyright 2011, Justin Force
* Licensed under the BSD 3-Clause License
*/

/*jslint browser: true, indent: 2 */
/*global jQuery */

(function ($) {

  'use strict';

  // default values for optional arguments
  var DEFAULT_WAKEEVENTS = 'mousemove mousedown mouseup keydown keyup',
    DEFAULT_INTERVAL     = 60000,
    DEFAULT_IMMEDIATELY  = true;

  // sugar. Just get the current time in milliseconds
  function getTime() {
    return (new Date()).getTime();
  }

  $.stillAlive = function (callback, interval, immediately, wakeEvents) {

    var args,               // object containing optional arguments
      awake = true,         // awake status. Are we awake?
      lastSeen = getTime(); // the last time the user was seen (a wake event triggered)

    // For named arguments, copy the arguments object and assign its supported
    // properties.
    if (typeof interval === 'object') {
      args = interval;
      interval = args.interval;
      immediately = args.immediately;
      wakeEvents = args.wakeEvents;
    }

    // set default values for optional arguments
    if (interval === undefined) {
      interval = DEFAULT_INTERVAL;
    }
    if (immediately === undefined) {
      immediately = DEFAULT_IMMEDIATELY;
    }
    if (wakeEvents === undefined) {
      wakeEvents = DEFAULT_WAKEEVENTS;
    }

    // true if it's been (1.5 x interval) milliseconds
    function timeToSleep() {
      return (getTime() - lastSeen > (interval + (interval / 2)));
    }

    // set status to awake and update the lastSeen time. If we're waking and
    // we're supposed to run immediately, execute the callback.
    function wake() {
      if (!awake && immediately) {
        callback();
      }
      awake = true;
      lastSeen = getTime();
    }

    // set status to !awake (asleep)
    function sleep() {
      awake = false;
    }

    $(window).bind(wakeEvents, wake);

    // if enough time has passed without a wake event, sleep. If we happen to
    // be awake, execute the callback.
    setInterval(function () {
      if (timeToSleep()) {
        sleep();
      }
      if (awake) {
        callback();
      }
    }, interval);

    // if we're supposed to run immediately, execute the callback once
    if (immediately) {
      callback();
    }
  };

}(jQuery));

<|endoftext|>


<commit_before>(function($) {

  function TypeSuggest(el, data, options, callback){
    var defaults = {
      min_characters: 2,
      display_key: undefined,
      search_key: undefined,
      ignored: [
        ' ',
        ',',
        '.'
      ],
      ajax: false,
      url: '',
      param: 'q'
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = data;

    // set callback
    this.callback = typeof callback === "function" && callback || false;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");
          var data = $selectedItem.data();

          self.selectItem(data);

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.options = [];

          if(val.length && val.length >= self.config.min_characters){

            self.startDebounce();

            if(self.config.ajax){
              var url = self.config.url + "?" + self.config.param + "=" + val
              $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                success: function(data, textStatus, xhr){
                  self.options = data[self.config.results_key];

                  self.updateList();
                }
              })
            }else{
              // compare value to provided list
              $.each(self.data, function(i, item){
                var valLower = val.toLowerCase();
                var itemLower;

                if(self.config.search_key){
                  itemLower = item[self.config.search_key].toLowerCase();
                }else{
                  itemLower = item.toLowerCase();
                }

                if(itemLower.search(valLower) > -1){
                  self.options.push(item);
                }
              })

            }

          }

          self.updateList();
        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('click', 'li', function(){
      var data = $(this).data();

      self.selectItem(data);
      self.callback && self.callback(data);
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.updateList = function(){
    var self = this;
    var $list = $('
');

    if(self.options.length){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        var $listItem = $("
").data("option", option);

        if(self.config.display_keys){
          var output = '';

          $.each(self.config.display_keys, function(i, key){
            output += '' + option[key] + ''
          })

          $listItem.html(output);
        }else{
          $listItem.html(option);
        }

        $list.append($listItem);
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting")
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList();

    if(!self.config.display_keys){
      self.$currentField.val(data.option);
    }

    self.callback && self.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, 250)
  }

  $.fn.suggest = function(data, options, callback){
    var suggest = new TypeSuggest(this, data, options, callback);
    return this;
  }

})(jQuery);<commit_msg>add beforeSend function<commit_after>(function($) {

  function TypeSuggest(el, data, options, callback){
    var defaults = {
      min_characters: 2,
      display_key: undefined,
      search_key: undefined,
      ignored: [
        ' ',
        ',',
        '.'
      ],
      ajax: false,
      url: '',
      param: 'q',
      beforeSend: undefined
    };

    this.config = $.extend({}, defaults, options);

    // set parent element
    this.$el = $(el).addClass("typesuggest");

    this.data = data;

    // set callback
    this.callback = typeof callback === "function" && callback || false;

    this.init();
    this.events();

  }

  TypeSuggest.prototype.init = function(){
    var self = this;

    self.$fields = self.$el.find("input").addClass("typesuggest-input").attr('autocomplete', 'off');
    self.$suggest = $('
').appendTo(self.$el);
  }

  TypeSuggest.prototype.events = function(){
    var self = this;

    self.$fields.on('keydown', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      // if up or down arrows, scroll thru list. if enter key, select item
      if(key == 40 || key == 38 || key == 13){

        if(e.keyCode == 40){ // down arrow

          self.$suggest.addClass("selecting");
          self.move(+1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 38){ // up arrow

          self.$suggest.addClass("selecting");
          self.move(-1);

          // prevent caret from moving
          return false;

        }else if(e.keyCode == 13){ // enter

          var $selectedItem = self.$suggest.find(".selected");
          var data = $selectedItem.data();

          self.selectItem(data);

          // prevent caret from moving
          return false;

        }else{ // everything else

          self.$suggest.removeClass("selecting");

        }

      }

    })

    self.$fields.on('keyup', function(e){
      var $field = $(this);
      var key = e.keyCode || e.which;

      self.$currentField = $field;

      // if not up or down arrows or enter key, fill suggestion
      if(key !== 40 && key !== 38 && key !== 13){

        if(!self.debounce){

          // get value
          var val = $field.val();

          self.options = [];

          if(val.length && val.length >= self.config.min_characters){

            self.startDebounce();

            if(self.config.ajax){
              if(self.config.beforeSend){
                val = self.config.beforeSend(val);
              }

              var url = self.config.url + "?" + self.config.param + "=" + val;

              $.ajax({
                url: url,
                type: 'GET',
                dataType: 'json',
                success: function(data, textStatus, xhr){
                  self.options = data[self.config.results_key];

                  self.updateList();
                }
              })
            }else{
              // compare value to provided list
              $.each(self.data, function(i, item){
                var valLower = val.toLowerCase();
                var itemLower;

                if(self.config.search_key){
                  itemLower = item[self.config.search_key].toLowerCase();
                }else{
                  itemLower = item.toLowerCase();
                }

                if(itemLower.search(valLower) > -1){
                  self.options.push(item);
                }
              })

            }

          }

          self.updateList();
        }
      }else{
        // prevent caret from moving
        return false;
      }
    })

    self.$suggest.on('click', 'li', function(){
      var data = $(this).data();

      self.selectItem(data);
      self.callback && self.callback(data);
    })

    self.$suggest.on('mouseover', 'li', function(){
      var $li = $(this);

      self.$suggest.find("li").removeClass("selected");
      $li.addClass("selected");
    })
  }

  TypeSuggest.prototype.move = function(where){
    var self = this;
    var $selectedItem = self.$suggest.find(".selected");
    var firstSelected = self.$suggest.find("li:first").hasClass("selected");
    var lastSelected = self.$suggest.find("li:last").hasClass("selected");

    if($selectedItem.length && where > 0){

      if(lastSelected){
        // select first
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:first").addClass("selected");
      }else{
        // selection exists, go to next
        $selectedItem.removeClass("selected").next("li").addClass("selected")
      }

    }
    else if($selectedItem.length && where < 0){

      if(firstSelected){
        // select last
        $selectedItem.removeClass("selected");
        self.$suggest.find("li:last").addClass("selected");
      }else{
        // selection exists, go to prev
        $selectedItem.removeClass("selected").prev("li").addClass("selected")
      }

    }
    else{

      // select first
      self.$suggest.find("li:first").addClass("selected");

    }
  }

  TypeSuggest.prototype.updateList = function(){
    var self = this;
    var $list = $('
');

    if(self.options.length){
      self.$suggest.addClass("selecting");

      var options = self.options;
      $.each(options, function(i, option){
        var $listItem = $("
").data("option", option);

        if(self.config.display_keys){
          var output = '';

          $.each(self.config.display_keys, function(i, key){
            output += '' + option[key] + ''
          })

          $listItem.html(output);
        }else{
          $listItem.html(option);
        }

        $list.append($listItem);
      })

      self.$suggest.html($list);

    }else{
      self.$suggest.removeClass("selecting")
    }
  }

  TypeSuggest.prototype.selectItem = function(data){
    var self = this;

    self.options = [];
    self.updateList();

    if(!self.config.display_keys){
      self.$currentField.val(data.option);
    }

    self.callback && self.callback(data);
  }

  TypeSuggest.prototype.startDebounce = function(){
    var self = this;

    self.debounce = true;

    setTimeout(function(){
      self.debounce = false;
    }, 250)
  }

  $.fn.suggest = function(data, options, callback){
    var suggest = new TypeSuggest(this, data, options, callback);
    return this;
  }

})(jQuery);<|endoftext|>


<commit_before>// Copyright 2013-2017, University of Colorado Boulder

/**
 * Radio button with a pseudo-Aqua (Mac OS) look. See "options" comment for list of options.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AquaRadioButtonIO = require( 'SUN/AquaRadioButtonIO' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );

  // constants
  var DEFAULT_RADIUS = 7;

  /**
   * @param property
   * @param value the value that corresponds to this button, same type as property
   * @param {Node} node that will be vertically centered to the right of the button
   * @param {Object} [options]
   * @constructor
   */
  function AquaRadioButton( property, value, node, options ) {

    options = _.extend( {
      cursor: 'pointer',
      enabled: true,
      selectedColor: 'rgb( 143, 197, 250 )', // color used to fill the button when it's selected
      deselectedColor: 'white', // color used to fill the button when it's deselected
      centerColor: 'black', // color used to fill the center of teh button when it's selected
      radius: DEFAULT_RADIUS, // radius of the button
      xSpacing: 8, // horizontal space between the button and the node
      stroke: 'black', // color used to stroke the outer edge of the button
      tandem: Tandem.required,
      phetioType: AquaRadioButtonIO,

      // a11y
      tagName: 'input',
      inputType: 'radio',
      parentContainerTagName: 'li',
      labelTagName: 'label',
      prependLabels: true,
      a11yNameAttribute: 'aquaRadioButton'
    }, options );

    var self = this;

    // @private
    this._enabled = options.enabled;

    // selected node creation
    var selectedNode = new Node();
    var innerCircle = new Circle( options.radius / 3, { fill: options.centerColor } );
    var outerCircleSelected = new Circle( options.radius, { fill: options.selectedColor, stroke: options.stroke } );

    // @private
    this.selectedCircleButton = new Node( {
      children: [ outerCircleSelected, innerCircle ]
    } );
    selectedNode.addChild( this.selectedCircleButton );
    selectedNode.addChild( node );
    node.left = outerCircleSelected.right + options.xSpacing;
    node.centerY = outerCircleSelected.centerY;

    // deselected node
    var deselectedNode = new Node();

    // @private
    this.deselectedCircleButton = new Circle( options.radius, {
      fill: options.deselectedColor,
      stroke: options.stroke
    } );
    deselectedNode.addChild( this.deselectedCircleButton );
    deselectedNode.addChild( node );
    node.left = this.deselectedCircleButton.right + options.xSpacing;
    node.centerY = this.deselectedCircleButton.centerY;

    Node.call( this );

    //Add an invisible node to make sure the layout for selected vs deselected is the same
    var background = new Rectangle( selectedNode.bounds.union( deselectedNode.bounds ) );
    selectedNode.pickable = deselectedNode.pickable = false; // the background rectangle suffices

    this.addChild( background );
    this.addChild( selectedNode );
    this.addChild( deselectedNode );

    // sync control with model
    var syncWithModel = function( newValue ) {
      selectedNode.visible = ( newValue === value );
      deselectedNode.visible = !selectedNode.visible;
    };
    property.link( syncWithModel );

    // set property value on fire
    var fire = function() {
      options.tandem.isSuppliedAndEnabled() && self.startEvent( 'user', 'fired', {
        value: property.phetioType.elementType.toStateObject( value )
      } );
      property.set( value );
      options.tandem.isSuppliedAndEnabled() && self.endEvent();
    };
    var buttonListener = new ButtonListener( { fire: fire } );
    this.addInputListener( buttonListener );

    // a11y - input listener so that updates the state of the radio button with keyboard interaction
    var changeListener = this.addAccessibleInputListener( {
      change: function() {
        fire();
      }
    } );

    // a11y - Specify the default value for assistive technology. This attribute is needed in addition to
    // the 'checked' property to mark this element as the default selection since 'checked' may be set before
    // we are finished adding RadioButtons to the containing group, and the browser will remove the boolean
    // 'checked' flag when new buttons are added.
    if ( property.value === value ) {
      this.setAccessibleAttribute( 'checked', 'checked' );
    }

    // a11y - when the property changes, make sure the correct radio button is marked as 'checked' so that this button
    // receives focus on 'tab'
    var accessibleCheckedListener = function( newValue ) {
      self.accessibleChecked = newValue === value;
    };
    property.link( accessibleCheckedListener );

    // @private
    this.disposeRadioButton = function() {
      self.removeInputListener( buttonListener );
      self.removeAccessibleInputListener( changeListener );
      property.unlink( accessibleCheckedListener );
      property.unlink( syncWithModel );
    };

    // a11y - allow consistent a11y naming for radio button types
    this.setAccessibleAttribute( 'name', options.a11yNameAttribute );

    this.mutate( options );
  }

  sun.register( 'AquaRadioButton', AquaRadioButton );

  return inherit( Node, AquaRadioButton, {

    /**
     * Sets whether the circular part of the radio button will be displayed.
     * @param {boolean} circleButtonVisible
     * @public
     */
    setCircleButtonVisible: function( circleButtonVisible ) {
      this.deselectedCircleButton.visible = circleButtonVisible;
      this.selectedCircleButton.visible = circleButtonVisible;
    },

    // @public - Provide dispose() on the prototype for ease of subclassing.
    dispose: function() {
      this.disposeRadioButton();
      Node.prototype.dispose.call( this );
    },

    /**
     * Sets the enabled state
     * @param {boolean} enabled
     * @public
     */
    setEnabled: function( enabled ) {
      this._enabled = enabled;
      this.opacity = enabled ? 1 : 0.3;
      this.pickable = enabled; // NOTE: This is a side-effect. If you set pickable independently, it will be changed when you set enabled.
    },
    set enabled( value ) { this.setEnabled( value ); },

    /**
     * Gets the enabled state
     * @returns {boolean}
     * @public
     */
    getEnabled: function() {
      return this._enabled;
    },
    get enabled() { return this.getEnabled(); }

  }, {

    DEFAULT_RADIUS: DEFAULT_RADIUS
  } );
} );<commit_msg>rename parentContainerTagName --> containerTagName, https://github.com/phetsims/scenery/issues/748<commit_after>// Copyright 2013-2017, University of Colorado Boulder

/**
 * Radio button with a pseudo-Aqua (Mac OS) look. See "options" comment for list of options.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var AquaRadioButtonIO = require( 'SUN/AquaRadioButtonIO' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var sun = require( 'SUN/sun' );
  var Tandem = require( 'TANDEM/Tandem' );

  // constants
  var DEFAULT_RADIUS = 7;

  /**
   * @param property
   * @param value the value that corresponds to this button, same type as property
   * @param {Node} node that will be vertically centered to the right of the button
   * @param {Object} [options]
   * @constructor
   */
  function AquaRadioButton( property, value, node, options ) {

    options = _.extend( {
      cursor: 'pointer',
      enabled: true,
      selectedColor: 'rgb( 143, 197, 250 )', // color used to fill the button when it's selected
      deselectedColor: 'white', // color used to fill the button when it's deselected
      centerColor: 'black', // color used to fill the center of teh button when it's selected
      radius: DEFAULT_RADIUS, // radius of the button
      xSpacing: 8, // horizontal space between the button and the node
      stroke: 'black', // color used to stroke the outer edge of the button
      tandem: Tandem.required,
      phetioType: AquaRadioButtonIO,

      // a11y
      tagName: 'input',
      inputType: 'radio',
      containerTagName: 'li',
      labelTagName: 'label',
      prependLabels: true,
      a11yNameAttribute: 'aquaRadioButton'
    }, options );

    var self = this;

    // @private
    this._enabled = options.enabled;

    // selected node creation
    var selectedNode = new Node();
    var innerCircle = new Circle( options.radius / 3, { fill: options.centerColor } );
    var outerCircleSelected = new Circle( options.radius, { fill: options.selectedColor, stroke: options.stroke } );

    // @private
    this.selectedCircleButton = new Node( {
      children: [ outerCircleSelected, innerCircle ]
    } );
    selectedNode.addChild( this.selectedCircleButton );
    selectedNode.addChild( node );
    node.left = outerCircleSelected.right + options.xSpacing;
    node.centerY = outerCircleSelected.centerY;

    // deselected node
    var deselectedNode = new Node();

    // @private
    this.deselectedCircleButton = new Circle( options.radius, {
      fill: options.deselectedColor,
      stroke: options.stroke
    } );
    deselectedNode.addChild( this.deselectedCircleButton );
    deselectedNode.addChild( node );
    node.left = this.deselectedCircleButton.right + options.xSpacing;
    node.centerY = this.deselectedCircleButton.centerY;

    Node.call( this );

    //Add an invisible node to make sure the layout for selected vs deselected is the same
    var background = new Rectangle( selectedNode.bounds.union( deselectedNode.bounds ) );
    selectedNode.pickable = deselectedNode.pickable = false; // the background rectangle suffices

    this.addChild( background );
    this.addChild( selectedNode );
    this.addChild( deselectedNode );

    // sync control with model
    var syncWithModel = function( newValue ) {
      selectedNode.visible = ( newValue === value );
      deselectedNode.visible = !selectedNode.visible;
    };
    property.link( syncWithModel );

    // set property value on fire
    var fire = function() {
      options.tandem.isSuppliedAndEnabled() && self.startEvent( 'user', 'fired', {
        value: property.phetioType.elementType.toStateObject( value )
      } );
      property.set( value );
      options.tandem.isSuppliedAndEnabled() && self.endEvent();
    };
    var buttonListener = new ButtonListener( { fire: fire } );
    this.addInputListener( buttonListener );

    // a11y - input listener so that updates the state of the radio button with keyboard interaction
    var changeListener = this.addAccessibleInputListener( {
      change: function() {
        fire();
      }
    } );

    // a11y - Specify the default value for assistive technology. This attribute is needed in addition to
    // the 'checked' property to mark this element as the default selection since 'checked' may be set before
    // we are finished adding RadioButtons to the containing group, and the browser will remove the boolean
    // 'checked' flag when new buttons are added.
    if ( property.value === value ) {
      this.setAccessibleAttribute( 'checked', 'checked' );
    }

    // a11y - when the property changes, make sure the correct radio button is marked as 'checked' so that this button
    // receives focus on 'tab'
    var accessibleCheckedListener = function( newValue ) {
      self.accessibleChecked = newValue === value;
    };
    property.link( accessibleCheckedListener );

    // @private
    this.disposeRadioButton = function() {
      self.removeInputListener( buttonListener );
      self.removeAccessibleInputListener( changeListener );
      property.unlink( accessibleCheckedListener );
      property.unlink( syncWithModel );
    };

    // a11y - allow consistent a11y naming for radio button types
    this.setAccessibleAttribute( 'name', options.a11yNameAttribute );

    this.mutate( options );
  }

  sun.register( 'AquaRadioButton', AquaRadioButton );

  return inherit( Node, AquaRadioButton, {

    /**
     * Sets whether the circular part of the radio button will be displayed.
     * @param {boolean} circleButtonVisible
     * @public
     */
    setCircleButtonVisible: function( circleButtonVisible ) {
      this.deselectedCircleButton.visible = circleButtonVisible;
      this.selectedCircleButton.visible = circleButtonVisible;
    },

    // @public - Provide dispose() on the prototype for ease of subclassing.
    dispose: function() {
      this.disposeRadioButton();
      Node.prototype.dispose.call( this );
    },

    /**
     * Sets the enabled state
     * @param {boolean} enabled
     * @public
     */
    setEnabled: function( enabled ) {
      this._enabled = enabled;
      this.opacity = enabled ? 1 : 0.3;
      this.pickable = enabled; // NOTE: This is a side-effect. If you set pickable independently, it will be changed when you set enabled.
    },
    set enabled( value ) { this.setEnabled( value ); },

    /**
     * Gets the enabled state
     * @returns {boolean}
     * @public
     */
    getEnabled: function() {
      return this._enabled;
    },
    get enabled() { return this.getEnabled(); }

  }, {

    DEFAULT_RADIUS: DEFAULT_RADIUS
  } );
} );<|endoftext|>


<commit_before>/* DownloadBuilder.js - v0.1.0 - 2012-08-06
* http://www.gregfranko.com/downloadBuilder.js/
* Copyright (c) 2012 Greg Franko; Licensed MIT */

// Immediately-Invoked Function Expression (IIFE) [Ben Alman Blog Post](http://benalman.com/news/2010/11/immediately-invoked-function-expression/) that calls another IIFE that contains all of the plugin logic.  I use this pattern so that anyone viewing this code does not have to scroll to the bottom of the page to view the local parameters that are passed into the main IIFE.
(function (downloadBuilder) {

    //ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
    "use strict";

    // Calls the second IIFE and locally passes in the global window, and document objects
    downloadBuilder(window, document);

}

// Locally passes in the `window` object, the `document` object, and an `undefined` variable.  The `window` and `document` objects are passed in locally, to improve performance, since javascript first searches for a variable match within the local variables set before searching the global variables set.  All of the global variables are also passed in locally to be minifier friendly. `undefined` can be passed in locally, because it is not a reserved word in JavaScript.
(function (window, document, undefined) {

    // ECMAScript 5 Strict Mode: [John Resig Blog Post](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/)
    "use strict";

    // Function object constructor
    var DownloadBuilder = function(obj) {

        // Sets the library options
        this.options = {

            // Location option (Defaults to an local)
            "location": (obj && obj.location) || "local",

            // Github author name option
            "author": (obj && obj.author) || "",

            // Github repo name option
            "repo": (obj && obj.repo) || "",

            // Github branch option
            "branch": (obj && obj.branch) || ""

        };

        // Cache option (defaults to an hour (in milliseconds))
        if(obj && obj.cache !== undefined) {

            this.options.cache = +obj.cache;

        }

        else {

            this.options.cache = 3600000;

        }

        // Sets up the plugin
        this._create();

    };

    // Adds methods to the downloadBuilder prototype object that are shared between all instances
    DownloadBuilder.prototype = {

        // Library Version Number
        VERSION: "0.1.0",

        //Github Rate Limit URL
        githubRateLimitUrl: "https://api.github.com/rate_limit",

        // String that will be used to hold the file content
        file: "",

        // Number of the current checkbox that is being traversed
        currentCheckbox: 0,

        // Checkbox HTML elements
        checkboxes: [],

        // Callback function that is called when the fileURL creation process is complete
        callback: function() {},

        // _Create
        // -------
        //      Sets up the Library.
        _create: function() {

            // Determines if the current browser supports the HTML5 Filesystem API and sets up some global variables needed for the HTML5 filesystem to work
            this.supportsFilesystem = this.fileSystemSupport();

            // Instance variable (object) that determines if a user is trying to use a Github resource and how many Github resources are being used
            this.github = this._isUsingGithub();

            // If one or more Github resources are trying to be used
            if(this.github.isUsing) {

                // Checks to make sure the user is not over the Github rate limiting capacity and sets an instance property to the number of remaining available Github requests
                this._checkGithubRateLimit();

            }

            // Maintains chainability
            return this;

        },

        // _fileSystemSetup
        // ----------------
        //      Sets up the HTML5 Filesystem global variables and determines if the current browser supports the API
        fileSystemSupport: function() {

            // HTML5 Filesystem API Variables
            window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem || window.mozRequestFileSystem;

            window.URL = window.URL || window.webkitURL || window.mozURL;

            window.storageInfo = window.storageInfo || window.webkitStorageInfo || window.mozStorageInfo;

            // If any of the global HTML5 Filesystem variables are not available
            if(!window.requestFileSystem || !window.Blob || !window.URL || !window.storageInfo) {

                // The HTML5 Filesystem API is not supported
                return false;

            }

            // The HTML5 Filesystem API is supported
            return true;

        },

        // _isUsingGithub
        // --------------
        //      Determines if any of the file downloads are trying to use Github
        _isUsingGithub: function() {

            // Grabs all of the elements on the page with the data-location='github' HTML5 data attribute
            var githubSelector = document.querySelectorAll('[data-location="github"]');

            // If there is one element or more that are trying to use Github resources
            if(githubSelector.length) {

                // Returns an object
                return { "isUsing": true, "length": githubSelector.length };

            }

            // If there are no elements that are trying to use Github resources
            else {

                // Returns an object
                return { "isUsing": false, "length": githubSelector.length };

            }

        },

        // _checkGithubRateLimit
        // ---------------------
        //      Determines if the number of Github HTTP requests has hit the maximum number of requests, within the hour, that Github allows before rate limiting
        _checkGithubRateLimit: function() {

            // Saves the Library object context in the this variable
            var self = this;

            // Calls the custom JSONP method (created by Oscar Goodson)
            this.JSONP(this.githubRateLimitUrl, function(data){

                // Sets an instance variable that determines if the Github rate limit has been reached or not
                self.rateLimit = data.data.rate.remaining;

            });

            // Maintains chainability
            return this;

        },

        // JSONP
        // -----
        //      Method created by Oscar Goodson [Github](https://github.com/OscarGodson/JSONP)
        JSONP: function(url,method,callback) {

            // Set the defaults
            url = url || "";

            method = method || "";

            callback = callback || function(){};

            var jsonpScript,
                generatedFunction;

            // If no method was set and they used the callback param in place of
            // the method param instead, we say method is callback and set a
            // default method of "callback"
            if(typeof method === "function") {

                callback = method;

                method = "callback";

            }

            // This randomizes a function *name* for generated script tag at the bottom to call
            // example: jsonp958653
            generatedFunction = "jsonp" + Math.round(Math.random()*1000001);

            // Generate the temp JSONP function using the name above
            // First, call the function the user defined in the callback param [callback(json)]
            // Then delete the generated function from the window [delete window[generatedFunction]]
            window[generatedFunction] = function(json){

                callback(json);

                // A try/catch block is used because IE8 does not support deleting global window properties
                try {

                    delete window[generatedFunction];

                }

                catch (e) {

                    window[generatedFunction] = undefined;

                }

            };

            // Check if the user set their own params, and if not add a ? to start a list of params
            // If in fact they did we add a & to add onto the params
            // example1: url = http://url.com THEN http://url.com?callback=X
            // example2: url = http://url.com?example=param THEN http://url.com?example=param&callback=X
            if(url.indexOf("?") === -1) {

                url = url+"?";

            }

            else {

                url = url+"&";

            }

            // This generates the <script> tag
            jsonpScript = document.createElement("script");

            jsonpScript.setAttribute("src", url + method + "=" + generatedFunction);

            document.getElementsByTagName("head")[0].appendChild(jsonpScript);
        },

        // buildURL
        // --------
        //      Downloads all of the files listed in the checkbox attributes,
        //      concatenates all files into a single file,
        //      constructs a file url (if supported),
        //      and calls the callback function when everything is complete
        buildURL: function(checkboxes, fileName, lang, callback) {

            // If there is at least one checkbox element
            if(checkboxes.length) {

                // LOCAL VARIABLES
                var self = this,
                    time = new Date().getTime(),
                    currentElem,
                    location,
                    repoAuthor,
                    repoName,
                    repoBranch,
                    localPath,
                    x,
                    lastElem;

                // If the callback parameter is a function
                if(typeof callback === "function") {

                    // Saves the callback function as an object instance property
                    self.callback = callback;

                }

                if(self.currentCheckbox === 0 && window.sessionStorage && !window.sessionStorage.getItem("cache-time")) {

                    window.sessionStorage.setItem("cache-time", time);

                }

                // Saves the checkbox elements as an object instance property
                self.checkboxes = checkboxes;

                // Determines if the currently traversed checkbox element is the last checkbox element in the list
                lastElem = (self.currentCheckbox === checkboxes.length -1) || false;

                // Stores the currently traversed checkbox element in a variable
                currentElem = checkboxes[self.currentCheckbox];

                // Stores the HTML5 data attribute, data-location, inside of a variable (Defaulting to local)
                location = currentElem.getAttribute("data-location") || self.options.location || "";

                // Stores the HTML5 data attribute, data-author, inside of a variable (Defaulting to an empty string)
                repoAuthor = currentElem.getAttribute("data-author") || self.options.author || "";

                // Stores the HTML5 data attribute, data-repo, inside of a variable (Defaulting to an empty string)
                repoName = currentElem.getAttribute("data-repo") || self.options.repo || "";

                // Stores the HTML5 data attribute, data-branch, inside of a variable (Defaulting to an empty string)
                repoBranch = currentElem.getAttribute("data-branch") || self.options.branch || "";

                // Stores the HTML5 data attribute, data-localpath, inside of a variable (Defaulting to an empty string)
                localPath = currentElem.getAttribute("data-localpath") || "";

                // If the current browser supports Session Storage
                if(window.sessionStorage) {

                    // If there are already items stored in the session
                    if(window.sessionStorage.getItem(currentElem.value) && window.sessionStorage.getItem("cache-time")) {

                         // If the caching time has expired
                        if((time - window.sessionStorage.getItem("cache-time")) >= self.options.cache) {

                            // Removes the session data
                            window.sessionStorage.removeItem(currentElem.value);

                            // Removes the session data
                            window.sessionStorage.removeItem("cache-time");

                            // Requests new data
                            self._sendRequest({ "location": location, "repoAuthor": repoAuthor, "repoName": repoName, "repoBranch": repoBranch, "localPath": localPath, "currentElem": currentElem, "time": time, "lastElem": lastElem, "fileName": fileName, "lang": lang });

                        }

                        // If the caching time has not expired
                        else {

                            // Appends to the file instance property with whatever text is already in the session
                            self.file += window.sessionStorage.getItem(currentElem.value);

                            // Checks to the see if the file URL is ready to be created
                            self._fileStatus({ "lastElem": lastElem, "lang": lang, "fileName": fileName });

                        }

                    }

                    // If there are no items in the session
                    else {

                        // Requests new data
                        self._sendRequest({ "location": location, "repoAuthor": repoAuthor, "repoName": repoName, "repoBranch": repoBranch, "localPath": localPath, "currentElem": currentElem, "time": time, "lastElem": lastElem, "fileName": fileName, "lang": lang });

                    }

                }

            }

            else {

                // Calls the callback function that was ininitially passed into the buildURL method
                callback.call(window, { "content": "", "fileName": fileName, "lang": lang });

            }

            // Maintains chainability
            return this;

        },

        // _localRequest
        // -------------
        //      Downloads the text of a local file
        _localRequest: function(obj) {

            // LOCAL VARIABLES
            var self = this,
                xhr,
                text;

            // Ajax Request wrapped in a try/catch block in case of an error
            try {

                // Constructs a new XMLHttpRequest object instance
                xhr = new XMLHttpRequest();

                // Creates a GET request with the relative url that is passed
                xhr.open("GET", obj.url, true);

                // Ajax event handler to check if the request is finished
                xhr.onreadystatechange = function(e) {

                    // If the Ajax request is complete and it is successful
                    if (this.readyState === 4 && this.status === 200) {

                        // Save the text response in a local variable
                        text = this.responseText || this.response || "";

                        // Append the text response to the file instance property
                        self.file += text;

                        // Sets necessary session data
                        window.sessionStorage.setItem(obj.elem.value, text || "");

                        // Checks to the see if the file URL should be created yet
                        self._fileStatus({ "lastElem": obj.lastElem, "lang": obj.lang, "fileName": obj.fileName });

                    }
                };

                // Sends the Ajax request
                xhr.send();

            } catch(error) {

            }

            // Maintains chainability
            return this;

        },

        // _thirdPartyRequest
        // ------------------
        //      Downloads the text of a third-party file (Currently only Github is supported)
        _thirdPartyRequest: function(obj) {

            // Stores the local object context in the self local variable
            var self = this,
                text;

            // Calls the custom JSONP method (created by Oscar Goodson)
            this.JSONP(obj.url, function(data) {

                // If the request is a Github request
                if(obj.type === "github") {

                    // Appends the parsed Github text response to the file instance property
                    text = self._parseGithubResponse({ "elem": obj.elem, "data": data }) || "";

                    self.file += text;

                    // Stores all the necessary data inside of the session
                    window.sessionStorage.setItem(obj.elem.value, text || "");

                    // Checks to the see if the file URL is ready to be created
                    self._fileStatus({ "lastElem": obj.lastElem, "lang": obj.lang, "fileName": obj.fileName });

                }

            });

        },

        // _sendRequest
        // ------------
        //      Determines what type of request (local or third party) is sent
        _sendRequest: function(obj) {

            // If the request is a Github request
            if(obj.location === "github") {

                // If the Github rate limit has not been met
                if(this.rateLimit !== 0) {

                    // Decreases the rate limit capacity amount
                    this.rateLimit -= 1;

                    // Sends a third party file request
                    this._thirdPartyRequest({ "type": obj.location, "url": "https://api.github.com/repos/" + obj.repoAuthor + "/" + obj.repoName + "/contents/?ref=" + obj.repoBranch + "&path=" + obj.currentElem.value, "elem": obj.currentElem, "time": obj.time, "lastElem": obj.lastElem, "lang": obj.lang, "fileName": obj.fileName });

                }

                // If the Github rate limit has been met
                else {

                    // If a backup local path is provided
                    if(obj.localPath) {

                        // Sends a local file request
                        this._localRequest({ "url": obj.localpath, "elem": obj.currentElem, "time": obj.time, "lastElem": obj.lastElem, "lang": obj.lang, "fileName": obj.fileName });

                    }

                }

            }

            // If the request is not a Github request
            else {

                // Sends a local file request
                this._localRequest({ "url": obj.currentElem.value, "elem": obj.currentElem, "time": obj.time, "lastElem": obj.lastElem, "lang": obj.lang, "fileName": obj.fileName });

            }

        },

        // _parseGithubResponse
        // --------------------
        //      Parses the Base 64 encoded Github response into text
        //      This method was heavily inspired by [James Ward](https://github.com/jamesward/github-files)
        _parseGithubResponse: function(obj) {

            // LOCAL VARIABLES
            var self = this,
                base64EncodedContent,
                content,
                contentArray,
                text;

            // If Github found content and the global `atob` method is available (not currently available in IE)
            if (obj.data.data.content !== undefined && window.atob) {

                // If the encoding is base64
                if (obj.data.data.encoding === "base64") {

                    // Stores the content property inside of the local variable
                    base64EncodedContent = obj.data.data.content;

                    // Removes all new lines
                    base64EncodedContent = base64EncodedContent.replace(/\n/g, "");

                    // Stores the base64 encoded content inside of a local variable
                    content = window.atob(base64EncodedContent);

                    // Splits the content up into an array
                    contentArray = content.split("\n");

                    // Grabs all of the content from the array and stores the text in a local variable
                    text = contentArray.slice(0, contentArray.length).join("\n") || "";

                }

            }

            // Maintains chainability
            return text;

        },

        // createURL
        // ---------
        //      Constructs a file url using the HTML5 Filesystem API (Only currently available in Chrome 12+)
        createURL: function(obj) {

            // LOCAL VARIABLES
            var self = this,
                blob;

            // Request temporary storage data (even though we are not creating any files this step is necessary)
            window.storageInfo.requestQuota(window.TEMPORARY, 1024*1024, function(grantedBytes) {

                // The callback function that uses the granted bytes requested in the previous callback
                window.requestFileSystem(self.storageType, grantedBytes, function(fs) {

                    // Creates a file (the name of the file is passed into the createURL method)
                    fs.root.getFile(obj.fileName, { create: true }, function(fileEntry) {

                        // Create a FileWriter object for our FileEntry
                        fileEntry.createWriter(function(fileWriter) {

                            // Create a new Blob
                            blob = new window.Blob([obj.data], { type: "text/" + obj.lang });

                            // Calls the callback function that was passed in, and constructs then passes the url that references the recently created blob
                            obj.callback.call(window, window.URL.createObjectURL(blob));

                        });

                    });

                });

            });

            // Maintains chainability
            return this;

        },

        // _fileStatus
        // -----------
        //      Determines if the file URL is ready to be created
        _fileStatus: function(obj) {

            // LOCAL VARIABLES
            var self = this,
                url;

            // If the last checkbox is being process
            if(obj.lastElem) {

                // Resets the currently traversed checkbox back to zero
                this.currentCheckbox = 0;

                // If the current browser supports the HTML5 Filesystem API
                if(self.supportsFilesystem) {

                    // Creates the file URL
                    self.createURL({ "lang": obj.lang, "fileName": obj.fileName, "data": self.file, "callback": function(url) {

                        // Saves the url inside of the session
                        window.sessionStorage.setItem("bloburl", url);

                        // Calls the callback function that was ininitially passed into the buildURL method
                        self.callback.call(window, { "url": url, "content": self.file, "fileName": obj.fileName, "lang": obj.lang });

                        // Removes the contents of the file instance property (that was used to create the file URL)
                        self.file = "";

                    }});

                }

                // If the current borwser does not support the HTML5 Filesystem API
                else {

                    // Calls the callback function that was ininitially passed into the buildURL method
                    self.callback.call(window, { "content": self.file, "fileName": obj.fileName, "lang": obj.lang });

                    // Removes the contents of the file instance property (that was used to create the file URL)
                    self.file = "";

                }

            }

            // If the last checkbox is not being processed
            else {

                // Increment the currently traversed checkbox number
                this.currentCheckbox += 1;

                // Call the buildURL method again
                self.buildURL(self.checkboxes, obj.fileName, obj.lang, self.callback);

            }

            // Maintains chainability
            return this;

        }

    };

    // Makes the local downloadBuilder object global
    window.DownloadBuilder = DownloadBuilder;

})); // End of Library<commit_msg>Updated Project Page<commit_after>/*! JavaScript Library to Create Custom Builds For Front-End Projects - v0.3.0 - 2012-08-09
* https://github.com/gfranko/DownloadBuilder.js
* Copyright (c) 2012 Greg Franko; Licensed MIT */
(function(a){"use strict",a(window,document)})(function(a,b,c){"use strict";var d=function(a){this.options={location:a&&a.location||"local",author:a&&a.author||"",repo:a&&a.repo||"",branch:a&&a.branch||""},a&&a.cache!==c?this.options.cache=+a.cache:this.options.cache=36e5,this._create()};d.prototype={VERSION:"0.3.0",githubRateLimitUrl:"https://api.github.com/rate_limit",file:"",currentCheckbox:0,checkboxes:[],callback:function(){},_create:function(){return this.supportsFilesystem=this.fileSystemSupport(),this.github=this._isUsingGithub(),this.github.isUsing&&this._checkGithubRateLimit(),this},fileSystemSupport:function(){return a.requestFileSystem=a.requestFileSystem||a.webkitRequestFileSystem||a.mozRequestFileSystem,a.URL=a.URL||a.webkitURL||a.mozURL,a.storageInfo=a.storageInfo||a.webkitStorageInfo||a.mozStorageInfo,!a.requestFileSystem||!a.Blob||!a.URL||!a.storageInfo?!1:!0},_isUsingGithub:function(){var a=b.querySelectorAll('[data-location="github"]');return a.length||this.options.location==="github"?{isUsing:!0,length:a.length}:{isUsing:!1,length:a.length}},_checkGithubRateLimit:function(){var a=this;return this.JSONP(this.githubRateLimitUrl,function(b){a.rateLimit=b.data.rate.remaining}),this},JSONP:function(d,e,f){d=d||"",e=e||"",f=f||function(){};var g,h;typeof e=="function"&&(f=e,e="callback"),h="jsonp"+Math.round(Math.random()*1000001),a[h]=function(b){f(b);try{delete a[h]}catch(d){a[h]=c}},d.indexOf("?")===-1?d=d+"?":d=d+"&",g=b.createElement("script"),g.setAttribute("src",d+e+"="+h),b.getElementsByTagName("head")[0].appendChild(g)},buildURL:function(b,c,d,e){if(b.length){var f=this,g=(new Date).getTime(),h,i,j,k,l,m,n,o;typeof e=="function"&&(f.callback=e),f.currentCheckbox===0&&a.sessionStorage&&!a.sessionStorage.getItem("cache-time")&&a.sessionStorage.setItem("cache-time",g),f.checkboxes=b,o=f.currentCheckbox===b.length-1||!1,h=b[f.currentCheckbox],i=h.getAttribute("data-location")||f.options.location||"",j=h.getAttribute("data-author")||f.options.author||"",k=h.getAttribute("data-repo")||f.options.repo||"",l=h.getAttribute("data-branch")||f.options.branch||"",m=h.getAttribute("data-localpath")||"",a.sessionStorage&&(a.sessionStorage.getItem(h.value)&&a.sessionStorage.getItem("cache-time")?g-a.sessionStorage.getItem("cache-time")>=f.options.cache?(a.sessionStorage.removeItem(h.value),a.sessionStorage.removeItem("cache-time"),f._sendRequest({location:i,repoAuthor:j,repoName:k,repoBranch:l,localPath:m,currentElem:h,time:g,lastElem:o,fileName:c,lang:d})):(f.file+=a.sessionStorage.getItem(h.value),f._fileStatus({lastElem:o,lang:d,fileName:c})):f._sendRequest({location:i,repoAuthor:j,repoName:k,repoBranch:l,localPath:m,currentElem:h,time:g,lastElem:o,fileName:c,lang:d}))}else e.call(a,{content:"",fileName:c,lang:d});return this},_localRequest:function(b){var c=this,d,e;try{d=new XMLHttpRequest,d.open("GET",b.url,!0),d.onreadystatechange=function(d){this.readyState===4&&this.status===200&&(e=this.responseText||this.response||"",c.file+=e,a.sessionStorage.setItem(b.elem.value,e||""),c._fileStatus({lastElem:b.lastElem,lang:b.lang,fileName:b.fileName}))},d.send()}catch(f){}return this},_thirdPartyRequest:function(b){var c=this,d;this.JSONP(b.url,function(e){b.type==="github"&&(d=c._parseGithubResponse({elem:b.elem,data:e})||"",c.file+=d,a.sessionStorage.setItem(b.elem.value,d||""),c._fileStatus({lastElem:b.lastElem,lang:b.lang,fileName:b.fileName}))})},_sendRequest:function(a){a.location==="github"?this.rateLimit!==0?(this.rateLimit-=1,this._thirdPartyRequest({type:a.location,url:"https://api.github.com/repos/"+a.repoAuthor+"/"+a.repoName+"/contents/?ref="+a.repoBranch+"&path="+a.currentElem.value,elem:a.currentElem,time:a.time,lastElem:a.lastElem,lang:a.lang,fileName:a.fileName})):a.localPath&&this._localRequest({url:a.localpath,elem:a.currentElem,time:a.time,lastElem:a.lastElem,lang:a.lang,fileName:a.fileName}):this._localRequest({url:a.currentElem.value,elem:a.currentElem,time:a.time,lastElem:a.lastElem,lang:a.lang,fileName:a.fileName})},_parseGithubResponse:function(b){var d=this,e,f,g,h;return b.data.data.content!==c&&a.atob&&b.data.data.encoding==="base64"&&(e=b.data.data.content,e=e.replace(/\n/g,""),f=a.atob(e),g=f.split("\n"),h=g.slice(0,g.length).join("\n")||""),h},createURL:function(b){var c=this,d;return a.storageInfo.requestQuota(a.TEMPORARY,1048576,function(e){a.requestFileSystem(c.storageType,e,function(c){c.root.getFile(b.fileName,{create:!0},function(c){c.createWriter(function(c){d=new a.Blob([b.data],{type:"text/"+b.lang}),b.callback.call(a,a.URL.createObjectURL(d))})})})}),this},_fileStatus:function(b){var c=this,d;return b.lastElem?(this.currentCheckbox=0,c.supportsFilesystem?c.createURL({lang:b.lang,fileName:b.fileName,data:c.file,callback:function(d){a.sessionStorage.setItem("bloburl",d),c.callback.call(a,{url:d,content:c.file,fileName:b.fileName,lang:b.lang}),c.file=""}}):(c.callback.call(a,{content:c.file,fileName:b.fileName,lang:b.lang}),c.file="")):(this.currentCheckbox+=1,c.buildURL(c.checkboxes,b.fileName,b.lang,c.callback)),this}},a.DownloadBuilder=d});<|endoftext|>