<commit_before>/*
	input_device.js
	Input Device Definitions Script

	Maverick Loneshark
	MAET, 2013 - 2017
*/

"use strict";

(function(MAETGDK) {
	/**
	Object that tracks received input; it is up to individual games to handle tracked input
	**/
	function InputDevice() {
		var thisInputDevice = this,
			keyboard = {
				left: false,
				up: false,
				right: false,
				down: false,
				start: false,
				select: false,
				jump: false,
				special: false
			},
			controller = {
				left: false,
				up: false,
				right: false,
				down: false,
				start: false,
				select: false,
				jump: false,
				special: false
			};

		/* members */
		this.left_button = false;
		this.up_button = false;
		this.right_button = false;
		this.down_button = false;
		this.start_button = false;
		this.select_button = false;
		this.jump_button = false;
		this.special_button = false;
		this.gamepad;

		/* methods */
		function handlePressedInput(event) {
			var keycode,
				keychar;

			if(window.event) {
				keycode = event.keyCode;
			}
			else if(event.which) {
				keycode = event.which;
			}

			switch(keycode) {
				case 37: //left
				case 65: //a
					keyboard.left = true;
				break;

				case 38: //up
				case 87: //w
					keyboard.up = true;
				break;

				case 39: //right
				case 68: //d
					keyboard.right = true;
				break;

				case 40: //down
				case 83: //s
					keyboard.down = true;
				break;

				case 90: //z
				case 74: //j
					keyboard.special = true;
				break;

				case 88: //x
				case 75: //k
				case 32: //[space]
					keyboard.jump = true;
				break;

				case 220: //'\'
					keyboard.select = true;
				break;

				case 13: //enter
					keyboard.start = true;
				break;

				default: //other
				break;
			}

			return;
		}

		function handleReleasedInput(event) {
			var keycode,
				keychar;

			if(window.event) {
				keycode = event.keyCode;
			}
			else if(event.which) {
				keycode = event.which;
			}

			switch(keycode) {
				case 37: //left
				case 65: //a
					keyboard.left = false;
				break;

				case 38: //up
				case 87: //w
					keyboard.up = false;
				break;

				case 39: //right
				case 68: //d
					keyboard.right = false;
				break;

				case 40: //down
				case 83: //s
					keyboard.down = false;
				break;

				case 90: //z
				case 74: //j
					keyboard.special = false;
				break;

				case 88: //x
				case 75: //k
				case 32: //[space]
					keyboard.jump = false;
				break;

				case 220: //'\'
					keyboard.select = false;
				break;

				case 13: //enter
					keyboard.start = false;
				break;

				default: //other
				break;
			}

			return;
		}

		this.fetchConnectedGamepad = function() {
			var gamepads = navigator.getGamepads(),
				i;

			for(i = 0; i < gamepads.length; i++) {
				if(gamepads[i]) {
					this.gamepad = gamepads[i];
					break;
				}
			}

			return;
		}

		MAETGDK.prototype.getInputs = function() {
			this.input_device.fetchConnectedGamepad();

			if(this.input_device.gamepad) {
				switch(this.input_device.gamepad.id) {
					case 'Xbox 360 Controller (XInput STANDARD GAMEPAD)':
					case 'xinput':
						processXInput(this.input_device.gamepad);
					break;

					default:
						processGeneric(this.input_device.gamepad);
					break;
				}
			}

			processInputs();

			return;
		}

		function processXInput(gamepad) {
			const A_BUTTON = 0,
				B_BUTTON = 1,
				X_BUTTON = 2,
				Y_BUTTON = 3,
				LB_BUTTON = 4,
				RB_BUTTON = 5,
				LT_BUTTON = 6,
				RT_BUTTON = 7,
				BACK_BUTTON = 8,
				START_BUTTON = 9,
				LEFT_STICK_IN = 10,
				RIGHT_STICK_IN = 11,
				UP_BUTTON = 12,
				DOWN_BUTTON = 13,
				LEFT_BUTTON = 14,
				RIGHT_BUTTON = 15;

			controller.left = gamepad.buttons[LEFT_BUTTON].pressed;
			controller.up = gamepad.buttons[UP_BUTTON].pressed;
			controller.right = gamepad.buttons[RIGHT_BUTTON].pressed;
			controller.down = gamepad.buttons[DOWN_BUTTON].pressed;
			controller.special = gamepad.buttons[X_BUTTON].pressed;
			controller.jump = gamepad.buttons[A_BUTTON].pressed;
			controller.start = gamepad.buttons[START_BUTTON].pressed;
			controller.select = gamepad.buttons[BACK_BUTTON].pressed;

			return;
		}

		function processGeneric(gamepad) {
			var i;

			for(i = 0; i < gamepad.buttons.length; i++) {
				if(gamepad.buttons[i].pressed) {
					console.debug("Button " + i + " was pressed! Woo-hoo!");
				}
			}

			return;
		}

		function processInputs() {
			thisInputDevice.left_button = controller.left || keyboard.left;
			thisInputDevice.up_button = controller.up || keyboard.up;
			thisInputDevice.right_button = controller.right || keyboard.right;
			thisInputDevice.down_button = controller.down || keyboard.down;
			thisInputDevice.special_button = controller.special || keyboard.special;
			thisInputDevice.jump_button = controller.jump || keyboard.jump;
			thisInputDevice.start_button = controller.start || keyboard.start;
			thisInputDevice.select_button = controller.select || keyboard.select;

			return;
		}

		document.body.onkeydown = handlePressedInput;
		document.body.onkeyup = handleReleasedInput;

		/*
		window.addEventListener("gamepadconnected", function(event) {
			thisInputDevice.gamepad = navigator.getGamepads()[event.gamepad.index];
		});
		*/

		return;
	}

	MAETGDK.prototype.InputDevice = InputDevice;
	MAET_GDK.input_device = new MAET_GDK.InputDevice();

	return;
})(MAETGDK);
<commit_msg>more input_device.js refactoring<commit_after>/*
	input_device.js
	Input Device Definitions Script

	Maverick Loneshark
	MAET, 2013 - 2017
*/

"use strict";

(function(MAETGDK) {
	//Object that tracks received input; it is up to individual games to handle tracked input
	function InputDevice() {
		var thisInputDevice = this,
			keyboard = {
				left: false,
				up: false,
				right: false,
				down: false,
				start: false,
				select: false,
				jump: false,
				special: false
			},
			controller = {
				left: false,
				up: false,
				right: false,
				down: false,
				start: false,
				select: false,
				jump: false,
				special: false
			};

		/* members */
		this.left_button = false;
		this.up_button = false;
		this.right_button = false;
		this.down_button = false;
		this.start_button = false;
		this.select_button = false;
		this.jump_button = false;
		this.special_button = false;
		this.gamepad;

		/* methods */
		function handlePressedInput(event) {
			var keycode,
				keychar;

			if(window.event) {
				keycode = event.keyCode;
			}
			else if(event.which) {
				keycode = event.which;
			}

			switch(keycode) {
				case 37: //left
				case 65: //a
					keyboard.left = true;
				break;

				case 38: //up
				case 87: //w
					keyboard.up = true;
				break;

				case 39: //right
				case 68: //d
					keyboard.right = true;
				break;

				case 40: //down
				case 83: //s
					keyboard.down = true;
				break;

				case 90: //z
				case 74: //j
					keyboard.special = true;
				break;

				case 88: //x
				case 75: //k
				case 32: //[space]
					keyboard.jump = true;
				break;

				case 220: //'\'
					keyboard.select = true;
				break;

				case 13: //enter
					keyboard.start = true;
				break;

				default: //other
				break;
			}

			return;
		}

		function handleReleasedInput(event) {
			var keycode,
				keychar;

			if(window.event) {
				keycode = event.keyCode;
			}
			else if(event.which) {
				keycode = event.which;
			}

			switch(keycode) {
				case 37: //left
				case 65: //a
					keyboard.left = false;
				break;

				case 38: //up
				case 87: //w
					keyboard.up = false;
				break;

				case 39: //right
				case 68: //d
					keyboard.right = false;
				break;

				case 40: //down
				case 83: //s
					keyboard.down = false;
				break;

				case 90: //z
				case 74: //j
					keyboard.special = false;
				break;

				case 88: //x
				case 75: //k
				case 32: //[space]
					keyboard.jump = false;
				break;

				case 220: //'\'
					keyboard.select = false;
				break;

				case 13: //enter
					keyboard.start = false;
				break;

				default: //other
				break;
			}

			return;
		}

		function fetchConnectedGamepad() {
			var gamepads = navigator.getGamepads(),
				i;

			for(i = 0; i < gamepads.length; i++) {
				if(gamepads[i]) {
					thisInputDevice.gamepad = gamepads[i];
					break;
				}
			}

			return;
		}

		MAETGDK.prototype.getInputs = function() {
			fetchConnectedGamepad();

			if(thisInputDevice.gamepad) {
				switch(thisInputDevice.gamepad.id) {
					case 'Xbox 360 Controller (XInput STANDARD GAMEPAD)':
					case 'xinput':
						processXInput(thisInputDevice.gamepad);
					break;

					default:
						processGeneric(thisInputDevice.gamepad);
					break;
				}
			}

			processInputs();

			return;
		}

		function processXInput(gamepad) {
			const A_BUTTON = 0,
				B_BUTTON = 1,
				X_BUTTON = 2,
				Y_BUTTON = 3,
				LB_BUTTON = 4,
				RB_BUTTON = 5,
				LT_BUTTON = 6,
				RT_BUTTON = 7,
				BACK_BUTTON = 8,
				START_BUTTON = 9,
				LEFT_STICK_IN = 10,
				RIGHT_STICK_IN = 11,
				UP_BUTTON = 12,
				DOWN_BUTTON = 13,
				LEFT_BUTTON = 14,
				RIGHT_BUTTON = 15;

			controller.left = gamepad.buttons[LEFT_BUTTON].pressed;
			controller.up = gamepad.buttons[UP_BUTTON].pressed;
			controller.right = gamepad.buttons[RIGHT_BUTTON].pressed;
			controller.down = gamepad.buttons[DOWN_BUTTON].pressed;
			controller.special = gamepad.buttons[X_BUTTON].pressed;
			controller.jump = gamepad.buttons[A_BUTTON].pressed;
			controller.start = gamepad.buttons[START_BUTTON].pressed;
			controller.select = gamepad.buttons[BACK_BUTTON].pressed;

			return;
		}

		function processGeneric(gamepad) {
			var i;

			for(i = 0; i < gamepad.buttons.length; i++) {
				if(gamepad.buttons[i].pressed) {
					console.debug("Button " + i + " was pressed! Woo-hoo!");
				}
			}

			return;
		}

		function processInputs() {
			thisInputDevice.left_button = controller.left || keyboard.left;
			thisInputDevice.up_button = controller.up || keyboard.up;
			thisInputDevice.right_button = controller.right || keyboard.right;
			thisInputDevice.down_button = controller.down || keyboard.down;
			thisInputDevice.special_button = controller.special || keyboard.special;
			thisInputDevice.jump_button = controller.jump || keyboard.jump;
			thisInputDevice.start_button = controller.start || keyboard.start;
			thisInputDevice.select_button = controller.select || keyboard.select;

			return;
		}

		document.body.onkeydown = handlePressedInput;
		document.body.onkeyup = handleReleasedInput;

		/*
		window.addEventListener("gamepadconnected", function(event) {
			thisInputDevice.gamepad = navigator.getGamepads()[event.gamepad.index];
		});
		*/

		return;
	}

	MAETGDK.prototype.InputDevice = InputDevice;
	MAET_GDK.input_device = new MAET_GDK.InputDevice();

	return;
})(MAETGDK);
<|endoftext|>


<commit_before>if (!window['django_select2']) {
	// This JS file can be included multiple times. So, as not to overwrite previous states, we run this only once.

	var django_select2 = {
		MULTISEPARATOR: String.fromCharCode(0), // We use this unprintable char as separator,
												// since this can't be entered by user.
		get_url_params: function (term, page, context) {
			var field_id = $(this).data('field_id'),
				res = {
					'term': term,
					'page': page,
					'context': context
				};
			if (field_id) {
				res['field_id'] = field_id;
			}
			return res;
		},
		process_results: function (data, page, context) {
			var results;
			if (data.err && data.err.toLowerCase() === 'nil') {
				results = {
					'results': data.results
				};
				if (context) {
					results['context'] = context;
				}
				if (data.more === true || data.more === false) {
					results['more'] = data.more;
				}
			} else {
				results = {'results':[]};
			}
			if (results.results) {
				$(this).data('results', results.results);
			} else {
				$(this).removeData('results');
			}
			return results;
		},
		onValChange: function () {
			django_select2.updateText($(this));
		},
		prepareValText: function (vals, txts, isMultiple) {
			var data = []
			$(vals).each(function (index) {
				data.push({id: this, text: txts[index]});
			});
			if (isMultiple) {
				return data;
			} else {
				if (data.length > 0) {
					return data[0];
				} else {
					return null;
				}
			}
		},
		updateText: function ($e) {
			var val = $e.select2('val'), data = $e.select2('data'), txt = $e.txt(), isMultiple = !!$e.attr('multiple'),
				diff;

			if (val || val === 0) { // Means value is set. A numerical 0 is also a valid value.
				if (isMultiple) {
					if (val.length !== txt.length) {
						txt = [];
						$(val).each(function (idx) {
							var i, value = this, id;

							for (i in data) {
								id = data [i].id;
								if (id instanceof String) {
									id = id.valueOf();
								}
								if (id == value) {
									txt.push(data[i].text);
								}
							}
						});
					}
				} else {
					txt = data.text;
				}
				$e.txt(txt);
			} else {
				$e.txt('');
			}
		},
		getValText: function ($e) {
			var val = $e.select2('val'), res = $e.data('results'), txt = $e.txt(), isMultiple = !!$e.attr('multiple'),
				f, id = $e.attr('id');
			if (val || val === 0) { // Means value is set. A numerical 0 is also a valid value.

				if (!isMultiple) {
					val = [val];
					if (txt || txt === 0) {
						txt = [txt];
					}
				}

				if (txt === 0 || (txt && val.length === txt.length)) {
					return [val, txt];
				}

				f = $e.data('userGetValText');
				if (f) {
					txt = f($e, val, isMultiple);
					if (txt || txt === 0) {
						return [val, txt];
					}
				}

				if (res) {
					txt = [];
					$(val).each(function (idx) {
						var i, value = this;

						for (i in res) {
							if (res[i].id == value) {
								val[idx] = res[i].id; // To set it to correct data type.
								txt.push(res[i].text);
							}
						}
					});
					if (txt || txt === 0) {
						return [val, txt];
					}
				}
			}
			return null;
		},
		onInit: function (e, callback) {
			e = $(e);
			var id = e.attr('id'), data = null, val = e.select2('val');

			if (!val && val !== 0) {
				val = e.data('initVal');
			}

			if (val || val === 0) {
				// Value is set so need to get the text.
				data = django_select2.getValText(e);
				if (data && data[0]) {
					data = django_select2.prepareValText(data[0], data[1], !!e.attr('multiple'));
				}
			}
			if (!data) {
				e.val(null); // Nulling out set value so as not to confuse users.
			}
			callback(data); // Change for 2.3.x
			django_select2.updateText(e);
		},
		onMultipleHiddenChange: function () {
			var $e = $(this), valContainer = $e.data('valContainer'), name = $e.data('name'), vals = $e.val();
			valContainer.empty();
			if (vals) {
				vals = vals.split(django_select2.MULTISEPARATOR);
				$(vals).each(function () {
					var inp = $('<input type="hidden">').appendTo(valContainer);
					inp.attr('name', name);
					inp.val(this);
				});
			}
		},
		initMultipleHidden: function ($e) {
			var valContainer;

			$e.data('name', $e.attr('name'));
			$e.attr('name', '');

			valContainer = $('<div>').insertAfter($e).css({'display': 'none'});
			$e.data('valContainer', valContainer);

			$e.change(django_select2.onMultipleHiddenChange);
			if ($e.val()) {
				$e.change();
			}
		},
		convertArrToStr: function (arr) {
			return arr.join(django_select2.MULTISEPARATOR);
		},
		runInContextHelper: function (f, id) {
			return function () {
				var args = Array.prototype.slice.call(arguments);
		        return f.apply($('#' + id).get(0), args);
		    }
		},
		logErr: function () {
			if (console && console.error) {
	            args = Array.prototype.slice.call(arguments);
	            console.error.apply(console, args);
	        }
		},
	};

	(function( $ ){
		// This sets or gets the text lables for an element. It merely takes care returing array or single
		// value, based on if element is multiple type.
		$.fn.txt = function(val) {
			if (typeof(val) !== 'undefined') {
				if (val) {
					if (val instanceof Array) {
						if (this.attr('multiple')) {
							val = django_select2.convertArrToStr(val);
						} else {
							val = val[0]
						}
					}
					this.attr('txt', val);
				} else {
					this.removeAttr('txt');
				}
				return this;
			} else {
				val = this.attr('txt');
				if (this.attr('multiple')) {
					if (val) {
						val = val.split(django_select2.MULTISEPARATOR);
					} else {
						val = [];
					}
				}
				return val;
			}
		};
	})( jQuery );
}<commit_msg>Update django_select2/static/js/heavy_data.js<commit_after>if (!window['django_select2']) {
	// This JS file can be included multiple times. So, as not to overwrite previous states, we run this only once.

	var django_select2 = {
		MULTISEPARATOR: String.fromCharCode(0), // We use this unprintable char as separator,
												// since this can't be entered by user.
		get_url_params: function (term, page, context) {
			var field_id = $(this).data('field_id'),
				res = {
					'term': term,
					'page': page,
					'context': context
				};
			if (field_id) {
				res['field_id'] = field_id;
			}
			return res;
		},
		process_results: function (data, page, context) {
			var results;
			if (data.err && data.err.toLowerCase() === 'nil') {
				results = {
					'results': data.results
				};
				if (context) {
					results['context'] = context;
				}
				if (data.more === true || data.more === false) {
					results['more'] = data.more;
				}
			} else {
				results = {'results':[]};
			}
			if (results.results) {
				$(this).data('results', results.results);
			} else {
				$(this).removeData('results');
			}
			return results;
		},
		onValChange: function () {
			django_select2.updateText($(this));
		},
		prepareValText: function (vals, txts, isMultiple) {
			var data = []
			$(vals).each(function (index) {
				data.push({id: this, text: txts[index]});
			});
			if (isMultiple) {
				return data;
			} else {
				if (data.length > 0) {
					return data[0];
				} else {
					return null;
				}
			}
		},
		updateText: function ($e) {
			var val = $e.select2('val'), data = $e.select2('data'), txt = $e.txt(), isMultiple = !!$e.attr('multiple'),
				diff;

			if (val || val === 0) { // Means value is set. A numerical 0 is also a valid value.
				if (isMultiple) {
					if (val.length !== txt.length) {
						txt = [];
						$(val).each(function (idx) {
							var i, value = this, id;

							for (i in data) {
								id = data [i].id;
								if (id instanceof String) {
									id = id.valueOf();
								}
								if (id == value) {
									txt.push(data[i].text);
								}
							}
						});
					}
				} else {
					txt = data.text;
				}
				$e.txt(txt);
			} else {
				$e.txt('');
			}
		},
		getValText: function ($e) {
			var val = $e.select2('val'), res = $e.data('results'), txt = $e.txt(), isMultiple = !!$e.attr('multiple'),
				f, id = $e.attr('id');
			if (val || val === 0) { // Means value is set. A numerical 0 is also a valid value.

				if (!isMultiple) {
					val = [val];
					if (txt || txt === 0) {
						txt = [txt];
					}
				}

				if (txt === 0 || (txt && val.length === txt.length)) {
					return [val, txt];
				}

				f = $e.data('userGetValText');
				if (f) {
					txt = f($e, val, isMultiple);
					if (txt || txt === 0) {
						return [val, txt];
					}
				}

				if (res) {
					txt = [];
					$(val).each(function (idx) {
						var i, value = this;

						for (i in res) {
							if (res[i].id == value) {
								val[idx] = res[i].id; // To set it to correct data type.
								txt.push(res[i].text);
							}
						}
					});
					if (txt || txt === 0) {
						return [val, txt];
					}
				}
			}
			return null;
		},
		onInit: function (e, callback) {
			e = $(e);
			var id = e.attr('id'), data = null, val = e.select2('val');

			if (!val && val !== 0) {
				val = e.data('initVal');
			}

			if (val || val === 0) {
				// Value is set so need to get the text.
				data = django_select2.getValText(e);
				if (data && data[0]) {
					data = django_select2.prepareValText(data[0], data[1], !!e.attr('multiple'));
				}
			}
			if (!data) {
				e.val(null); // Nulling out set value so as not to confuse users.
			}
			callback(data); // Change for 2.3.x
			django_select2.updateText(e);
		},
		onMultipleHiddenChange: function () {
			var $e = $(this), valContainer = $e.data('valContainer'), name = $e.data('name'), vals = $e.val();
			valContainer.empty();
			if (vals) {
				vals = vals.split(django_select2.MULTISEPARATOR);
				$(vals).each(function () {
					var inp = $('<input type="hidden">').appendTo(valContainer);
					inp.attr('name', name);
					inp.val(this);
				});
			}
		},
		initMultipleHidden: function ($e) {
			var valContainer;

			$e.data('name', $e.attr('name'));
			$e.attr('name', '');

			valContainer = $('<div>').insertAfter($e).css({'display': 'none'});
			$e.data('valContainer', valContainer);

			$e.change(django_select2.onMultipleHiddenChange);
			if ($e.val()) {
				$e.change();
			}
		},
		convertArrToStr: function (arr) {
			return arr.join(django_select2.MULTISEPARATOR);
		},
		runInContextHelper: function (f, id) {
			return function () {
				var args = Array.prototype.slice.call(arguments);
		        return f.apply($('#' + id).get(0), args);
		    }
		},
		logErr: function () {
			if (console && console.error) {
	            args = Array.prototype.slice.call(arguments);
	            console.error.apply(console, args);
	        }
		}
	};

	(function( $ ){
		// This sets or gets the text lables for an element. It merely takes care returing array or single
		// value, based on if element is multiple type.
		$.fn.txt = function(val) {
			if (typeof(val) !== 'undefined') {
				if (val) {
					if (val instanceof Array) {
						if (this.attr('multiple')) {
							val = django_select2.convertArrToStr(val);
						} else {
							val = val[0]
						}
					}
					this.attr('txt', val);
				} else {
					this.removeAttr('txt');
				}
				return this;
			} else {
				val = this.attr('txt');
				if (this.attr('multiple')) {
					if (val) {
						val = val.split(django_select2.MULTISEPARATOR);
					} else {
						val = [];
					}
				}
				return val;
			}
		};
	})( jQuery );
}
<|endoftext|>


<commit_before>const request = require('request')
const token = process.env.FB_PAGE_ACCESS_TOKEN
const songs = require('./songs/index');
var levenshtein = require('fast-levenshtein');

module.exports = {
  removeDuplicates: function(string){
    return  string.replace(/[^\w\s]|(.)(?=\1)/gi, "");
  },
  removePunctAndLowerCase: function(string){
    return string.replace(/[^a-zA-Z ]/gi, "").toLowerCase();
  },
  checkifEqual: function(string1, string2,levenshteinIndex ) {
    let cleanedString1 = this.removeDuplicates(this.removePunctAndLowerCase(string1));
    let cleanedString2 = this.removeDuplicates(this.removePunctAndLowerCase(string2));

    let trial1 = cleanedString1 === cleanedString2;
    let trial2 = (levenshtein.get(cleanedString1, cleanedString2) <levenshteinIndex);

    return (trial1 || trial2);
  },
  getlNextline: function(line) {
    var indexIThink = -1;
    for (let x = 1; x < 3 ; x ++ ){
      for (let i = 0; i < Object.keys(songs).length; i++ ){
        // return string if match in song[i]
        // console.log("LOGING: ", songs[i][1]);
          indexIThink = (songs[i])[1].findIndex(item => this.checkifEqual(line, item, x));
          if (indexIThink != -1){
            let name = songs[i][0];
            let currentLine = songs[i][1][indexIThink];
            let nextLine = songs[i][1][indexIThink + 1];
            let response = "["+songs[i][0]+"]"+ "\n\n"+ currentLine+ "\n"+nextLine;
            return response;
        }
      }
    }

    return "Hey Pubma! Do you know what song this is!";
  },
  sendTextMessage: function(sender, text) {
    let messageData = {
      text: text
    }
    request({
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: token
      },
      method: 'POST',
      json: {
        recipient: {
          id: sender
        },
        message: messageData,
      }
    }, function(error, response, body) {
      if (error) {
        console.log('Error sending messages: ', error)
      } else if (response.body.error) {
        console.log('Error: ', response.body.error)
      }
    })
  },
  sendGenericMessage: function(sender) {
    let messageData = {
      "attachment": {
        "type": "template",
        "payload": {
          "template_type": "generic",
          "elements": [{
            "title": "First card",
            "subtitle": "Element #1 of an hscroll",
            "image_url": "http://messengerdemo.parseapp.com/img/rift.png",
            "buttons": [{
              "type": "web_url",
              "url": "https://www.messenger.com",
              "title": "web url"
            }, {
              "type": "postback",
              "title": "Postback",
              "payload": "Payload for first element in a generic bubble",
            }],
          }, {
            "title": "Second card",
            "subtitle": "Element #2 of an hscroll",
            "image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
            "buttons": [{
              "type": "postback",
              "title": "Postback",
              "payload": "Payload for second element in a generic bubble",
            }],
          }]
        }
      }
    }
    request({
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: token
      },
      method: 'POST',
      json: {
        recipient: {
          id: sender
        },
        message: messageData,
      }
    }, function(error, response, body) {
      if (error) {
        console.log('Error sending messages: ', error)
      } else if (response.body.error) {
        console.log('Error: ', response.body.error)
      }
    })
  },
  messPostback: function(senderID, posback){
    if (posback.payload == "GREETINGS_POSTBACK"){
      this.sendTextMessage(senderID, "Hello stranger! Pumba and I want to welcome you our fanpage! :)" );
      this.sendTextMessage(senderID, "We can together all night long! Try and message us 'hakuna matata'! " );
    }else {
      this.sendTextMessage(senderID, {
        text: "Postback from" + senderID+ JSON.stringify(postback)
      })

    }
  },
  typingBubble: function(senderID){
    var options = {
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: process.env.FB_PAGE_APP_ID
      },
     json: {
         'recipient': {
             'id': senderID
         },
         'sender_action': 'typing_on'
       }
     };

     console.log("#######\nTYPING\n#######");
     request(options, function(error, response, body) {
       if (error) {
        console.log("TYPING DIDNT WORK"+error);
        return;
      }
      if (response.statusCode == 200) {
        console.log("TYPING WORKED!\n"+body);
      } else {
        console.log("receive status code : " + response.statusCode);
      }
    });

  }
};
<commit_msg>fixed options json<commit_after>const request = require('request')
const token = process.env.FB_PAGE_ACCESS_TOKEN
const songs = require('./songs/index');
var levenshtein = require('fast-levenshtein');

module.exports = {
  removeDuplicates: function(string){
    return  string.replace(/[^\w\s]|(.)(?=\1)/gi, "");
  },
  removePunctAndLowerCase: function(string){
    return string.replace(/[^a-zA-Z ]/gi, "").toLowerCase();
  },
  checkifEqual: function(string1, string2,levenshteinIndex ) {
    let cleanedString1 = this.removeDuplicates(this.removePunctAndLowerCase(string1));
    let cleanedString2 = this.removeDuplicates(this.removePunctAndLowerCase(string2));

    let trial1 = cleanedString1 === cleanedString2;
    let trial2 = (levenshtein.get(cleanedString1, cleanedString2) <levenshteinIndex);

    return (trial1 || trial2);
  },
  getlNextline: function(line) {
    var indexIThink = -1;
    for (let x = 1; x < 3 ; x ++ ){
      for (let i = 0; i < Object.keys(songs).length; i++ ){
        // return string if match in song[i]
        // console.log("LOGING: ", songs[i][1]);
          indexIThink = (songs[i])[1].findIndex(item => this.checkifEqual(line, item, x));
          if (indexIThink != -1){
            let name = songs[i][0];
            let currentLine = songs[i][1][indexIThink];
            let nextLine = songs[i][1][indexIThink + 1];
            let response = "["+songs[i][0]+"]"+ "\n\n"+ currentLine+ "\n"+nextLine;
            return response;
        }
      }
    }

    return "Hey Pubma! Do you know what song this is!";
  },
  sendTextMessage: function(sender, text) {
    let messageData = {
      text: text
    }
    request({
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: token
      },
      method: 'POST',
      json: {
        recipient: {
          id: sender
        },
        message: messageData,
      }
    }, function(error, response, body) {
      if (error) {
        console.log('Error sending messages: ', error)
      } else if (response.body.error) {
        console.log('Error: ', response.body.error)
      }
    })
  },
  sendGenericMessage: function(sender) {
    let messageData = {
      "attachment": {
        "type": "template",
        "payload": {
          "template_type": "generic",
          "elements": [{
            "title": "First card",
            "subtitle": "Element #1 of an hscroll",
            "image_url": "http://messengerdemo.parseapp.com/img/rift.png",
            "buttons": [{
              "type": "web_url",
              "url": "https://www.messenger.com",
              "title": "web url"
            }, {
              "type": "postback",
              "title": "Postback",
              "payload": "Payload for first element in a generic bubble",
            }],
          }, {
            "title": "Second card",
            "subtitle": "Element #2 of an hscroll",
            "image_url": "http://messengerdemo.parseapp.com/img/gearvr.png",
            "buttons": [{
              "type": "postback",
              "title": "Postback",
              "payload": "Payload for second element in a generic bubble",
            }],
          }]
        }
      }
    }
    request({
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: token
      },
      method: 'POST',
      json: {
        recipient: {
          id: sender
        },
        message: messageData,
      }
    }, function(error, response, body) {
      if (error) {
        console.log('Error sending messages: ', error)
      } else if (response.body.error) {
        console.log('Error: ', response.body.error)
      }
    })
  },
  messPostback: function(senderID, posback){
    if (posback.payload == "GREETINGS_POSTBACK"){
      this.sendTextMessage(senderID, "Hello stranger! Pumba and I want to welcome you our fanpage! :)" );
      this.sendTextMessage(senderID, "We can together all night long! Try and message us 'hakuna matata'! " );
    }else {
      this.sendTextMessage(senderID, {
        text: "Postback from" + senderID+ JSON.stringify(postback)
      })

    }
  },
  typingBubble: function(senderID){
    var options = {
      url: 'https://graph.facebook.com/v2.10/me/messages',
      qs: {
        access_token: process.env.FB_PAGE_APP_ID
      },
      method: 'POST',
      json: {
         recipient: {
             'id': senderID
         },
         sender_action: 'typing_on'
       }
     };

     console.log("#######\nTYPING\n#######");
     request(options, function(error, response, body) {
       if (error) {
        console.log("TYPING DIDNT WORK"+error);
        return;
      }
      if (response.statusCode == 200) {
        console.log("TYPING WORKED!\n"+body);
      } else {
        console.log("receive status code : " + response.statusCode);
      }
    });

  }
};
<|endoftext|>


<commit_before>var isMobile=(function(a){'use strict';return !!a.match(/android|webos|iphone|ipad|ipod|opera mini|opera mobi|opera tablet|iemobile|windows phone|blackberry|bb10\; touch/)}(navigator.userAgent.toLowerCase()));
<commit_msg>Missing a semicolon in ismobile.min.js<commit_after>;var isMobile=(function(a){'use strict';return !!a.match(/android|webos|iphone|ipad|ipod|opera mini|opera mobi|opera tablet|iemobile|windows phone|blackberry|bb10\; touch/)}(navigator.userAgent.toLowerCase()));
<|endoftext|>


<commit_before>/**
 * jQuery DOMEC (DOM Elements Creator) 1.0.1
 * http://code.google.com/p/jquery-domec
 *
 * Copyright (c) 2008-2009 Lukasz Rajchel (lukasz@rajchel.pl | http://lukasz.rajchel.pl)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Documentation	:	http://code.google.com/p/jquery-domec/wiki/Documentation
 * Changelog		:	http://code.google.com/p/jquery-domec/wiki/Changelog
 */

/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true,
bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4 */
/*global document, jQuery*/
/*members DOMEC, addAttributes, addChildren, append, attr, create, createElement , extend, isArray,
length, propertyIsEnumerable, splice, text, toString*/
"use strict";
(function ($) {

	// DOMEC Core class
	$.DOMEC = (function () {
		// variables declaration
		// DOM element
		var Element = (function () {
			return {
				// create element
				create: function (name, root) {
					// set default root if undefined
					if (root === undefined || root === null) {
						root = document;
					}

					if (typeof root === 'object' && !$.isArray(root) &&
						typeof name === 'string') {
						return $(root.createElement(name));
					}

					return undefined;
				},
				// add attributes
				addAttributes: function (elem, attributes) {
					if (typeof attributes === 'object' && !$.isArray(attributes)) {
						for (var key in attributes) {
							if (typeof attributes[key] === 'string' ||
								typeof attributes[key] === 'number') {
								elem.attr(key, attributes[key]);
							}
						}
					}
				},
				// add child elements
				addChildren: function (elem, children) {
					if (children !== undefined && children !== null) {
						if ($.isArray(children)) {
							for (var i = 0; i < children.length; i += 1) {
								elem.append(children[i]);
							}
						} else if (typeof children === 'object') {
							elem.append(children);
						} else {
							elem.text(children.toString());
						}
					}
				}
			};
		}());

		// DOMEC public members
		return {
			create: function (name, attributes, children, root) {
				var elem = Element.create(name, root);

				if (elem !== undefined) {
					Element.addAttributes(elem, attributes);
					Element.addChildren(elem, children);
				}

				return elem;
			}
		};
	}());

 	// register jQuery extension
	$.extend({
		create: $.DOMEC.create
	});

}(jQuery));<commit_msg>version updated<commit_after>/**
 * jQuery DOMEC (DOM Elements Creator) 1.0.2
 * http://code.google.com/p/jquery-domec
 *
 * Copyright (c) 2008-2009 Lukasz Rajchel (lukasz@rajchel.pl | http://lukasz.rajchel.pl)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Documentation	:	http://code.google.com/p/jquery-domec/wiki/Documentation
 * Changelog		:	http://code.google.com/p/jquery-domec/wiki/Changelog
 */

/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true,
bitwise: true, regexp: true, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4 */
/*global document, jQuery*/
/*members DOMEC, addAttributes, addChildren, append, attr, create, createElement , extend, isArray,
length, propertyIsEnumerable, splice, text, toString*/
"use strict";
(function ($) {

	// DOMEC Core class
	$.DOMEC = (function () {
		// variables declaration
		// DOM element
		var Element = (function () {
			return {
				// create element
				create: function (name, root) {
					// set default root if undefined
					if (root === undefined || root === null) {
						root = document;
					}

					if (typeof root === 'object' && !$.isArray(root) &&
						typeof name === 'string') {
						return $(root.createElement(name));
					}

					return undefined;
				},
				// add attributes
				addAttributes: function (elem, attributes) {
					if (typeof attributes === 'object' && !$.isArray(attributes)) {
						for (var key in attributes) {
							if (typeof attributes[key] === 'string' ||
								typeof attributes[key] === 'number') {
								elem.attr(key, attributes[key]);
							}
						}
					}
				},
				// add child elements
				addChildren: function (elem, children) {
					if (children !== undefined && children !== null) {
						if ($.isArray(children)) {
							for (var i = 0; i < children.length; i += 1) {
								elem.append(children[i]);
							}
						} else if (typeof children === 'object') {
							elem.append(children);
						} else {
							elem.text(children.toString());
						}
					}
				}
			};
		}());

		// DOMEC public members
		return {
			create: function (name, attributes, children, root) {
				var elem = Element.create(name, root);

				if (elem !== undefined) {
					Element.addAttributes(elem, attributes);
					Element.addChildren(elem, children);
				}

				return elem;
			}
		};
	}());

 	// register jQuery extension
	$.extend({
		create: $.DOMEC.create
	});

}(jQuery));<|endoftext|>


<commit_before>/*
    A simple jQuery modal (http://github.com/kylefox/jquery-modal)
    Version 0.2.4
*/
(function() {

  var current_modal = null;

  $.fn.modal = function(options) {

    var $elm = $(this);

    // If this is a link, bind to its click event.
    if($elm.attr('href')) {
      $elm.click(open_modal_from_link);
      return;
    }

    options = $.extend({}, $.fn.modal.defaults, options);

    function block() {
      current_modal.blocker = $('

').css({
        top: 0, right: 0, bottom: 0, left: 0,
        width: "100%", height: "100%",
        position: "fixed",
        zIndex: options.zIndex,
        background: options.overlay,
        opacity: options.opacity
      });
      if(options.escapeClose) {
        $(document).keydown(function(event) {
          if(event.which == 27) {$.fn.modal.close();}
        });
      }
      if(options.clickClose) {
        current_modal.blocker.click($.fn.modal.close);
      }
      $('body').append(current_modal.blocker);
      $elm.trigger($.fn.modal.BLOCK, [current_modal]);
    }

    function show() {
      center_modal(current_modal);
      if(options.showClose) {
        current_modal.closeButton = $('' + options.closeText + '');
        current_modal.elm.append(current_modal.closeButton);
      }
      $elm.addClass(options.modalClass).addClass('current').show();
      $elm.trigger($.fn.modal.OPEN, [current_modal]);
    }

    current_modal = {elm: $elm, options: options};
    $elm.trigger($.fn.modal.BEFORE_BLOCK, [current_modal]);
    block();
    $elm.trigger($.fn.modal.BEFORE_OPEN, [current_modal]);
    show();
  };

  $.fn.modal.defaults = {
    overlay: "#000",
    opacity: 0.75,
    zIndex: 1,
    escapeClose: true,
    clickClose: true,
    closeText: 'Close',
    modalClass: "modal",
    showClose: true
  };

  // Event constants:
  $.fn.modal.BEFORE_BLOCK = 'modal:before-block';
  $.fn.modal.BLOCK = 'modal:block';
  $.fn.modal.BEFORE_OPEN = 'modal:before-open';
  $.fn.modal.OPEN = 'modal:open';
  $.fn.modal.BEFORE_CLOSE = 'modal:before-close';
  $.fn.modal.CLOSE = 'modal:close';

  $.fn.modal.close = function(event) {
    if(event) {
      event.preventDefault();
    }
    if(!current_modal) {
      return;
    }

    current_modal.elm.trigger($.fn.modal.BEFORE_CLOSE, [current_modal]);
    if(current_modal.closeButton) {
      current_modal.closeButton.remove();
    }
    current_modal.blocker.remove();
    current_modal.elm.hide();
    current_modal.elm.trigger($.fn.modal.CLOSE, [current_modal]);
    current_modal = null;
  };

  $.fn.modal.resize = function() {
    center_modal(current_modal);
  };

  function open_modal_from_link(event) {
    event.preventDefault();
    var target = $(this).attr('href');
    if(/^#/.test(target)) { // DOM id
      $(target).modal();
    } else { // AJAX
      $.get(target, {}, function(html) {
        $(html)
          .appendTo('body')
          .bind('modal:close', function(event, modal) { modal.elm.remove(); })
          .modal();
      });
    }
  }

  function center_modal(modal) {
    modal.elm.css({
      position: 'fixed',
      top: "50%",
      left: "50%",
      marginTop: - (modal.elm.outerHeight() / 2),
      marginLeft: - (modal.elm.outerWidth() / 2),
      zIndex: modal.options.zIndex + 1
    });
  };

  // Automatically bind links with rel="modal:close" to, well, close the modal.
  $('a[rel="modal:open"]').live('click', open_modal_from_link);
  $('a[rel="modal:close"]').live('click', $.fn.modal.close);

})();<commit_msg>fix bug with escapeClose option<commit_after>/*
    A simple jQuery modal (http://github.com/kylefox/jquery-modal)
    Version 0.2.4
*/
(function() {

  var current_modal = null;

  $.fn.modal = function(options) {

    var $elm = $(this);

    // If this is a link, bind to its click event.
    if($elm.attr('href')) {
      $elm.click(open_modal_from_link);
      return;
    }

    options = $.extend({}, $.fn.modal.defaults, options);

    function block() {
      current_modal.blocker = $('

').css({
        top: 0, right: 0, bottom: 0, left: 0,
        width: "100%", height: "100%",
        position: "fixed",
        zIndex: options.zIndex,
        background: options.overlay,
        opacity: options.opacity
      });
      if(options.escapeClose) {
        $(document).bind('keydown.modal', function(event) {
          if(event.which == 27) {$.fn.modal.close();}
        });
      }
      if(options.clickClose) {
        current_modal.blocker.click($.fn.modal.close);
      }
      $('body').append(current_modal.blocker);
      $elm.trigger($.fn.modal.BLOCK, [current_modal]);
    }

    function show() {
      center_modal(current_modal);
      if(options.showClose) {
        current_modal.closeButton = $('' + options.closeText + '');
        current_modal.elm.append(current_modal.closeButton);
      }
      $elm.addClass(options.modalClass).addClass('current').show();
      $elm.trigger($.fn.modal.OPEN, [current_modal]);
    }

    current_modal = {elm: $elm, options: options};
    $elm.trigger($.fn.modal.BEFORE_BLOCK, [current_modal]);
    block();
    $elm.trigger($.fn.modal.BEFORE_OPEN, [current_modal]);
    show();
  };

  $.fn.modal.defaults = {
    overlay: "#000",
    opacity: 0.75,
    zIndex: 1,
    escapeClose: true,
    clickClose: true,
    closeText: 'Close',
    modalClass: "modal",
    showClose: true
  };

  // Event constants:
  $.fn.modal.BEFORE_BLOCK = 'modal:before-block';
  $.fn.modal.BLOCK = 'modal:block';
  $.fn.modal.BEFORE_OPEN = 'modal:before-open';
  $.fn.modal.OPEN = 'modal:open';
  $.fn.modal.BEFORE_CLOSE = 'modal:before-close';
  $.fn.modal.CLOSE = 'modal:close';

  $.fn.modal.close = function(event) {
    if(event) {
      event.preventDefault();
    }
    if(!current_modal) {
      return;
    }

    current_modal.elm.trigger($.fn.modal.BEFORE_CLOSE, [current_modal]);
    if(current_modal.closeButton) {
      current_modal.closeButton.remove();
    }
    current_modal.blocker.remove();
    current_modal.elm.hide();
    current_modal.elm.trigger($.fn.modal.CLOSE, [current_modal]);
    current_modal = null;

    $(document).unbind('keydown.modal');
  };

  $.fn.modal.resize = function() {
    center_modal(current_modal);
  };

  function open_modal_from_link(event) {
    event.preventDefault();
    var target = $(this).attr('href');
    if(/^#/.test(target)) { // DOM id
      $(target).modal();
    } else { // AJAX
      $.get(target, {}, function(html) {
        $(html)
          .appendTo('body')
          .bind('modal:close', function(event, modal) { modal.elm.remove(); })
          .modal();
      });
    }
  }

  function center_modal(modal) {
    modal.elm.css({
      position: 'fixed',
      top: "50%",
      left: "50%",
      marginTop: - (modal.elm.outerHeight() / 2),
      marginLeft: - (modal.elm.outerWidth() / 2),
      zIndex: modal.options.zIndex + 1
    });
  };

  // Automatically bind links with rel="modal:close" to, well, close the modal.
  $('a[rel="modal:open"]').live('click', open_modal_from_link);
  $('a[rel="modal:close"]').live('click', $.fn.modal.close);

})();<|endoftext|>


<commit_before>/**
 * A component
 * @constructor
 * @param {Object} options
 * @param {Element} options.element
 * @param {String} options.name
 * @param {Object} options.listen A listener
 */
hui.ui.Component = function(options) {
	this.name = options.name;
	if (!this.name) {
		hui.ui.latestObjectIndex++;
		this.name = 'unnamed'+hui.ui.latestObjectIndex;
	}
	this.element = hui.get(options.element);
  this.delegates = [];
  if (this.nodes) {
  	this.nodes = hui.collect(this.nodes,this.element);
  }
  if (options.listen) {
    this.listen(options.listen);
  }
  hui.ui.registerComponent(this);
}

hui.ui.Component.prototype = {
  /**
   * Add event listener
   * @param {Object} listener An object with methods for different events
   */
  listen : function(listener) {
    this.delegates.push(listener);
  },
  fire : function(name,value,event) {
  		return hui.ui.callDelegates(this,name,value,event);
  },
  /**
   * Get the components root element
   * @returns Element
   */
  getElement : function() {
    return this.element;
  },
  destroy : function() {
    if (this.element) {
      hui.dom.remove(this.element);
    }
  }
}<commit_msg>Added more of the original functionality to the Component<commit_after>/**
 * A component
 * @constructor
 * @param {Object} options
 * @param {Element} options.element
 * @param {String} options.name
 * @param {Object} options.listen A listener
 */
hui.ui.Component = function(options) {
	this.name = options.name;
	if (!this.name) {
		hui.ui.latestObjectIndex++;
		this.name = 'unnamed'+hui.ui.latestObjectIndex;
	}
	this.element = hui.get(options.element);
  this.delegates = [];
  if (this.nodes) {
  	this.nodes = hui.collect(this.nodes,this.element);
  }
  if (options.listen) {
    this.listen(options.listen);
  }
  hui.ui.registerComponent(this);
}

hui.ui.Component.prototype = {
  /**
   * Add event listener
   * @param {Object} listener An object with methods for different events
   */
  listen : function(listener) {
    this.delegates.push(listener);
  },
  fire : function(name,value,event) {
  		return hui.ui.callDelegates(this,name,value,event);
  },
  /**
   * Get the components root element
   * @returns Element
   */
  getElement : function() {
    return this.element;
  },
  destroy : function() {
    if (this.element) {
      hui.dom.remove(this.element);
    }
  },
	valueForProperty : function(property) {
	  return this[property];
	},
	fireValueChange : function() {
		this.fire('valueChanged',this.value);
		hui.ui.firePropertyChange(this,'value',this.value);
		hui.ui.callAncestors(this,'childValueChanged',this.value);
	},
	fireSizeChange : function() {
		hui.ui.callAncestors(this,'$$childSizeChanged');
	}
}<|endoftext|>


<commit_before>/*jshint devel: true, bitwise: false*/
import ResourceManager from '../Resource/ResourceManager';
import Binary from '../Binary/Binary';
import DisplayManager from '../Display/DisplayManager';
import AudioManager from '../Audio/AudioManager';
import NM from '../Notification/NotificationManager';
import Input from '../Input/InputManager';
import fpscounter from '../lib/fpscounter/fpscounter.min';
import Dom from './Dom';

/**
 * The `Game` class is the central part to AthenaJS
 *
 * @param {Object} options
 * @param {boolean} [options.debug=false] Debug will be enabled if this is true.
 * @param {string} [options.name] The name of the game.
 * @param {string|HTMLElement} [options.target="new Dom('div')"] target The DOM target of the game: this is where the game canvas elements will be added.
 * By default the target is a new Div that is appened to the body element.
 * @param {boolean} [options.showFps=false] A little fps counter will be displayed if this is true.
 * @param {number} [options.width=1024] The width of the game display.
 * @param {number} [options.height=768] The height of the game display.
 * @param {Object} [options.resources] An optionnal array of resources of the form:``{ id: 'unique id', type: 'image|script|map|audio', src: 'path_to_resource'}`` that the scene needs.
 *
 */
class Game {
    constructor(options = {}) {
        console.log('[Game] Init()'/*, options*/);

        this.debug = options.debug;
        this.name = options.name;

        const target = options.target && new Dom(options.target);

        // weather to display FPS
        this.showFps = typeof options.showFps !== 'undefined' ? options.showFps : false;

        if (this.showFps) {
            fpscounter({
                width: 50,
                height: 50
            });
        }

        this.width = options.width || 1024;
        this.height = options.height || 768;

        // DOM element to use for the game (it will be cleared)
        this.target = target && target.length && target[0] || Dom('div').css({
            width: `${this.width}px`,
            height: `${this.height}px`
        }).appendTo('body')[0];

        Dom(this.target).addClass('athena-game');

        this.resources = options.resources;

        // current display
        this.display = null;

        // current scene
        this.scene = null;

        this.running = false;

        // get ready for input events
        this._initEvents();

        // creates the main display
        this.createDisplay({
            name: 'main',
            width: options.width,
            height: options.height,
            layers: options.layers || [true, true]
        }, this.target);

        this.toggleSound(typeof options.sound !== 'undefined' ? options.sound : true);

        if (!this.sound) {
            console.warn('sound disabled: skipping audio resources');
            ResourceManager.skipResources.push('audio');
        }

        // eventLoop timeout
        this.timeout = null;

        // rendering loop
        this.animFrame = null;

        // listen for all events
        this.bindEvents('*');

        if (options.scene) {
            this.setScene(options.scene);
        }

        if (this.debug) {
            document.addEventListener('keyup', (event) => {
                if (event.keyCode === 68) {
                    if (this.scene) {
                        this.scene.debug();
                        this.scene.map && this.toggleTileInspector(this.scene.isDebug);
                    }
                } else if (event.keyCode === 70) {
                    this.toggleFullscreen();
                } else if (event.keyCode === 80) {
                    this.togglePause();
                } else if (event.keyCode === 82) {
                    this.setScene(this.scene);
                }
            });

            if (navigator.userAgent.match(/Xbox/)) {
                this.target.addEventListener('click', (event) => {
                    console.log('toggeling fullscreen');
                    this.toggleFullscreen();
                });
            }
        }
    }

    addInspector() {
        this.moveHandler = (event) => {
            const map = this.scene.map;
            const offsetX = event.offsetX > 0 ? event.offsetX : 0;
            const offsetY = event.offsetY > 0 ? event.offsetY : 0;
            const mapOffsetX = this.scene.mapOffsetX,
                mapOffsetY = this.scene.mapOffsetY,
                mapWidth = map.viewportW,
                mapHeight = map.viewportH;

            if (event.offsetX < mapOffsetX || event.offsetX > (mapOffsetX + mapWidth) || event.offsetY < mapOffsetY || event.offsetY > (mapOffsetY + mapHeight)) {
                this.tileInspector.hide();
                this.tileHover.hide();
            } else {
                const pos = map.getTileIndexFromPixel(offsetX - mapOffsetX - map.viewportX, offsetY - mapOffsetY - map.viewportY);
                if (pos) {
                    this.tileInspector.html(`${pos.x}, ${pos.y}<br />[${map.map[pos.x + pos.y * map.numCols]}, ${map.tileBehaviors[pos.x + pos.y * map.numCols]}]`).css({
                        left: 0,
                        top: 0
                    });
                    this.tileInspector.show();
                    this.tileHover.css({
                        left: ((pos.x * map.tileWidth) + mapOffsetX + map.viewportX) + 'px',
                        top: ((pos.y * map.tileHeight) + mapOffsetY + map.viewportY) + 'px'
                    }).show();
                } else {
                    this.tileInspector.hide();
                    this.tileHover.hide();
                }
            }
        };

        this.tileInspector = new Dom('div').css({
            border: '1px dotted white',
            'background-color': 'rgba(0,0,0,.7)',
            color: 'white',
            'z-index': 10,
            position: 'absolute',
            'pointer-events': 'none'
        }).appendTo(this.target);

        this.tileHover = new Dom('div').css({
            border: '1px dotted white',
            width: `${this.scene.map.tileWidth}px`,
            height: `${this.scene.map.tileHeight}px`,
            'background-color': 'rgba(255,0,0,.4)',
            position: 'absolute',
            'z-index': 10,
            top: 0,
            left: 0,
            'pointer-events': 'none'
        }).appendTo(this.target);

        // TODO: do not change position if != static
        Dom(this.target).css('position', 'relative');
    }

    toggleTileInspector(enable) {
        if (enable) {
            if (!this.tileInspector) {
                this.addInspector();
            }
            this.tileInspector.show();
            this.target.addEventListener('mousemove', this.moveHandler, false);
        } else if (this.tileInspector) {
            this.target.removeEventListener('mousemove', this.moveHandler);
            this.tileInspector.hide();
            this.tileHover.hide();
        }
    }

    /**
     * Get ready for events from NotificationManager
     *
     * @param {String} eventList space-separated list of events to listen to
     *
     */
    bindEvents(eventList) {
        NM.listen(eventList, this.onEvent.bind(this));
    }

    /**
     * Method that gets called when receiving an event: by default it does nothing
     * It's up to the developer to overwrite this method on its Game
     *
     * @param {string} event the event name that got fired.
     *
     */
    onEvent(event) {

    }

    /**
     * Toggles global sound
     *
     * @param {boolean} bool Weather to enable or disable sound.
     *
     */
    toggleSound(bool) {
        this.sound = bool;
        AudioManager.toggleSound(bool);
    }

    toggleFullscreen() {
        if (this.display) {
            this.display.toggleFullscreen();
        }
    }

    /**
     * Creates a new display
     *
     * TODO: DESCRIBE
     * @param {Object} options
     * @param {String|HTMLElement} The target of the display: this is were canvas elements will be added
     * @private
     *
     */
    createDisplay(options, target) {
        this.display = DisplayManager.addDisplay(options, target);
    }

    /**
     * INTERNAL: initialize input events
     * @private
     */
    _initEvents() {
        Input.init({
            joystick: true
        });

        this._addVisibilityEvents();
    }

    _addVisibilityEvents() {
        let eventName = '',
            property = '';

        console.log('adding events');
        if (typeof document.hidden !== "undefined") {
            property = "hidden";
            eventName = "visibilitychange";
        } else if (typeof document.msHidden !== "undefined") {
            property = "msHidden";
            eventName = "msvisibilitychange";
        } else if (typeof document.webkitHidden !== "undefined") {
            property = "webkitHidden";
            eventName = "webkitvisibilitychange";
        }

        if (!eventName.length) {
            console.warn('[Game] Visibility API not available.');
        } else {
            document.addEventListener(eventName, () => {
                console.log('got visibility event', document[property]);
                if (document[property]) {
                    if (this.running) {
                        this.togglePause();
                    }
                }
            }, false);
        }
    }

    /**
     * Sets a new scene as the current scene
     *
     * @param {Scene} scene instance to set as current Scene
     * @param {Boolean=false} resetMap set to true to reset the map
     *
     */
    setScene(scene) {
        console.log('[Game] setScene()');
        if (this.scene !== scene) {
            if (this.scene) {
                // stops render + input loops
                this._stopSceneLoops();

                // stops the scene from running
                this.scene._stop();

                this.toggleTileInspector(false);
            }

            // TODO: handle case where user attempt to set the scene that's currently running
            if (scene) {
                // this.scene = scene;

                // // debug
                // window.currentScene = scene;

                // console.log('**resetMap', resetMap);
                //*** this.startScene(resetMap);
                this._setupScene(scene);
            } else {
                console.warn('attempt to set non-existing scene:', scene);
            }
        } else {
            console.log('same scene');
        }
    }

    _startScene() {
        console.log('[Game] _startScene()');
        const hudScene = this.scene.hudScene;
        this.display.clearAllScreens();
        this.scene._start();
        this.scene.start();

        if (hudScene) {
            hudScene.start();
        }

        this._runSceneLoop();
        this._renderSceneLoop();
    }

    _setupScene(scene) {
        console.log('[Game] _setupScene()');
        this.scene = scene;

        const hudScene = scene.hudScene;

        // Debug
        window.currentScene = scene;

        // first do internal stuff
        this.scene.setDisplay(this.display);
        this.display.clearDisplay();

        // call the scene's internal setup method: we don't want to
        // rely on the user calling the parent scene's method so we call
        // it ourselves
        this.scene._setup();
        // then the public one
        this.scene.setup();
        if (hudScene) {
            hudScene.setup();
        }

        // finally load the scene, and call startScene once the scene has been loaded
        this.scene._load().then((scene) => {
            if (this.scene === scene) {
                this._startScene();
            } else {
                console.warn("[Game] scene", scene, "has finished loading but is not the current scene anymore so it won't be started.");
            }
        }).catch(err => {
            console.error(err);
        });
    }

    /**
     * This is the render scene loop that's get called at up to 60 times per second
     *
     * @param {any} time since last frame was rendered
     *
     * @private
     */
    _renderSceneLoop(time) {
        var scene = this.scene;

        if (this.running) {
            // if we are playing events, set on each frame
            // TODO: maybe we could throttle it for 1/2 frame
            if (Input.playingEvents) {
                Input.nextRecordedEvents();
            }

            // schedule a call for next frame
            this.animFrame = window.requestAnimationFrame(this._renderSceneLoop.bind(this));

            // render the scone onto the current display
            this.display.renderScene(scene);

            // TODO: hudScene drawing is hardcoded into display
            // this should be moved back here
            if (scene.hudScene) {
                // use another display (canvas) for the hud: we could use the same one
                // this.display.renderSecondary(scene.hudScene);
                //
                // this.display.renderScene(scene.hudScene);
            }

            // if we are recording events, we do it on each frame
            // TODO: maybe we could only record once key has been received ?
            if (Input.recording) {
                Input.recordEvents();
            }
        }
    }

    /**
     * Main event loop: handles scene based-events
     *
     * @private
     */
    _runSceneLoop() {
        let scene = this.scene;

        if (!this.running) {
            this.running = true;
        }

        scene.update(new Date().getTime());

        if (this.running) {
            this.timeout = setTimeout(this._runSceneLoop.bind(this), 16);
        }
    }

    /**
     * Pauses the game: both loops are stopped so almost no cpu/gpu is used when calling it
     *
     */
    togglePause() {
        if (this.running) {
            this.running = false;
            // let the scene have a chance to update display, like showing
            // a pause logo, etc...
            this.scene.pause(this.running);
            // and display changes
            this.display.renderScene(this.scene);
            // then immediately stop the scene
            this._stopSceneLoops();
        } else {
            this.running = true;
            this.scene.pause(this.running);

            this._runSceneLoop();
            this._renderSceneLoop();
        }
    }
    // togglePauseGame() {
    //     if (this.running) {
    //         console.log('pausing game');
    //         this.running = false;
    //         this.scene.pause();

    //         // be sure to render any changes from the scene before stopping refresh
    //         this.display.renderScene(this.scene);

    //         // then stop render/event loop
    //         this.stopScene();
    //     } else {
    //         console.log('un-pausing game');
    //         this.running = true;
    //         this.scene.unpause();
    //         this._runSceneLoop();
    //         this._renderSceneLoop();
    //     }
    // }

    /**
     * Starts the current scene
     *
     * @param {Boolean=false} resetMap resets the map associated to the map
     *
     * - loads the scene if not already loaded
     * - once it's loaded calls scene.start() and start both event & render loops
     *
     */
    // startScene(resetMap = false) {
    //     console.log('[Game] startScene');

    //     if (this.scene) {
    //         console.log('[Game] loading scene');
    //         this.scene.load().then(() => {
    //             console.log('[Game] Scene', this.scene.name, 'loaded: starting run & render loops');
    //             this.scene.start(resetMap);
    //             this._runSceneLoop();
    //             this._renderSceneLoop();
    //         });
    //     } else {
    //         console.log('[Game] nothing to start: no scene selected!!');
    //     }
    // }

    /**
     * Stops current scene from running: this will both halt render & event loops
     *
     * Use Game.togglePauseGame() to temporarly pause a game
     *
     * @private
     *
     */
    _stopSceneLoops() {
        this.running = false;

        console.log('[Game] Scene stopped, stopping run & render loops');

        if (this.animFrame) {
            window.cancelAnimationFrame(this.animFrame);
            this.animFrame = null;
        }
        if (this.timeout) {
            clearTimeout(this.timeout);
            this.timeout = null;
        }
    }

}

export default Game;
<commit_msg>UPDATED: default layers = [foreground, background]<commit_after>/*jshint devel: true, bitwise: false*/
import ResourceManager from '../Resource/ResourceManager';
import Binary from '../Binary/Binary';
import DisplayManager from '../Display/DisplayManager';
import AudioManager from '../Audio/AudioManager';
import NM from '../Notification/NotificationManager';
import Input from '../Input/InputManager';
import fpscounter from '../lib/fpscounter/fpscounter.min';
import Dom from './Dom';

/**
 * The `Game` class is the central part to AthenaJS
 *
 * @param {Object} options
 * @param {boolean} [options.debug=false] Debug will be enabled if this is true.
 * @param {string} [options.name] The name of the game.
 * @param {string|HTMLElement} [options.target="new Dom('div')"] target The DOM target of the game: this is where the game canvas elements will be added.
 * By default the target is a new Div that is appened to the body element.
 * @param {boolean} [options.showFps=false] A little fps counter will be displayed if this is true.
 * @param {number} [options.width=1024] The width of the game display.
 * @param {number} [options.height=768] The height of the game display.
 * @param {Object} [options.resources] An optionnal array of resources of the form:``{ id: 'unique id', type: 'image|script|map|audio', src: 'path_to_resource'}`` that the scene needs.
 *
 */
class Game {
    constructor(options = {}) {
        console.log('[Game] Init()'/*, options*/);

        this.debug = options.debug;
        this.name = options.name;

        const target = options.target && new Dom(options.target);

        // weather to display FPS
        this.showFps = typeof options.showFps !== 'undefined' ? options.showFps : false;

        if (this.showFps) {
            fpscounter({
                width: 50,
                height: 50
            });
        }

        this.width = options.width || 1024;
        this.height = options.height || 768;

        // DOM element to use for the game (it will be cleared)
        this.target = target && target.length && target[0] || Dom('div').css({
            width: `${this.width}px`,
            height: `${this.height}px`
        }).appendTo('body')[0];

        Dom(this.target).addClass('athena-game');

        this.resources = options.resources;

        // current display
        this.display = null;

        // current scene
        this.scene = null;

        this.running = false;

        // get ready for input events
        this._initEvents();

        // creates the main display
        this.createDisplay({
            name: 'main',
            width: options.width,
            height: options.height,
            layers: options.layers || [false, true]
        }, this.target);

        this.toggleSound(typeof options.sound !== 'undefined' ? options.sound : true);

        if (!this.sound) {
            console.warn('sound disabled: skipping audio resources');
            ResourceManager.skipResources.push('audio');
        }

        // eventLoop timeout
        this.timeout = null;

        // rendering loop
        this.animFrame = null;

        // listen for all events
        this.bindEvents('*');

        if (options.scene) {
            this.setScene(options.scene);
        }

        if (this.debug) {
            document.addEventListener('keyup', (event) => {
                if (event.keyCode === 68) {
                    if (this.scene) {
                        this.scene.debug();
                        this.scene.map && this.toggleTileInspector(this.scene.isDebug);
                    }
                } else if (event.keyCode === 70) {
                    this.toggleFullscreen();
                } else if (event.keyCode === 80) {
                    this.togglePause();
                } else if (event.keyCode === 82) {
                    this.setScene(this.scene);
                }
            });

            if (navigator.userAgent.match(/Xbox/)) {
                this.target.addEventListener('click', (event) => {
                    console.log('toggeling fullscreen');
                    this.toggleFullscreen();
                });
            }
        }
    }

    addInspector() {
        this.moveHandler = (event) => {
            const map = this.scene.map;
            const offsetX = event.offsetX > 0 ? event.offsetX : 0;
            const offsetY = event.offsetY > 0 ? event.offsetY : 0;
            const mapOffsetX = this.scene.mapOffsetX,
                mapOffsetY = this.scene.mapOffsetY,
                mapWidth = map.viewportW,
                mapHeight = map.viewportH;

            if (event.offsetX < mapOffsetX || event.offsetX > (mapOffsetX + mapWidth) || event.offsetY < mapOffsetY || event.offsetY > (mapOffsetY + mapHeight)) {
                this.tileInspector.hide();
                this.tileHover.hide();
            } else {
                const pos = map.getTileIndexFromPixel(offsetX - mapOffsetX - map.viewportX, offsetY - mapOffsetY - map.viewportY);
                if (pos) {
                    this.tileInspector.html(`${pos.x}, ${pos.y}<br />[${map.map[pos.x + pos.y * map.numCols]}, ${map.tileBehaviors[pos.x + pos.y * map.numCols]}]`).css({
                        left: 0,
                        top: 0
                    });
                    this.tileInspector.show();
                    this.tileHover.css({
                        left: ((pos.x * map.tileWidth) + mapOffsetX + map.viewportX) + 'px',
                        top: ((pos.y * map.tileHeight) + mapOffsetY + map.viewportY) + 'px'
                    }).show();
                } else {
                    this.tileInspector.hide();
                    this.tileHover.hide();
                }
            }
        };

        this.tileInspector = new Dom('div').css({
            border: '1px dotted white',
            'background-color': 'rgba(0,0,0,.7)',
            color: 'white',
            'z-index': 10,
            position: 'absolute',
            'pointer-events': 'none'
        }).appendTo(this.target);

        this.tileHover = new Dom('div').css({
            border: '1px dotted white',
            width: `${this.scene.map.tileWidth}px`,
            height: `${this.scene.map.tileHeight}px`,
            'background-color': 'rgba(255,0,0,.4)',
            position: 'absolute',
            'z-index': 10,
            top: 0,
            left: 0,
            'pointer-events': 'none'
        }).appendTo(this.target);

        // TODO: do not change position if != static
        Dom(this.target).css('position', 'relative');
    }

    toggleTileInspector(enable) {
        if (enable) {
            if (!this.tileInspector) {
                this.addInspector();
            }
            this.tileInspector.show();
            this.target.addEventListener('mousemove', this.moveHandler, false);
        } else if (this.tileInspector) {
            this.target.removeEventListener('mousemove', this.moveHandler);
            this.tileInspector.hide();
            this.tileHover.hide();
        }
    }

    /**
     * Get ready for events from NotificationManager
     *
     * @param {String} eventList space-separated list of events to listen to
     *
     */
    bindEvents(eventList) {
        NM.listen(eventList, this.onEvent.bind(this));
    }

    /**
     * Method that gets called when receiving an event: by default it does nothing
     * It's up to the developer to overwrite this method on its Game
     *
     * @param {string} event the event name that got fired.
     *
     */
    onEvent(event) {

    }

    /**
     * Toggles global sound
     *
     * @param {boolean} bool Weather to enable or disable sound.
     *
     */
    toggleSound(bool) {
        this.sound = bool;
        AudioManager.toggleSound(bool);
    }

    toggleFullscreen() {
        if (this.display) {
            this.display.toggleFullscreen();
        }
    }

    /**
     * Creates a new display
     *
     * TODO: DESCRIBE
     * @param {Object} options
     * @param {String|HTMLElement} The target of the display: this is were canvas elements will be added
     * @private
     *
     */
    createDisplay(options, target) {
        this.display = DisplayManager.addDisplay(options, target);
    }

    /**
     * INTERNAL: initialize input events
     * @private
     */
    _initEvents() {
        Input.init({
            joystick: true
        });

        this._addVisibilityEvents();
    }

    _addVisibilityEvents() {
        let eventName = '',
            property = '';

        console.log('adding events');
        if (typeof document.hidden !== "undefined") {
            property = "hidden";
            eventName = "visibilitychange";
        } else if (typeof document.msHidden !== "undefined") {
            property = "msHidden";
            eventName = "msvisibilitychange";
        } else if (typeof document.webkitHidden !== "undefined") {
            property = "webkitHidden";
            eventName = "webkitvisibilitychange";
        }

        if (!eventName.length) {
            console.warn('[Game] Visibility API not available.');
        } else {
            document.addEventListener(eventName, () => {
                console.log('got visibility event', document[property]);
                if (document[property]) {
                    if (this.running) {
                        this.togglePause();
                    }
                }
            }, false);
        }
    }

    /**
     * Sets a new scene as the current scene
     *
     * @param {Scene} scene instance to set as current Scene
     * @param {Boolean=false} resetMap set to true to reset the map
     *
     */
    setScene(scene) {
        console.log('[Game] setScene()');
        if (this.scene !== scene) {
            if (this.scene) {
                // stops render + input loops
                this._stopSceneLoops();

                // stops the scene from running
                this.scene._stop();

                this.toggleTileInspector(false);
            }

            // TODO: handle case where user attempt to set the scene that's currently running
            if (scene) {
                // this.scene = scene;

                // // debug
                // window.currentScene = scene;

                // console.log('**resetMap', resetMap);
                //*** this.startScene(resetMap);
                this._setupScene(scene);
            } else {
                console.warn('attempt to set non-existing scene:', scene);
            }
        } else {
            console.log('same scene');
        }
    }

    _startScene() {
        console.log('[Game] _startScene()');
        const hudScene = this.scene.hudScene;
        this.display.clearAllScreens();
        this.scene._start();
        this.scene.start();

        if (hudScene) {
            hudScene.start();
        }

        this._runSceneLoop();
        this._renderSceneLoop();
    }

    _setupScene(scene) {
        console.log('[Game] _setupScene()');
        this.scene = scene;

        const hudScene = scene.hudScene;

        // Debug
        window.currentScene = scene;

        // first do internal stuff
        this.scene.setDisplay(this.display);
        this.display.clearDisplay();

        // call the scene's internal setup method: we don't want to
        // rely on the user calling the parent scene's method so we call
        // it ourselves
        this.scene._setup();
        // then the public one
        this.scene.setup();
        if (hudScene) {
            hudScene.setup();
        }

        // finally load the scene, and call startScene once the scene has been loaded
        this.scene._load().then((scene) => {
            if (this.scene === scene) {
                this._startScene();
            } else {
                console.warn("[Game] scene", scene, "has finished loading but is not the current scene anymore so it won't be started.");
            }
        }).catch(err => {
            console.error(err);
        });
    }

    /**
     * This is the render scene loop that's get called at up to 60 times per second
     *
     * @param {any} time since last frame was rendered
     *
     * @private
     */
    _renderSceneLoop(time) {
        var scene = this.scene;

        if (this.running) {
            // if we are playing events, set on each frame
            // TODO: maybe we could throttle it for 1/2 frame
            if (Input.playingEvents) {
                Input.nextRecordedEvents();
            }

            // schedule a call for next frame
            this.animFrame = window.requestAnimationFrame(this._renderSceneLoop.bind(this));

            // render the scone onto the current display
            this.display.renderScene(scene);

            // TODO: hudScene drawing is hardcoded into display
            // this should be moved back here
            if (scene.hudScene) {
                // use another display (canvas) for the hud: we could use the same one
                // this.display.renderSecondary(scene.hudScene);
                //
                // this.display.renderScene(scene.hudScene);
            }

            // if we are recording events, we do it on each frame
            // TODO: maybe we could only record once key has been received ?
            if (Input.recording) {
                Input.recordEvents();
            }
        }
    }

    /**
     * Main event loop: handles scene based-events
     *
     * @private
     */
    _runSceneLoop() {
        let scene = this.scene;

        if (!this.running) {
            this.running = true;
        }

        scene.update(new Date().getTime());

        if (this.running) {
            this.timeout = setTimeout(this._runSceneLoop.bind(this), 16);
        }
    }

    /**
     * Pauses the game: both loops are stopped so almost no cpu/gpu is used when calling it
     *
     */
    togglePause() {
        if (this.running) {
            this.running = false;
            // let the scene have a chance to update display, like showing
            // a pause logo, etc...
            this.scene.pause(this.running);
            // and display changes
            this.display.renderScene(this.scene);
            // then immediately stop the scene
            this._stopSceneLoops();
        } else {
            this.running = true;
            this.scene.pause(this.running);

            this._runSceneLoop();
            this._renderSceneLoop();
        }
    }
    // togglePauseGame() {
    //     if (this.running) {
    //         console.log('pausing game');
    //         this.running = false;
    //         this.scene.pause();

    //         // be sure to render any changes from the scene before stopping refresh
    //         this.display.renderScene(this.scene);

    //         // then stop render/event loop
    //         this.stopScene();
    //     } else {
    //         console.log('un-pausing game');
    //         this.running = true;
    //         this.scene.unpause();
    //         this._runSceneLoop();
    //         this._renderSceneLoop();
    //     }
    // }

    /**
     * Starts the current scene
     *
     * @param {Boolean=false} resetMap resets the map associated to the map
     *
     * - loads the scene if not already loaded
     * - once it's loaded calls scene.start() and start both event & render loops
     *
     */
    // startScene(resetMap = false) {
    //     console.log('[Game] startScene');

    //     if (this.scene) {
    //         console.log('[Game] loading scene');
    //         this.scene.load().then(() => {
    //             console.log('[Game] Scene', this.scene.name, 'loaded: starting run & render loops');
    //             this.scene.start(resetMap);
    //             this._runSceneLoop();
    //             this._renderSceneLoop();
    //         });
    //     } else {
    //         console.log('[Game] nothing to start: no scene selected!!');
    //     }
    // }

    /**
     * Stops current scene from running: this will both halt render & event loops
     *
     * Use Game.togglePauseGame() to temporarly pause a game
     *
     * @private
     *
     */
    _stopSceneLoops() {
        this.running = false;

        console.log('[Game] Scene stopped, stopping run & render loops');

        if (this.animFrame) {
            window.cancelAnimationFrame(this.animFrame);
            this.animFrame = null;
        }
        if (this.timeout) {
            clearTimeout(this.timeout);
            this.timeout = null;
        }
    }

}

export default Game;
<|endoftext|>


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

/**
 * GaugeNode is a circular gauge that depicts some dynamic value.
 * This was originally ported from the speedometer node in forces-and-motion-basics.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author John Blanco (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Range = require( 'DOT/Range' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Shape = require( 'KITE/Shape' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} valueProperty which is portrayed
   * @param {string} label label to display (scaled to fit if necessary)
   * @param {Range} range
   * @param {Object} [options]
   * @constructor
   */
  function GaugeNode( valueProperty, label, range, options ) {
    options = _.extend( {
      // Defaults
      radius: 67,
      backgroundFill: 'white',
      backgroundStroke: 'rgb( 85, 85, 85 )',
      backgroundLineWidth: 2,
      anglePerTick: Math.PI * 2 / 4 / 8,

      // defines max width of the label, relative to the radius
      maxLabelWidthScale: 1.3,

      // 8 ticks goes to 9 o'clock (on the left side), and two more ticks appear below that mark.
      // The ticks are duplicated for the right side, and one tick appears in the middle at the top
      numTicks: ( 8 + 2 ) * 2 + 1,

      majorTickLength: 10,
      minorTickLength: 5,
      majorTickLineWidth: 2,
      minorTickLineWidth: 1,

      // Determines whether the gauge will be updated when the value changes.
      // Use this to (for example) disable updates while a gauge is not visible.
      updateEnabledProperty: new Property( true ),
      tandem: Tandem.required
    }, options );

    assert && assert( range instanceof Range, 'range must be of type Range:' + range );
    assert && assert( options.numTicks * options.anglePerTick <= 2 * Math.PI,
      'options.numTicks * options.anglePerTick must be <= 2 * Math.PI. numTicks: ' + options.numTicks +
      ', anglePerTick: ' + options.anglePerTick
    );

    Node.call( this );

    var tandem = options.tandem;

    this.addChild( new Circle( options.radius, {
      fill: options.backgroundFill,
      stroke: options.backgroundStroke,
      lineWidth: options.backgroundLineWidth
    } ) );

    var foregroundNode = new Node( { pickable: false, tandem: tandem.createTandem( 'foregroundNode' ) } );
    this.addChild( foregroundNode );

    var needle = new Path( Shape.lineSegment( 0, 0, options.radius, 0 ), {
      stroke: 'red',
      lineWidth: 3
    } );
    foregroundNode.addChild( needle );

    var labelNode = new Text( label, {
      font: new PhetFont( 20 ),
      maxWidth: options.radius * options.maxLabelWidthScale,
      tandem: tandem.createTandem( 'labelNode' )
    } ).mutate( {
      centerX: 0,
      centerY: -options.radius / 3
    } );
    foregroundNode.addChild( labelNode );

    var pin = new Circle( 2, { fill: 'black' } );
    foregroundNode.addChild( pin );

    var totalAngle = ( options.numTicks - 1 ) * options.anglePerTick;
    var startAngle = -1 / 2 * Math.PI - totalAngle / 2;
    var endAngle = startAngle + totalAngle;

    var scratchMatrix = new Matrix3();

    var updateNeedle = function() {
      if ( options.updateEnabledProperty.get() ) {
        if ( typeof( valueProperty.get() ) === 'number' ) {
          needle.visible = true;
          var needleAngle = Util.linear( range.min, range.max, startAngle, endAngle, Math.abs( valueProperty.get() ) );

          // 2d rotation, but reusing our matrix above
          needle.setMatrix( scratchMatrix.setToRotationZ( needleAngle ) );
        }
        else {

          // Hide the needle if there is no value number value to portray.
          needle.visible = false;
        }
      }
    };

    valueProperty.link( updateNeedle );
    options.updateEnabledProperty.link( updateNeedle );

    // Render all of the ticks into two layers (since they have different strokes)
    // see https://github.com/phetsims/energy-skate-park-basics/issues/208
    var bigTicksShape = new Shape();
    var smallTicksShape = new Shape();

    // Add the tick marks
    for ( var i = 0; i < options.numTicks; i++ ) {
      var tickAngle = i * options.anglePerTick + startAngle;

      var tickLength = i % 2 === 0 ? options.majorTickLength : options.minorTickLength;
      var x1 = ( options.radius - tickLength ) * Math.cos( tickAngle );
      var y1 = ( options.radius - tickLength ) * Math.sin( tickAngle );
      var x2 = options.radius * Math.cos( tickAngle );
      var y2 = options.radius * Math.sin( tickAngle );
      if ( i % 2 === 0 ) {
        bigTicksShape.moveTo( x1, y1 );
        bigTicksShape.lineTo( x2, y2 );
      }
      else {
        smallTicksShape.moveTo( x1, y1 );
        smallTicksShape.lineTo( x2, y2 );
      }
    }

    foregroundNode.addChild( new Path( bigTicksShape, { stroke: 'gray', lineWidth: options.majorTickLineWidth } ) );
    foregroundNode.addChild( new Path( smallTicksShape, { stroke: 'gray', lineWidth: options.minorTickLineWidth } ) );
    this.mutate( options );

    // @private
    this.disposeGaugeNode = function() {
      if ( valueProperty.hasListener( updateNeedle ) ) {
        valueProperty.unlink( updateNeedle );
      }
      if ( options.updateEnabledProperty.hasListener( updateNeedle ) ) {
        options.updateEnabledProperty.unlink( updateNeedle );
      }
    };
  }

  sceneryPhet.register( 'GaugeNode', GaugeNode );

  return inherit( Node, GaugeNode, {

    // @public
    dispose: function() {
      this.disposeGaugeNode();
      Node.prototype.dispose.call( this );
    }
  } );
} );<commit_msg>Make default radius be a nice number, see https://github.com/phetsims/scenery-phet/issues/432<commit_after>// Copyright 2013-2018, University of Colorado Boulder

/**
 * GaugeNode is a circular gauge that depicts some dynamic value.
 * This was originally ported from the speedometer node in forces-and-motion-basics.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author John Blanco (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix3 = require( 'DOT/Matrix3' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Property = require( 'AXON/Property' );
  var Range = require( 'DOT/Range' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Shape = require( 'KITE/Shape' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} valueProperty which is portrayed
   * @param {string} label label to display (scaled to fit if necessary)
   * @param {Range} range
   * @param {Object} [options]
   * @constructor
   */
  function GaugeNode( valueProperty, label, range, options ) {
    options = _.extend( {
      // Defaults
      radius: 100,
      backgroundFill: 'white',
      backgroundStroke: 'rgb( 85, 85, 85 )',
      backgroundLineWidth: 2,
      anglePerTick: Math.PI * 2 / 4 / 8,

      // defines max width of the label, relative to the radius
      maxLabelWidthScale: 1.3,

      // 8 ticks goes to 9 o'clock (on the left side), and two more ticks appear below that mark.
      // The ticks are duplicated for the right side, and one tick appears in the middle at the top
      numTicks: ( 8 + 2 ) * 2 + 1,

      majorTickLength: 10,
      minorTickLength: 5,
      majorTickLineWidth: 2,
      minorTickLineWidth: 1,

      // Determines whether the gauge will be updated when the value changes.
      // Use this to (for example) disable updates while a gauge is not visible.
      updateEnabledProperty: new Property( true ),
      tandem: Tandem.required
    }, options );

    assert && assert( range instanceof Range, 'range must be of type Range:' + range );
    assert && assert( options.numTicks * options.anglePerTick <= 2 * Math.PI,
      'options.numTicks * options.anglePerTick must be <= 2 * Math.PI. numTicks: ' + options.numTicks +
      ', anglePerTick: ' + options.anglePerTick
    );

    Node.call( this );

    var tandem = options.tandem;

    this.addChild( new Circle( options.radius, {
      fill: options.backgroundFill,
      stroke: options.backgroundStroke,
      lineWidth: options.backgroundLineWidth
    } ) );

    var foregroundNode = new Node( { pickable: false, tandem: tandem.createTandem( 'foregroundNode' ) } );
    this.addChild( foregroundNode );

    var needle = new Path( Shape.lineSegment( 0, 0, options.radius, 0 ), {
      stroke: 'red',
      lineWidth: 3
    } );
    foregroundNode.addChild( needle );

    var labelNode = new Text( label, {
      font: new PhetFont( 20 ),
      maxWidth: options.radius * options.maxLabelWidthScale,
      tandem: tandem.createTandem( 'labelNode' )
    } ).mutate( {
      centerX: 0,
      centerY: -options.radius / 3
    } );
    foregroundNode.addChild( labelNode );

    var pin = new Circle( 2, { fill: 'black' } );
    foregroundNode.addChild( pin );

    var totalAngle = ( options.numTicks - 1 ) * options.anglePerTick;
    var startAngle = -1 / 2 * Math.PI - totalAngle / 2;
    var endAngle = startAngle + totalAngle;

    var scratchMatrix = new Matrix3();

    var updateNeedle = function() {
      if ( options.updateEnabledProperty.get() ) {
        if ( typeof( valueProperty.get() ) === 'number' ) {
          needle.visible = true;
          var needleAngle = Util.linear( range.min, range.max, startAngle, endAngle, Math.abs( valueProperty.get() ) );

          // 2d rotation, but reusing our matrix above
          needle.setMatrix( scratchMatrix.setToRotationZ( needleAngle ) );
        }
        else {

          // Hide the needle if there is no value number value to portray.
          needle.visible = false;
        }
      }
    };

    valueProperty.link( updateNeedle );
    options.updateEnabledProperty.link( updateNeedle );

    // Render all of the ticks into two layers (since they have different strokes)
    // see https://github.com/phetsims/energy-skate-park-basics/issues/208
    var bigTicksShape = new Shape();
    var smallTicksShape = new Shape();

    // Add the tick marks
    for ( var i = 0; i < options.numTicks; i++ ) {
      var tickAngle = i * options.anglePerTick + startAngle;

      var tickLength = i % 2 === 0 ? options.majorTickLength : options.minorTickLength;
      var x1 = ( options.radius - tickLength ) * Math.cos( tickAngle );
      var y1 = ( options.radius - tickLength ) * Math.sin( tickAngle );
      var x2 = options.radius * Math.cos( tickAngle );
      var y2 = options.radius * Math.sin( tickAngle );
      if ( i % 2 === 0 ) {
        bigTicksShape.moveTo( x1, y1 );
        bigTicksShape.lineTo( x2, y2 );
      }
      else {
        smallTicksShape.moveTo( x1, y1 );
        smallTicksShape.lineTo( x2, y2 );
      }
    }

    foregroundNode.addChild( new Path( bigTicksShape, { stroke: 'gray', lineWidth: options.majorTickLineWidth } ) );
    foregroundNode.addChild( new Path( smallTicksShape, { stroke: 'gray', lineWidth: options.minorTickLineWidth } ) );
    this.mutate( options );

    // @private
    this.disposeGaugeNode = function() {
      if ( valueProperty.hasListener( updateNeedle ) ) {
        valueProperty.unlink( updateNeedle );
      }
      if ( options.updateEnabledProperty.hasListener( updateNeedle ) ) {
        options.updateEnabledProperty.unlink( updateNeedle );
      }
    };
  }

  sceneryPhet.register( 'GaugeNode', GaugeNode );

  return inherit( Node, GaugeNode, {

    // @public
    dispose: function() {
      this.disposeGaugeNode();
      Node.prototype.dispose.call( this );
    }
  } );
} );<|endoftext|>


<commit_before>/*global module:false*/
module.exports = function (grunt) {

    grunt.initConfig({
        meta: {
            banner: "/*! Mopidy.js - built " +
                "<%= grunt.template.today('yyyy-mm-dd') %>\n" +
                " * http://www.mopidy.com/\n" +
                " * Copyright (c) <%= grunt.template.today('yyyy') %> " +
                "Stein Magnus Jodal and contributors\n" +
                " * Licensed under the Apache License, Version 2.0 */\n",
            files: {
                own: ["Gruntfile.js", "src/**/*.js", "test/**/*-test.js"],
                concat: "../mopidy/frontends/http/data/mopidy.js",
                minified: "../mopidy/frontends/http/data/mopidy.min.js"
            }
        },
        buster: {
            test: {
                config: "buster.js"
            }
        },
        concat: {
            options: {
                banner: "<%= meta.banner %>",
                stripBanners: true
            },
            all: {
                files: {
                    "<%= meta.files.concat %>": [
                        "lib/bane-*.js",
                        "lib/when-*.js",
                        "src/mopidy.js"
                    ]
                }
            }
        },
        jshint: {
            options: {
                curly: true,
                eqeqeq: true,
                immed: true,
                indent: 4,
                latedef: true,
                newcap: true,
                noarg: true,
                sub: true,
                quotmark: "double",
                undef: true,
                unused: true,
                eqnull: true,
                browser: true,
                devel: true,
                globals: {}
            },
            files: "<%= meta.files.own %>"
        },
        uglify: {
            options: {
                banner: "<%= meta.banner %>"
            },
            all: {
                files: {
                    "<%= meta.files.minified %>": ["<%= meta.files.concat %>"]
                }
            }
        },
        watch: {
            files: "<%= meta.files.own %>",
            tasks: ["default"]
        }
    });

    grunt.registerTask("test", ["jshint", "buster"]);
    grunt.registerTask("build", ["test", "concat", "uglify"]);
    grunt.registerTask("default", ["build"]);

    grunt.loadNpmTasks("grunt-buster");
    grunt.loadNpmTasks("grunt-contrib-concat");
    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-contrib-watch");
};
<commit_msg>js: Remove redundant config not working on Node 0.10<commit_after>/*global module:false*/
module.exports = function (grunt) {

    grunt.initConfig({
        meta: {
            banner: "/*! Mopidy.js - built " +
                "<%= grunt.template.today('yyyy-mm-dd') %>\n" +
                " * http://www.mopidy.com/\n" +
                " * Copyright (c) <%= grunt.template.today('yyyy') %> " +
                "Stein Magnus Jodal and contributors\n" +
                " * Licensed under the Apache License, Version 2.0 */\n",
            files: {
                own: ["Gruntfile.js", "src/**/*.js", "test/**/*-test.js"],
                concat: "../mopidy/frontends/http/data/mopidy.js",
                minified: "../mopidy/frontends/http/data/mopidy.min.js"
            }
        },
        concat: {
            options: {
                banner: "<%= meta.banner %>",
                stripBanners: true
            },
            all: {
                files: {
                    "<%= meta.files.concat %>": [
                        "lib/bane-*.js",
                        "lib/when-*.js",
                        "src/mopidy.js"
                    ]
                }
            }
        },
        jshint: {
            options: {
                curly: true,
                eqeqeq: true,
                immed: true,
                indent: 4,
                latedef: true,
                newcap: true,
                noarg: true,
                sub: true,
                quotmark: "double",
                undef: true,
                unused: true,
                eqnull: true,
                browser: true,
                devel: true,
                globals: {}
            },
            files: "<%= meta.files.own %>"
        },
        uglify: {
            options: {
                banner: "<%= meta.banner %>"
            },
            all: {
                files: {
                    "<%= meta.files.minified %>": ["<%= meta.files.concat %>"]
                }
            }
        },
        watch: {
            files: "<%= meta.files.own %>",
            tasks: ["default"]
        }
    });

    grunt.registerTask("test", ["jshint", "buster"]);
    grunt.registerTask("build", ["test", "concat", "uglify"]);
    grunt.registerTask("default", ["build"]);

    grunt.loadNpmTasks("grunt-buster");
    grunt.loadNpmTasks("grunt-contrib-concat");
    grunt.loadNpmTasks("grunt-contrib-jshint");
    grunt.loadNpmTasks("grunt-contrib-uglify");
    grunt.loadNpmTasks("grunt-contrib-watch");
};
<|endoftext|>


<commit_before>var LineChart = {
    labels: ["1.0", "2.0", "3.0", "4.0", "5.0"],
    datasets: [

    /*
    {
        fillColor: "rgba(151,249,190,0.5)",
        strokeColor: "rgba(255,255,255,1)",
        pointColor: "rgba(220,220,220,1)",
        pointStrokeColor: "#fff",
        data: [10, 20, 30, 40, 50]
    },*/

    {
        fillColor: "rgba(252,147,65,0.5)",
        strokeColor: "rgba(255,255,255,1)",
        pointColor: "rgba(173,173,173,1)",
        pointStrokeColor: "#fff",
        data: [28, 68, 40, 19, 96]
    }

    ]
}

var myLineChart = new Chart(document.getElementById("canvasLine").getContext("2d")).Line(LineChart, { scaleFontSize: 13, scaleFontColor: "#ffa45e", responsive: true });

<commit_msg>fixed data points<commit_after>var LineChart = {
    labels: ["2.0", "4.0", "6.0", "8.0", "10.0"],
    datasets: [

    /*
    {
        fillColor: "rgba(151,249,190,0.5)",
        strokeColor: "rgba(255,255,255,1)",
        pointColor: "rgba(220,220,220,1)",
        pointStrokeColor: "#fff",
        data: [10, 20, 30, 40, 50]
    },*/

    {
        fillColor: "rgba(252,147,65,0.5)",
        strokeColor: "rgba(255,255,255,1)",
        pointColor: "rgba(173,173,173,1)",
        pointStrokeColor: "#fff",
        data: [5, 17, 37, 65, 101]
    }

    ]
}

var myLineChart = new Chart(document.getElementById("canvasLine").getContext("2d")).Line(LineChart, { scaleFontSize: 13, scaleFontColor: "#ffa45e", responsive: true });

<|endoftext|>