<commit_before>/* jshint node:true */
'use strict';

var path = require('path'),
	FS = require('q-io/fs'),
	Q = require('q'),
	Mithril = require('mithril'),
	mock = require('./mock.js');

var page,
	routesFname,
	app,
	routes,
	def,
	mod,
	localRoot,
	remoteRoot,
	script,
	placeholder,
	rxApp,
	routeParams = {},
	isoName,
	IsoModules = {},
	modes = {
		pathname: "",
		hash: "#",
		search: "?"
	},
	routeMode;

// Attach the mock DOM to Mithril
Mithril.deps(mock.window);

// Redefine the original `m.route.param`
// to read from my collection of parameters
// collected on the server side.
Mithril.route.param = function (key) {
	console.log('param', key, routeParams[key]);
	return routeParams[key];
};

// Monkey patch the original `m.render` method
// to read the HTML from the mock DOM once it is done rendering.

Mithril.render = (function (original) {

	var logNode = function (node) {
		var prop, val,
			style, styles = [],
			html = '';

		if (!node.nodeName && node.nodeValue !== undefined) {
			// For text nodes, I return the uppercase text
			// so that you can tell the parts generated at the server
			// from the normal lowercase of the actual app when run on the client
			return node.nodeValue.toUpperCase();
		}
		html += '<' + node.nodeName;
		for (prop in node) {
			val = node[prop];

			// Ignore functions, those will be revived on the client side.
			if (typeof val == 'function') continue;
			switch (prop) {
			case 'nodeName':
			case 'parentNode':
			case 'childNodes':
			case 'pathname':
			case 'search':
				continue;
			case 'checked':
				if (val == 'false') continue;
				break;
			case 'href':
				val = node.pathname;
				break;
			case 'className':
				prop = 'class';
				break;
			case 'style':
				if (val) {
					for (style in val) {
						if (val[style]) {
							styles.push(style + ': ' + val[style]);
						}
					}
					if (!styles.length) continue;
					val = styles.join(';');
				}
				break;
			}
			html += ' ' + prop + '="' + val.replace('"', '\\"') + '"';
		}

		if (node.childNodes.length) {
			html += '>' + node.childNodes.reduce(function (prev, node) {
				return prev + logNode(node);
			}, '') + '</' + node.nodeName + '>';
		} else {
			// I don't know why Mithril assigns the content of textareas
			// to its value attribute instead of the innerHTML property.
			// Since it doesn't have children, the closing tag has to be forced.
			if (node.nodeName == 'TEXTAREA') {
				html += '></TEXTAREA>';
			} else {
				html += '/>';
			}
		}
		return html;
	};

	// This is the monkey-patching part:
	return function () {

		var result = original.apply(this, arguments);
		mock.html = mock.window.document.body.childNodes.reduce(function (prev, node) {
			return prev + logNode(node);
		}, '');
		return result;
	};
})(Mithril.render);

//var wAttrList = [];
//Mithril.withAttr = (function (original) {
//	return function () {
//		console.log('withAttr', arguments[0], arguments[1].toString());
//		var result = original.apply(this, arguments);
//		wAttrList.push({
//			attr: arguments[0],
//			fn: arguments[1],
//			wrap: result
//		});
//		return result;
//	};
//
//})(Mithril.withAttr);

module.exports = function (config) {

	config = config || {};
	// Folders
	// Where the application resides
	app = config.app || './app';
	// The root for the static files
	localRoot = config.localRoot || 'client';
	// The root as seen from the far side
	remoteRoot = config.remoteRoot || '/';

	// Files:
	// The home page, located within `localRoot`
	page = config.page || 'index.html';
	// The placeholder for the body within that home page
	placeholder = config.placeholder || '{{body}}';
	// The file name for the routes configuration, located in `app`
	routesFname = config.routes || 'routes.js';
	// The name of the script file where all the application code is collected, located in `app`
	script = config.script || '__.js';

	// The name of the global object where all the modules are collected
	isoName = config.isoName || 'IsoModules';
	// The route mode as per m.routes.mode
	routeMode = config.routeMode || 'search';

	mock.window.location.pathname = localRoot;
	rxApp = new RegExp(path.join('/', app, '/') + '(.*)');

	// Read the routes file for the modified call to
	// `m.route` or `m.module`

	var r = require(path.resolve(app, routesFname))({
		route: function (d, r) {
			def = d;
			routes = r;
		},
		module: function (m) {
			mod = m;
		}
	});

	// Collect everything that needs to be sent to the client into one script.
	var out = path.resolve(app, script);

	// First, send Mithril itself
	FS.copy(require.resolve('mithril'), out).then(function () {
		// Add the declaration of the variable that will hold the collection of modules:
		return FS.append(out, ';\n' + isoName + ' = {};');
	}).then(function () {
		// Add the reviver
		return FS.read(require.resolve('./reviver.js')).then(function (contents) {
			return FS.append(out, contents);
		});
	}).then(function () {
		// Add all the modules declared
		// skipping over the routes file and the file where the scripts are being collected
		return FS.listTree(app, function (fullPath) {
			return !(
				path.extname(fullPath) != '.js' ||
				path.basename(fullPath) == script ||
				path.basename(fullPath) == routesFname
			);
		}).then(function (files) {
			return Q.all(files.map(function (file) {
				var module = require(path.resolve('./', file));
				module(Mithril, IsoModules);
				return FS.append(out, '\n;(' + module.toString() + ')(Mithril, ' + isoName + ')');
			}));
		});
	}).then(function () {
		// Add either the `m.module` or `m.route` call at the end.
		if (mod) {
			return FS.append(out, '\n;Mithril.module(document.body,' + isoName + '.' + mod + ');');
		}
		if (routes) {
			var rs = [],
				r;
			for (r in routes) {
				rs.push('"' + r + '":' + isoName + '.' + routes[r]);
			}
			return FS.append(out, '\n;Mithril.route(document.body, "' + def + '", {' + rs.join(',') + '});');
		}
	});

	//Entry point to the middleware
	return function (req, res, next) {

		console.log('-----------------------');
		console.log('req.url:', req.url);

		// Skip over the requests I don't need to handle
		var match = rxApp.exec(req.url);
		if (match) {
			if (match[1] == script) {
				console.log('rxmatch == script', match[1]);
				res.sendfile(path.join(app, script));
				return;
			}
			console.log('rxmatch else', match[1]);
			res.send('(' + require(path.resolve(app, match[1])).toString() + ')(Mithril)');
		}

		var accept = req.headers.accept;
		if (
			req.method !== 'GET' ||
			accept.indexOf('application/json') === 0 || !(accept.indexOf('text/html') !== -1 || accept.indexOf('*/*') !== -1)
		) {
			console.log('first ignore');
			return void next();
		}

		if (req.url.indexOf('.') !== -1) {
			console.log('second ignore');
			return void next();
		}

		// This is a copy of the function by the same name
		// in Mithril which is inaccessible from outside.
		var routeByValue = function (router, path) {
			path = path.replace(/^\/\?/, '');
			routeParams = {};
			var replacer = function () {
				var keys = route.match(/:[^\/]+/g);
				var values = [].slice.call(arguments, 1, -2);
				for (var i = 0; i < keys.length; i++) routeParams[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]);
				return router[route];
			};
			for (var route in router) {
				if (route == path) return router[route];

				var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "$");

				if (matcher.test(path)) {
					return path.replace(matcher, replacer);
				}
			}
		};

		// If the routes file contained a routes declaration
		// find the module to be called and extract the params.
		if (routes) {
			mod = routeByValue(routes, req.url) || routes[def];
			console.log('rbv', mod, routeParams);
		}
		// If the routes file had a single call to `m.module`
		// then it would already be stored in `mod`
		if (!mod) {
			res.send(500, 'Missing routes or single module declaration');
		}

		// Call that module, with the body of the fake DOM as its root.
		Mithril.module(mock.window.document.body, IsoModules[mod]);

		// Read the template for the page normally the `index.html` file.
		FS.read(path.join(localRoot, page)).then(function (data) {
			// Locate the placeholder for the server-side content
			var i = data.indexOf(placeholder);
			// If the placeholder is not found, return the page as-is
			if (i == -1) return void res.send(data);
			// avoid 304 - Not Modified
			res.setHeader('Last-Modified', (new Date()).toUTCString());

			//			console.log('\n\nvar cellCache = ', JSON.stringify(Mithril.$cellCache[0], function (key, value) {
			//				if (key == 'nodes') value = value.length + ' [' + value.map(function (node) { return node.nodeName || node.nodeValue;}) + ']';
			//				return value;
			//			}));
			//			var inController = function (value) {
			//				var m, mod = Mithril.$controller;
			//				for (m in mod) {
			//					if (mod[m] == value) {
			//						return m;
			//					}
			//				}
			//			};
			//
			//			console.log('\n\nvar cell = ', JSON.stringify(Mithril.$cell, function (key, value) {
			//				var m, obj;
			//				if (typeof value == 'function') {
			//
			//					for (m in Mithril) {
			//						if (Mithril[m] == value) {
			//							value = '* Mithril: ' + m;
			//						}
			//					}
			//
			//					wAttrList.forEach(function (entry) {
			//						if (entry.wrap == value) {
			//							m = inController(entry.fn);
			//							value = '* withAttr ' + entry.attr + (m ? ', controller: ' + m : '');
			//						}
			//					});
			//
			//					obj = IsoModules[mod];
			//					for (m in obj) {
			//						if (obj[m] == value) {
			//							value = '* IsoModules[' + mod + ']: ' + m;
			//						}
			//					}
			//					m = inController(value);
			//					if (m) value = '* controller: ' + m;
			//
			//					value = value.toString();
			//				}
			//				if (key == 'config' && value == Mithril.route) value = 'm.route()';
			//				return value;
			//			}));

			// Send the HTML produced server-side
			// inserted where the placeholder should have been.
			res.send(
				data.substr(0, i) +
				// send the HTML
				mock.html +
				// send code to fix the URL on the client
				(
					req.url.length > 2 && req.url.substr(0, 2) != '/?' ?
					'\n<script>window.history.replaceState(null, window.document.title, "' +
					remoteRoot + modes[routeMode] + req.url + '")</script>' :
					''
				) +
				// Send the script containing Mithril, the app, the reviver and the app.
				'\n<script src="' + path.join('/', app, script) + '" async></script>' +
				data.substr(i + placeholder.length)
			);

		});
	};
};<commit_msg>Added a means to add JS client-side components<commit_after>/* jshint node:true */
'use strict';

var path = require('path'),
	FS = require('q-io/fs'),
	Q = require('q'),
	Mithril = require('mithril'),
	mock = require('./mock.js');

var page,
	routesFname,
	app,
	components,
	routes,
	def,
	mod,
	localRoot,
	remoteRoot,
	script,
	placeholder,
	rxApp,
	routeParams = {},
	isoName,
	IsoModules = {},
	modes = {
		pathname: "",
		hash: "#",
		search: "?"
	},
	routeMode;

// Attach the mock DOM to Mithril
Mithril.deps(mock.window);

// Redefine the original `m.route.param`
// to read from my collection of parameters
// collected on the server side.
Mithril.route.param = function (key) {
	console.log('param', key, routeParams[key]);
	return routeParams[key];
};

// Monkey patch the original `m.render` method
// to read the HTML from the mock DOM once it is done rendering.

Mithril.render = (function (original) {

	var logNode = function (node) {
		var prop, val,
			style, styles = [],
			html = '';

		if (!node.nodeName && node.nodeValue !== undefined) {
			// For text nodes, I return the uppercase text
			// so that you can tell the parts generated at the server
			// from the normal lowercase of the actual app when run on the client
			return node.nodeValue.toUpperCase();
		}
		html += '<' + node.nodeName;
		for (prop in node) {
			val = node[prop];

			// Ignore functions, those will be revived on the client side.
			if (typeof val == 'function') continue;
			switch (prop) {
			case 'nodeName':
			case 'parentNode':
			case 'childNodes':
			case 'pathname':
			case 'search':
				continue;
			case 'checked':
				if (val == 'false') continue;
				break;
			case 'href':
				val = node.pathname;
				break;
			case 'className':
				prop = 'class';
				break;
			case 'style':
				if (val) {
					for (style in val) {
						if (val[style]) {
							styles.push(style + ': ' + val[style]);
						}
					}
					if (!styles.length) continue;
					val = styles.join(';');
				}
				break;
			}
			html += ' ' + prop + '="' + val.replace('"', '\\"') + '"';
		}

		if (node.childNodes.length) {
			html += '>' + node.childNodes.reduce(function (prev, node) {
				return prev + logNode(node);
			}, '') + '</' + node.nodeName + '>';
		} else {
			// I don't know why Mithril assigns the content of textareas
			// to its value attribute instead of the innerHTML property.
			// Since it doesn't have children, the closing tag has to be forced.
			if (node.nodeName == 'TEXTAREA') {
				html += '></TEXTAREA>';
			} else {
				html += '/>';
			}
		}
		return html;
	};

	// This is the monkey-patching part:
	return function () {

		var result = original.apply(this, arguments);
		mock.html = mock.window.document.body.childNodes.reduce(function (prev, node) {
			return prev + logNode(node);
		}, '');
		return result;
	};
})(Mithril.render);

//var wAttrList = [];
//Mithril.withAttr = (function (original) {
//	return function () {
//		console.log('withAttr', arguments[0], arguments[1].toString());
//		var result = original.apply(this, arguments);
//		wAttrList.push({
//			attr: arguments[0],
//			fn: arguments[1],
//			wrap: result
//		});
//		return result;
//	};
//
//})(Mithril.withAttr);

module.exports = function (config) {

	config = config || {};
	// Folders
	// Where the application resides
	app = config.app || './app';

	// The source for extra client-side components
	components = config.components || './components';

	// The root for the static files
	localRoot = config.localRoot || 'client';
	// The root as seen from the far side
	remoteRoot = config.remoteRoot || '/';

	// Files:
	// The home page, located within `localRoot`
	page = config.page || 'index.html';
	// The placeholder for the body within that home page
	placeholder = config.placeholder || '{{body}}';
	// The file name for the routes configuration, located in `app`
	routesFname = config.routes || 'routes.js';
	// The name of the script file where all the application code is collected, located in `app`
	script = config.script || '__.js';

	// The name of the global object where all the modules are collected
	isoName = config.isoName || 'IsoModules';
	// The route mode as per m.routes.mode
	routeMode = config.routeMode || 'search';

	mock.window.location.pathname = localRoot;
	rxApp = new RegExp(path.join('/', app, '/') + '(.*)');

	// Read the routes file for the modified call to
	// `m.route` or `m.module`

	var r = require(path.resolve(app, routesFname))({
		route: function (d, r) {
			def = d;
			routes = r;
		},
		module: function (m) {
			mod = m;
		}
	});

	// Collect everything that needs to be sent to the client into one script.
	var out = path.resolve(app, script);

	// First, send Mithril itself
	FS.copy(require.resolve('mithril'), out).then(function () {
		// Add the declaration of the variable that will hold the collection of modules:
		return FS.append(out, ';\n' + isoName + ' = {};');
	}).then(function () {
		// Add the reviver
		return FS.read(require.resolve('./reviver.js')).then(function (contents) {
			return FS.append(out, contents);
		});
	}).then(function () {
		// Add all the components
		return FS.listTree(components, function (fullPath) {
			return path.extname(fullPath) == '.js';
		}).then(function (files) {
			return Q.all(files.map(function (file) {
				return FS.read(file).then(function (contents) {
					return FS.append(out, contents);
				});
			}));
		});
	}).then(function () {
		// Add all the modules declared
		// skipping over the routes file and the file where the scripts are being collected
		return FS.listTree(app, function (fullPath) {
			return !(
				path.extname(fullPath) != '.js' ||
				path.basename(fullPath) == script ||
				path.basename(fullPath) == routesFname
			);
		}).then(function (files) {
			return Q.all(files.map(function (file) {
				var module = require(path.resolve('./', file));
				module(Mithril, IsoModules);
				return FS.append(out, '\n;(' + module.toString() + ')(Mithril, ' + isoName + ')');
			}));
		});
	}).then(function () {
		// Add either the `m.module` or `m.route` call at the end.
		if (mod) {
			return FS.append(out, '\n;Mithril.module(document.body,' + isoName + '.' + mod + ');');
		}
		if (routes) {
			var rs = [],
				r;
			for (r in routes) {
				rs.push('"' + r + '":' + isoName + '.' + routes[r]);
			}
			return FS.append(out, '\n;Mithril.route(document.body, "' + def + '", {' + rs.join(',') + '});');
		}
	});

	//Entry point to the middleware
	return function (req, res, next) {

		console.log('-----------------------');
		console.log('req.url:', req.url);

		// Skip over the requests I don't need to handle
		var match = rxApp.exec(req.url);
		if (match) {
			if (match[1] == script) {
				console.log('rxmatch == script', match[1]);
				res.sendfile(path.join(app, script));
				return;
			}
			console.log('rxmatch else', match[1]);
			res.send('(' + require(path.resolve(app, match[1])).toString() + ')(Mithril)');
		}

		var accept = req.headers.accept;
		if (
			req.method !== 'GET' ||
			accept.indexOf('application/json') === 0 || !(accept.indexOf('text/html') !== -1 || accept.indexOf('*/*') !== -1)
		) {
			console.log('first ignore');
			return void next();
		}

		if (req.url.indexOf('.') !== -1) {
			console.log('second ignore');
			return void next();
		}

		// This is a copy of the function by the same name
		// in Mithril which is inaccessible from outside.
		var routeByValue = function (router, path) {
			path = path.replace(/^\/\?/, '');
			routeParams = {};
			var replacer = function () {
				var keys = route.match(/:[^\/]+/g);
				var values = [].slice.call(arguments, 1, -2);
				for (var i = 0; i < keys.length; i++) routeParams[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i]);
				return router[route];
			};
			for (var route in router) {
				if (route == path) return router[route];

				var matcher = new RegExp("^" + route.replace(/:[^\/]+?\.{3}/g, "(.*?)").replace(/:[^\/]+/g, "([^\\/]+)") + "$");

				if (matcher.test(path)) {
					return path.replace(matcher, replacer);
				}
			}
		};

		// If the routes file contained a routes declaration
		// find the module to be called and extract the params.
		if (routes) {
			mod = routeByValue(routes, req.url) || routes[def];
			console.log('rbv', mod, routeParams);
		}
		// If the routes file had a single call to `m.module`
		// then it would already be stored in `mod`
		if (!mod) {
			res.send(500, 'Missing routes or single module declaration');
		}

		// Call that module, with the body of the fake DOM as its root.
		Mithril.module(mock.window.document.body, IsoModules[mod]);

		// Read the template for the page normally the `index.html` file.
		FS.read(path.join(localRoot, page)).then(function (data) {
			// Locate the placeholder for the server-side content
			var i = data.indexOf(placeholder);
			// If the placeholder is not found, return the page as-is
			if (i == -1) return void res.send(data);
			// avoid 304 - Not Modified
			res.setHeader('Last-Modified', (new Date()).toUTCString());

			//			console.log('\n\nvar cellCache = ', JSON.stringify(Mithril.$cellCache[0], function (key, value) {
			//				if (key == 'nodes') value = value.length + ' [' + value.map(function (node) { return node.nodeName || node.nodeValue;}) + ']';
			//				return value;
			//			}));
			//			var inController = function (value) {
			//				var m, mod = Mithril.$controller;
			//				for (m in mod) {
			//					if (mod[m] == value) {
			//						return m;
			//					}
			//				}
			//			};
			//
			//			console.log('\n\nvar cell = ', JSON.stringify(Mithril.$cell, function (key, value) {
			//				var m, obj;
			//				if (typeof value == 'function') {
			//
			//					for (m in Mithril) {
			//						if (Mithril[m] == value) {
			//							value = '* Mithril: ' + m;
			//						}
			//					}
			//
			//					wAttrList.forEach(function (entry) {
			//						if (entry.wrap == value) {
			//							m = inController(entry.fn);
			//							value = '* withAttr ' + entry.attr + (m ? ', controller: ' + m : '');
			//						}
			//					});
			//
			//					obj = IsoModules[mod];
			//					for (m in obj) {
			//						if (obj[m] == value) {
			//							value = '* IsoModules[' + mod + ']: ' + m;
			//						}
			//					}
			//					m = inController(value);
			//					if (m) value = '* controller: ' + m;
			//
			//					value = value.toString();
			//				}
			//				if (key == 'config' && value == Mithril.route) value = 'm.route()';
			//				return value;
			//			}));

			// Send the HTML produced server-side
			// inserted where the placeholder should have been.
			res.send(
				data.substr(0, i) +
				// send the HTML
				mock.html +
				// send code to fix the URL on the client
				(
					req.url.length > 2 && req.url.substr(0, 2) != '/?' ?
					'\n<script>window.history.replaceState(null, window.document.title, "' +
					remoteRoot + modes[routeMode] + req.url + '")</script>' :
					''
				) +
				// Send the script containing Mithril, the app, the reviver and the app.
				'\n<script src="' + path.join('/', app, script) + '" async></script>' +
				data.substr(i + placeholder.length)
			);

		});
	};
};<|endoftext|>


<commit_before>/*!
 * sparta.js 0.0.2
 * (c) 2014 Blendle <rick@blendle.nl>
 * sparta may be freely distributed under the MIT license.
 */
 (function(root, factory) {
	// Set up sparta in the right environment. Start with AMD.
	if (typeof define === 'function' && define.amd) {
		define(['underscore', 'q', 'exports'], function(_, Q, exports) {
			exports = factory(_, Q);
		});

	// Next for Node.js or CommonJS
	} else if (typeof exports !== 'undefined') {
		var Q;
		try { Q = require('q'); } catch(e) { }
		module.exports = factory(require('underscore'), Q, exports);
	}

}(this, function (_, Q) {
	'use strict';

	var globalSpartaOptions = {};

	var uniqid = 0,
		jsonpLastValue = null,
		jsonpLoaded = 0,
		jsonpCallbackPrefix = 'sparta_' + (+new Date()),
		head = document.getElementsByTagName('head')[0];

	function Sparta (options) {
		/**
		 * This function can be called to abort the XHR request
		 */
		var abortXHR = function () {
			this.abort();
		};

		/**
		 * abort a JSONP request
		 */
		var abortJSONP = function (script) {
			script.onload = script.onreadystatechange = null;
			head.removeChild(script);

			jsonpLastValue = null;
			jsonpLoaded = 1; // Act like we already did.
		};

		/**
		 * Store JSONP return data
		 */
		var jsonpCallback = function (data) {
			jsonpLastValue = data;
		};

		var jsonp = function (options, deferred) {
			var requestId = uniqid++,
				url = options.url,
				cbKey = options.jsonpCallback || 'callback',
				cbVal = options.jsonpCallbackName || jsonpCallbackPrefix + requestId,
				cbRegExp = new RegExp('((^|\\?|&)' + cbKey + ')=([^&]+)'),
				cbMatch = url.match(cbRegExp),
				script = document.createElement('script');

			// Determine callback
			if (cbMatch) {
				if (cbMatch[3] === '?') {
					url = url.replace(cbRegExp, '$1=' + cbVal); // wildcard callback func name
				} else {
					cbVal = cbMatch[3]; // provided callback func name
				}
			} else {
				// no callback details, add 'em
				url = url + (/\?/.test(url) ? '&' : '?') + (cbKey + '=' + cbVal);
			}

			// make callback available
			window[cbVal] = jsonpCallback;

			// setup script tag and it's readystate
			script.type = 'text/javascript';
			script.src = url;
			script.async = options.async !== undefined ? options.async : true;
			script.onload = script.onreadystatechange = function () {
				if ((script.readyState && script.readyState !== 'complete' && script.readyState !== 'loaded') || jsonpLoaded) {
					return false;
				}

				script.onload = script.onreadystatechange = null;

				// Resolve promise with data
				deferred.resolve(jsonpLastValue);

				jsonpLastValue = null;
				head.removeChild(script);
				jsonpLoaded = 1;
			};

			// Add the script to the DOM head
			head.appendChild(script);

			// make promise abortable
			var extendedPromise = _.extend(deferred.promise, {
				abort: abortJSONP.bind(this, script)
			});

			return extendedPromise;
		};

		return (function(options) {
			// Set up deferred
			var deferred = sparta.deferred && sparta.deferred();

			// handle jsonp
			if (options.jsonp) {
				return jsonp(options, deferred);
			}

			var req = new XMLHttpRequest();

			// Set up defaults
			if (globalSpartaOptions) {
				_.defaults(options || (options = {}), globalSpartaOptions);
			}

			// Invoke beforesend
			if (options.beforeSend) {
				options.beforeSend(req, options);
			}

			// Open request
			req.open(options.type || 'GET', options.url, options.async || true);

			// Set up content type
			if (options.contentType) {
				if (!options.headers) options.headers = {};
				options.headers['Content-Type'] = options.contentType;
			}

			if (!options.successStatusCodes) {
				options.successStatusCodes = [200, 201, 202, 204, 304];
			}

			// Stringify GET query params.
			if (options.type === 'GET' && typeof options.data === 'object') {
				var query = '';
				var stringifyKeyValuePair = function(key, value) {
					return value === null ? '' :
						'&' + encodeURIComponent(key) +
						'=' + encodeURIComponent(value);
				};

				for (var key in options.data) {
					query += stringifyKeyValuePair(key, options.data[key]);
				}

				if (query) {
					var sep = (options.url.indexOf('?') === -1) ? '?' : '&';
					options.url += sep + query.substring(1);
				}
			}

			// Are we sending credentials?
			if (options.credentials) {
				options.withCredentials = true;
			}

			// Set request headers
			Object.keys(options.headers || {}).forEach(function (key) {
				req.setRequestHeader(key, options.headers[key]);
			});

			// Listen to state changes
			req.onreadystatechange = function() {
				if (this.readyState !== 4 || this.status === 0) {
					return;
				}

				var data = null;
				var xmlResponseType = /^(?:application|text)\/xml/;
				var jsonResponseType = /^application\/json/;

				if (this.response && this.response !== '') {
					var contentType = this.getResponseHeader('content-type');
					// TODO For now we parse anything that isn't supposed to be xml as JSON. The backend
					// gives us wrong contentType headers sometimes. This "fixes" that, though probably will
					// throw some json parse errors.
					if (!xmlResponseType.test(contentType)) {
						try {
							data = JSON.parse(this.response);
						} catch(err) {
							sparta.handleParseError(this.response, options);
						}
					}
					else if (xmlResponseType.test(contentType)) {
						data = this.responseXML;
					} else {
						data = this.responseText;
					}
				}

				if (options.successStatusCodes.indexOf(this.status) === -1) {
					deferred.reject({
						data: data,
						statusCode: this.status
					});
				} else {
					deferred.resolve({
						data: data,
						statusCode: this.status
					});
				}

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			// Listen to abort changes
			req.onabort = function() {
				deferred.resolve({
					data: null,
					statusCode: 0
				});

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			// Listen to errors
			req.onerror = function() {
				deferred.reject({
					data: null,
					statusCode: -1
				});

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			req.send(options.data || void 0);

			var extendedPromise = _.extend(deferred.promise, {
				abort: abortXHR.bind(req)
			});

			return extendedPromise;
		})(options);
	}

	function sparta (options) {
		return new Sparta(options);
	}

	sparta.ajaxDefaults = function (options) {
		options = options || {};
		for (var k in options) {
			globalSpartaOptions[k] = options[k];
		}
	};

	sparta.deferred = function () {
		if (Q) {
			return Q.defer();
		} else {
			throw new Error('Please define your own deferred library by implementing sparta.deferred()');
		}
	};

	sparta.handleParseError = function (response, options) {};

	return sparta;
}));<commit_msg>Updated universal loader<commit_after>/*!
 * sparta.js 0.0.2
 * (c) 2014 Blendle <rick@blendle.nl>
 * sparta may be freely distributed under the MIT license.
 */
 (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['underscore', 'q'], factory);
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory(require('underscore'), require('q'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root._, root.Q);
    }
}(this, function (_, Q) {
    'use strict';

	var globalSpartaOptions = {};

	var uniqid = 0,
		jsonpLastValue = null,
		jsonpLoaded = 0,
		jsonpCallbackPrefix = 'sparta_' + (+new Date()),
		head = document.getElementsByTagName('head')[0];

	function Sparta (options) {
		/**
		 * This function can be called to abort the XHR request
		 */
		var abortXHR = function () {
			this.abort();
		};

		/**
		 * abort a JSONP request
		 */
		var abortJSONP = function (script) {
			script.onload = script.onreadystatechange = null;
			head.removeChild(script);

			jsonpLastValue = null;
			jsonpLoaded = 1; // Act like we already did.
		};

		/**
		 * Store JSONP return data
		 */
		var jsonpCallback = function (data) {
			jsonpLastValue = data;
		};

		var jsonp = function (options, deferred) {
			var requestId = uniqid++,
				url = options.url,
				cbKey = options.jsonpCallback || 'callback',
				cbVal = options.jsonpCallbackName || jsonpCallbackPrefix + requestId,
				cbRegExp = new RegExp('((^|\\?|&)' + cbKey + ')=([^&]+)'),
				cbMatch = url.match(cbRegExp),
				script = document.createElement('script');

			// Determine callback
			if (cbMatch) {
				if (cbMatch[3] === '?') {
					url = url.replace(cbRegExp, '$1=' + cbVal); // wildcard callback func name
				} else {
					cbVal = cbMatch[3]; // provided callback func name
				}
			} else {
				// no callback details, add 'em
				url = url + (/\?/.test(url) ? '&' : '?') + (cbKey + '=' + cbVal);
			}

			// make callback available
			window[cbVal] = jsonpCallback;

			// setup script tag and it's readystate
			script.type = 'text/javascript';
			script.src = url;
			script.async = options.async !== undefined ? options.async : true;
			script.onload = script.onreadystatechange = function () {
				if ((script.readyState && script.readyState !== 'complete' && script.readyState !== 'loaded') || jsonpLoaded) {
					return false;
				}

				script.onload = script.onreadystatechange = null;

				// Resolve promise with data
				deferred.resolve(jsonpLastValue);

				jsonpLastValue = null;
				head.removeChild(script);
				jsonpLoaded = 1;
			};

			// Add the script to the DOM head
			head.appendChild(script);

			// make promise abortable
			var extendedPromise = _.extend(deferred.promise, {
				abort: abortJSONP.bind(this, script)
			});

			return extendedPromise;
		};

		return function(options) {
			// Set up deferred
			var deferred = sparta.deferred && sparta.deferred();

			// handle jsonp
			if (options.jsonp) {
				return jsonp(options, deferred);
			}

			var req = new XMLHttpRequest();

			// Set up defaults
			if (globalSpartaOptions) {
				_.defaults(options || (options = {}), globalSpartaOptions);
			}

			// Invoke beforesend
			if (options.beforeSend) {
				options.beforeSend(req, options);
			}

			// Open request
			req.open(options.type || 'GET', options.url, options.async || true);

			// Set up content type
			if (options.contentType) {
				if (!options.headers) options.headers = {};
				options.headers['Content-Type'] = options.contentType;
			}

			if (!options.successStatusCodes) {
				options.successStatusCodes = [200, 201, 202, 204, 304];
			}

			// Stringify GET query params.
			if (options.type === 'GET' && typeof options.data === 'object') {
				var query = '';
				var stringifyKeyValuePair = function(key, value) {
					return value === null ? '' :
						'&' + encodeURIComponent(key) +
						'=' + encodeURIComponent(value);
				};

				for (var key in options.data) {
					query += stringifyKeyValuePair(key, options.data[key]);
				}

				if (query) {
					var sep = (options.url.indexOf('?') === -1) ? '?' : '&';
					options.url += sep + query.substring(1);
				}
			}

			// Are we sending credentials?
			if (options.credentials) {
				options.withCredentials = true;
			}

			// Set request headers
			Object.keys(options.headers || {}).forEach(function (key) {
				req.setRequestHeader(key, options.headers[key]);
			});

			// Listen to state changes
			req.onreadystatechange = function() {
				if (this.readyState !== 4 || this.status === 0) {
					return;
				}

				var data = null;
				var xmlResponseType = /^(?:application|text)\/xml/;
				var jsonResponseType = /^application\/json/;

				if (this.response && this.response !== '') {
					var contentType = this.getResponseHeader('content-type');
					// TODO For now we parse anything that isn't supposed to be xml as JSON. The backend
					// gives us wrong contentType headers sometimes. This "fixes" that, though probably will
					// throw some json parse errors.
					if (!xmlResponseType.test(contentType)) {
						try {
							data = JSON.parse(this.response);
						} catch(err) {
							sparta.handleParseError(this.response, options);
						}
					}
					else if (xmlResponseType.test(contentType)) {
						data = this.responseXML;
					} else {
						data = this.responseText;
					}
				}

				if (options.successStatusCodes.indexOf(this.status) === -1) {
					deferred.reject({
						data: data,
						statusCode: this.status
					});
				} else {
					deferred.resolve({
						data: data,
						statusCode: this.status
					});
				}

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			// Listen to abort changes
			req.onabort = function() {
				deferred.resolve({
					data: null,
					statusCode: 0
				});

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			// Listen to errors
			req.onerror = function() {
				deferred.reject({
					data: null,
					statusCode: -1
				});

				// Remove listeners
				this.onreadystatechange = null;
				this.onabort = null;
				this.onerror = null;
			};

			req.send(options.data || void 0);

			var extendedPromise = _.extend(deferred.promise, {
				abort: abortXHR.bind(req)
			});

			return extendedPromise;
		}(options);
	}

	function sparta (options) {
		return new Sparta(options);
	}

	sparta.ajaxDefaults = function (options) {
		globalSpartaOptions = _.extend(globalSpartaOptions || {}, options);
	};

	sparta.deferred = function () {
		return Q.defer();
	};

	sparta.handleParseError = function (response, options) {};

	return sparta;
}));<|endoftext|>


<commit_before>/**
 * @fileoverview Driver script for speech processing.
 * @author aravart@cs.wisc.edu (Ara Vartanian)
 */
goog.provide('SpeechGames');

goog.require('Blockly.Workspace');
goog.require('SpeechBlocks.Controller');
goog.require('SpeechBlocks.Interpreter');
goog.require('Turtle.Answers');

/**
 * A global reference to the current workspace's controller.
 * @public {SpeechBlocks.Controller}
 */
SpeechGames.controller = null;

/**
 * A global reference to the workspace itself.
 * @public {Blockly.Workspace}
 */
SpeechGames.workspace = null;

/**
 * Extracts a parameter from the URL.
 * If the parameter is absent default_value is returned.
 * @param {string} name The name of the parameter.
 * @param {string} defaultValue Value to return if paramater not found.
 * @return {string} The parameter value or the default value if not found.
 */
SpeechGames.getStringParamFromUrl = function(name, defaultValue) {
  var val =
      window.location.search.match(new RegExp('[?&]' + name + '=([^&]+)'));
  return val ? decodeURIComponent(val[1].replace(/\+/g, '%20')) : defaultValue;
};

/**
 * Extracts a numeric parameter from the URL.
 * If the parameter is absent or less than min_value, min_value is
 * returned.  If it is greater than max_value, max_value is returned.
 * @param {string} name The name of the parameter.
 * @param {number} minValue The minimum legal value.
 * @param {number} maxValue The maximum legal value.
 * @return {number} A number in the range [min_value, max_value].
 */
SpeechGames.getNumberParamFromUrl = function(name, minValue, maxValue) {
  var val = Number(SpeechGames.getStringParamFromUrl(name, 'NaN'));
  return isNaN(val) ? minValue : goog.math.clamp(minValue, val, maxValue);
};

/**
 * Maximum number of levels.  Common to all apps.
 * @public @const
 */
SpeechGames.MAX_LEVEL = 12;

/**
 * User's current level (e.g. 5).
 * @public
 */
SpeechGames.LEVEL =
    SpeechGames.getNumberParamFromUrl('level', 1, SpeechGames.MAX_LEVEL);

/**
 * Bind a function to a button's click event.
 * On touch-enabled browsers, ontouchend is treated as equivalent to onclick.
 * @param {!Element|string} el Button element or ID thereof.
 * @param {!Function} func Event handler to bind.
 */
SpeechGames.bindClick = function(el, func) {
  if (typeof el == 'string') {
    el = document.getElementById(el);
  }
  el.addEventListener('click', func, true);
  el.addEventListener('touchend', func, true);
};

// Initialize microphone and speech handling.
$(document).ready(function() {
  var oldQ = null;
  var parseTimer = null;
  var output = null;
  var timeout = null;
  SpeechGames.controller = SpeechBlocks.Controller.injectIntoDiv('blocklyDiv', {
       media: 'lib/google-blockly/media/',
       trashcan: false,
       scrollbars: false,
       toolbox: document.getElementById('toolbox')});
  SpeechGames.workspace = SpeechGames.controller.workspace_;
  var interpreter = new SpeechBlocks.Interpreter(SpeechGames.controller);

  function speechCorrections(speech) {
    speech = speech.toLowerCase();
    speech = speech.replace(/\batom\b/, 'add a');
    speech = speech.replace(/\badam's\b/, 'add a');
    speech = speech.replace(/\bblack\b/, 'block');
    speech = speech.replace(/\block\b/, 'block');
    speech = speech.replace(/\badam block\b/, 'add a move block');
    speech = speech.replace(/\bnumber to\b/, 'number 2');
    speech = speech.replace(/\bone\b/, '1');
    speech = speech.replace(/\btwo\b/, '2');
    speech = speech.replace(/\bthree\b/, '3');
    speech = speech.replace(/\b425\b/, '4 to 5');
    speech = speech.replace(/\bblock to\b/, 'block 2');
    speech = speech.replace(/\bblock what\b/, 'block 1');
    speech = speech.replace(/\bblock won\b/, 'block 1');
    speech = speech.replace(/\bunblock\b/, 'in block');
    speech = speech.replace(/\bhad\b/, 'add');
    speech = speech.replace(/\bterm\b/, 'turn');
    speech = speech.replace(/\b - \b/, ' to ');
    speech = speech.replace(/\b272\b/, 'to 72');
    return speech;
  }

  function startDictation() {
    if (window.hasOwnProperty('webkitSpeechRecognition')) {
      var mic_animate = 'https://www.google.com/intl/en/chrome/assets/common/images/content/mic-animate.gif';
      var mic = 'https://www.google.com/intl/en/chrome/assets/common/images/content/mic.gif';
      var recognition = new webkitSpeechRecognition();
      recognition.continuous = false;
      recognition.interimResults = false;
      recognition.lang = 'en-US';
      document.getElementById('microphone').src = mic_animate;
      recognition.start();
      recognition.onresult = function(e) {
        unfiltered = e.results[0][0].transcript;
        corrections = speechCorrections(unfiltered);
        document.getElementById('q').value = corrections;
        recognition.stop();
        // document.getElementById('microphone').src = mic;
        parseSpeech();
      };
      recognition.onerror = function(e) {
        recognition.stop();
        parseSpeech();
        // document.getElementById('microphone').src = mic;
      }
    }
  }

  function buildErrorMessage(e) {
    return e.location !== undefined ? 'Line ' + e.location.start.line + ', column ' + e.location.start.column + ': ' + e.message : e.message;
  }

  function parseSpeech() {
    oldQ = $('#q').val();
    $('#parse-message').attr('class', 'message progress').text('Parsing the input...');
    $('#output').addClass('disabled').text('Output not available.');

    try {
      var speech = $('#q').val()
      output = parser.parse(speech.toLowerCase());

      $('#parse-message')
            .attr('class', 'message info')
            .text('Input parsed successfully.');
      $('#output').removeClass('disabled').text(jsDump.parse(output));
      var response = interpretSpeech();
      clearTimeout(timeout);
      var result = true;
      $("#user-message").hide().text(response).fadeIn(200);
      $("#q").val("");
    } catch (e) {
      if(e instanceof SpeechBlocks.UserError) {
        $('#user-message').text(e.message)
      } else {
        // console.log(output);
        // console.log(e);
        $('#parse-message').attr('class', 'message error').text(buildErrorMessage(e));
        if(speech != '') {
          $('#user-message').hide().text('Sorry, I didn\'t understand \"' + speech + '\"').fadeIn(200);
          $("#q").val("");
          clearTimeout(timeout);
          timeout = setTimeout(function(){
            $('#user-message').hide().text("Awaiting your command!").fadeIn(200);
          },5000);
        }
      }
      var result = false;
    }
    startDictation();
    return result;
  }

  function interpretSpeech() {
    if (output !== null) {
      return interpreter.interpret(output);
    }
  }

  function scheduleParse() {
    if ($('#q').val() === oldQ) { return; }
    if (parseTimer !== null) {
      clearTimeout(parseTimer);
      parseTimer = null;
    }

    parseTimer = setTimeout(function() {
      parseSpeech();
      parseTimer = null;
    }, 1000);
  }

  $('#q')
  .change(scheduleParse)
  .mousedown(scheduleParse)
  .mouseup(scheduleParse)
  .click(scheduleParse)
  .keydown(scheduleParse)
  .keyup(scheduleParse)
  .keypress(scheduleParse);

  $('#microphone')
  .click(startDictation);

  $("#user-message").hide().text("Awaiting your command!").fadeIn(500);

  // $('#runButton').on('click', run);
  // $('#showButton').on('click', showCode);
  if(!getParameterByName('debug')) {
    $('#debug').hide();
  }
  $('#debugButton').on('click', function() { $('#debug').toggle() });
  $('#buttonRow').hide();

  $('#levelDescription').text(Turtle.descriptions[SpeechGames.LEVEL])

  startDictation();

});

function createCode() {
  Blockly.JavaScript.addReservedWords('code');
  return Blockly.JavaScript.workspaceToCode(SpeechGames.controller.workspace_);
}

function showCode() {
  var modalEl = document.createElement('generatedCode');
  modalEl.style.width = '400px';
  modalEl.style.height = '300px';
  modalEl.style.margin = '100px auto';
  modalEl.style.backgroundColor = '#ff';
  modalEl.textContent = createCode()
  mui.overlay('on', modalEl);
}

function getParameterByName(name, url) {
  if (!url) {
    url = window.location.href;
  }
  name = name.replace(/[\[\]]/g, '\\$&');
  var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
      results = regex.exec(url);
  if (!results) return null;
  if (!results[2]) return '';
  return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
<commit_msg>Fixed speech recognition ending during next level dialog and when running locally<commit_after>/**
 * @fileoverview Driver script for speech processing.
 * @author aravart@cs.wisc.edu (Ara Vartanian)
 */
goog.provide('SpeechGames');

goog.require('Blockly.Workspace');
goog.require('SpeechBlocks.Controller');
goog.require('SpeechBlocks.Interpreter');
goog.require('Turtle.Answers');

/**
 * A global reference to the current workspace's controller.
 * @public {SpeechBlocks.Controller}
 */
SpeechGames.controller = null;

/**
 * A global reference to the workspace itself.
 * @public {Blockly.Workspace}
 */
SpeechGames.workspace = null;

/**
 * Extracts a parameter from the URL.
 * If the parameter is absent default_value is returned.
 * @param {string} name The name of the parameter.
 * @param {string} defaultValue Value to return if paramater not found.
 * @return {string} The parameter value or the default value if not found.
 */
SpeechGames.getStringParamFromUrl = function(name, defaultValue) {
  var val =
      window.location.search.match(new RegExp('[?&]' + name + '=([^&]+)'));
  return val ? decodeURIComponent(val[1].replace(/\+/g, '%20')) : defaultValue;
};

/**
 * Extracts a numeric parameter from the URL.
 * If the parameter is absent or less than min_value, min_value is
 * returned.  If it is greater than max_value, max_value is returned.
 * @param {string} name The name of the parameter.
 * @param {number} minValue The minimum legal value.
 * @param {number} maxValue The maximum legal value.
 * @return {number} A number in the range [min_value, max_value].
 */
SpeechGames.getNumberParamFromUrl = function(name, minValue, maxValue) {
  var val = Number(SpeechGames.getStringParamFromUrl(name, 'NaN'));
  return isNaN(val) ? minValue : goog.math.clamp(minValue, val, maxValue);
};

/**
 * Maximum number of levels.  Common to all apps.
 * @public @const
 */
SpeechGames.MAX_LEVEL = 12;

/**
 * User's current level (e.g. 5).
 * @public
 */
SpeechGames.LEVEL =
    SpeechGames.getNumberParamFromUrl('level', 1, SpeechGames.MAX_LEVEL);

/**
 * Bind a function to a button's click event.
 * On touch-enabled browsers, ontouchend is treated as equivalent to onclick.
 * @param {!Element|string} el Button element or ID thereof.
 * @param {!Function} func Event handler to bind.
 */
SpeechGames.bindClick = function(el, func) {
  if (typeof el == 'string') {
    el = document.getElementById(el);
  }
  el.addEventListener('click', func, true);
  el.addEventListener('touchend', func, true);
};

// Initialize microphone and speech handling.
$(document).ready(function() {
  var oldQ = null;
  var parseTimer = null;
  var previousRecognitionTime = null;
  var output = null;
  var timeout = null;
  var animating = false;
  SpeechGames.controller = SpeechBlocks.Controller.injectIntoDiv('blocklyDiv', {
       media: 'lib/google-blockly/media/',
       trashcan: false,
       scrollbars: false,
       toolbox: document.getElementById('toolbox')});
  SpeechGames.workspace = SpeechGames.controller.workspace_;
  var interpreter = new SpeechBlocks.Interpreter(SpeechGames.controller);

  function speechCorrections(speech) {
    speech = speech.toLowerCase();
    speech = speech.replace(/\batom\b/, 'add a');
    speech = speech.replace(/\badam's\b/, 'add a');
    speech = speech.replace(/\bblack\b/, 'block');
    speech = speech.replace(/\block\b/, 'block');
    speech = speech.replace(/\badam block\b/, 'add a move block');
    speech = speech.replace(/\bnumber to\b/, 'number 2');
    speech = speech.replace(/\bone\b/, '1');
    speech = speech.replace(/\btwo\b/, '2');
    speech = speech.replace(/\bthree\b/, '3');
    speech = speech.replace(/\b425\b/, '4 to 5');
    speech = speech.replace(/\bblock to\b/, 'block 2');
    speech = speech.replace(/\bblock what\b/, 'block 1');
    speech = speech.replace(/\bblock won\b/, 'block 1');
    speech = speech.replace(/\bunblock\b/, 'in block');
    speech = speech.replace(/\bhad\b/, 'add');
    speech = speech.replace(/\bterm\b/, 'turn');
    speech = speech.replace(/\b - \b/, ' to ');
    speech = speech.replace(/\b272\b/, 'to 72');
    return speech;
  }

  function startDictation() {
    if (window.hasOwnProperty('webkitSpeechRecognition')) {
      var mic_animate = 'https://www.google.com/intl/en/chrome/assets/common/images/content/mic-animate.gif';
      var mic = 'https://www.google.com/intl/en/chrome/assets/common/images/content/mic.gif';
      var recognition = new webkitSpeechRecognition();
      recognition.continuous = false;
      recognition.interimResults = false;
      recognition.lang = 'en-US';
      recognition.onstart = function() {
        if (!animating) {
          animating = true;
          document.getElementById('microphone').src = mic_animate;
        }
      }
      recognition.start();
      recognition.onresult = function(e) {
        unfiltered = e.results[0][0].transcript;
        corrections = speechCorrections(unfiltered);
        document.getElementById('q').value = corrections;
        recognition.stop();
        // document.getElementById('microphone').src = mic;
        parseSpeech();
      };
      recognition.onend = function(e) {
        animating = false;
        document.getElementById('microphone').src = mic;
        if (Date.now() - previousRecognitionTime < 10)
        {
          return;
        }
        recognition.stop();
        parseSpeech();
      }
    }
  }

  function buildErrorMessage(e) {
    return e.location !== undefined ? 'Line ' + e.location.start.line + ', column ' + e.location.start.column + ': ' + e.message : e.message;
  }

  function parseSpeech() {
    previousRecognitionTime = Date.now();
    oldQ = $('#q').val();
    $('#parse-message').attr('class', 'message progress').text('Parsing the input...');
    $('#output').addClass('disabled').text('Output not available.');

    try {
      var speech = $('#q').val()
      output = parser.parse(speech.toLowerCase());

      $('#parse-message')
            .attr('class', 'message info')
            .text('Input parsed successfully.');
      $('#output').removeClass('disabled').text(jsDump.parse(output));
      var response = interpretSpeech();
      clearTimeout(timeout);
      var result = true;
      $("#user-message").hide().text(response).fadeIn(200);
      $("#q").val("");
    } catch (e) {
      if(e instanceof SpeechBlocks.UserError) {
        $('#user-message').text(e.message)
      } else {
        // console.log(output);
        // console.log(e);
        $('#parse-message').attr('class', 'message error').text(buildErrorMessage(e));
        if(speech != '') {
          $('#user-message').hide().text('Sorry, I didn\'t understand \"' + speech + '\"').fadeIn(200);
          $("#q").val("");
          clearTimeout(timeout);
          timeout = setTimeout(function(){
            $('#user-message').hide().text("Awaiting your command!").fadeIn(200);
          },5000);
        }
      }
      var result = false;
    }
    startDictation();
    return result;
  }

  function interpretSpeech() {
    if (output !== null) {
      return interpreter.interpret(output);
    }
  }

  function scheduleParse() {
    if ($('#q').val() === oldQ) { return; }
    if (parseTimer !== null) {
      clearTimeout(parseTimer);
      parseTimer = null;
    }

    parseTimer = setTimeout(function() {
      parseSpeech();
      parseTimer = null;
    }, 1000);
  }

  $('#q')
  .change(scheduleParse)
  .mousedown(scheduleParse)
  .mouseup(scheduleParse)
  .click(scheduleParse)
  .keydown(scheduleParse)
  .keyup(scheduleParse)
  .keypress(scheduleParse);

  $('#microphone')
  .click(startDictation);

  $("#user-message").hide().text("Awaiting your command!").fadeIn(500);

  // $('#runButton').on('click', run);
  // $('#showButton').on('click', showCode);
  if(!getParameterByName('debug')) {
    $('#debug').hide();
  }
  $('#debugButton').on('click', function() { $('#debug').toggle() });
  $('#buttonRow').hide();

  $('#levelDescription').text(Turtle.descriptions[SpeechGames.LEVEL])

  startDictation();

});

function createCode() {
  Blockly.JavaScript.addReservedWords('code');
  return Blockly.JavaScript.workspaceToCode(SpeechGames.controller.workspace_);
}

function showCode() {
  var modalEl = document.createElement('generatedCode');
  modalEl.style.width = '400px';
  modalEl.style.height = '300px';
  modalEl.style.margin = '100px auto';
  modalEl.style.backgroundColor = '#ff';
  modalEl.textContent = createCode()
  mui.overlay('on', modalEl);
}

function getParameterByName(name, url) {
  if (!url) {
    url = window.location.href;
  }
  name = name.replace(/[\[\]]/g, '\\$&');
  var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
      results = regex.exec(url);
  if (!results) return null;
  if (!results[2]) return '';
  return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
<|endoftext|>


<commit_before>var Sprint;

(function() {
  "use strict"

  var d = document
  var matchSelector = (function() {
    var prefixes = ["m", "webkitM", "msM", "mozM"]
    var i = -1
    var l = prefixes.length
    while (++i < l) {
      var name = prefixes[i] + "atchesSelector"
      if (Element.prototype[name]) {
        return name
      }
    }
  })()

  function toArray(obj) {
    // Converts array-like objects to actual arrays.
    // If obj is a Sprint object, the DOM reference gets updated.

    if (obj instanceof Array) return obj
    var isSprintObj = obj instanceof Init
    var dom = [].map.call(isSprintObj ? obj.get() : obj, function(el) {
      return el
    })
    isSprintObj && (obj.dom = dom)
    return dom
  }

  function selectByTag(tagName) {
    switch (tagName) {
      case "body":
        return [d.body]
      case d:
        return [d]
      default:
        return d.getElementsByTagName(tagName)
    }
  }

  function selectElements(selector) {
    // .class, #id or tagName
    if (/^[\#.]?[\w-]+$/.test(selector)) {
      switch (selector[0]) {
        case ".":
          return d.getElementsByClassName(selector.slice(1))
        case "#":
          return [d.getElementById(selector.slice(1))]
        default:
          return selectByTag(selector)
      }
    }
    return d.querySelectorAll(selector)
  }

  function duplicateEventListeners(el, clone) {
    if (!el.sprintEventListeners) return
    var $clone = Sprint(clone)
    Object.keys(el.sprintEventListeners).forEach(function(key) {
      el.sprintEventListeners[key].forEach(function(callback) {
        $clone.on(key, callback)
      })
    })
  }

  function insertHTML(position, content) {
    if (typeof content == "string") {
      this.each(function() {
        this.insertAdjacentHTML(position, content)
      })
    }
    else {
      // DOM node: single existing DOM node, createTextNode() or createElement()
      // Or collection: $("div"), [element1, element2], document.getElementsByTagName, etc.

      var elementsToInsert = content.nodeType ? [content] : toArray(content)
      var clonedElements = []
      var methods = {
        beforeend: function(clone) {
          this.appendChild(clone)
        },
        beforebegin: function(clone) {
          this.parentNode.insertBefore(clone, this)
        }
      }

      var isSprintObj = content instanceof Init

      this.each(function(index) {

        var self = this
        elementsToInsert.forEach(function(el) {
          var clone = el.cloneNode(true)
          methods[position].call(self, clone)
          duplicateEventListeners(el, clone)
          clonedElements.push(clone)

          if (index > 0) return
          var prt = el.parentNode
          if (prt) prt.removeChild(el)
        })
      })

      if (isSprintObj) content.dom = clonedElements
      return clonedElements
    }
  }

  function Init(selector) {
    switch (typeof selector) {
      case "string":
        if (selector[0] == "<") {
          var tmp = d.createElement("div")
          tmp.innerHTML = selector.trim()
          this.dom = [tmp.firstChild]
        }
        else {
          this.dom = selectElements(selector)
        }
        this.length = this.dom.length
        break
      case "function":
        this.dom = [d]
        this.length = 1
        this.on("DOMContentLoaded", selector)
        break
      default:
        var selectorLength = selector.length
        if (selectorLength === 0) {
          this.dom = selector
          this.length = selectorLength
        }
        else {
          this.dom = selectorLength ? selector : [selector]
          this.length = selectorLength || 1
        }
    }
  }

  Init.prototype = {
    addClass: function(name) {
      this.updateClass("add", name)
      return this
    },
    append: function(content) {
      insertHTML.call(this, "beforeend", content)
      return this
    },
    appendTo: function(selector) {
      selector instanceof Init || (selector = Sprint(selector))
      return Sprint(insertHTML.call(selector, "beforeend", this))
    },
    attr: function(name, value) {
      if (value === undefined) {
        var attributes = []
        this.each(function() {
          attributes.push(this.getAttribute(name))
        })
        return attributes.length == 1 ? attributes[0] : attributes
      }
      else {
        this.each(function() {
          this.setAttribute(name, value)
        })
        return this
      }
    },
    before: function(content) {
      insertHTML.call(this, "beforebegin", content)
      return this
    },
    children: function(selector) {
      var dom = []
      if (selector) {
        var self = this
        this.each(function() {
          var nodes = this.childNodes
          var i = -1
          var l = nodes.length

          while (++i < l) {
            var node = nodes[i]
            if (node.nodeType == 1 && self.is(selector, node)) {
              dom.push(node)
            }
          }
        })
      }
      else
        this.each(function() {
          var nodes = this.childNodes
          var i = -1
          var l = nodes.length

          while (++i < l) {
            var node = nodes[i]
            node.nodeType == 1 && dom.push(node)
          }
        })
      return Sprint(dom)
    },
    closest: function(selector) {
      var dom = []
      var self = this
      this.each(function() {
        var prt = this.parentNode
        var root = d.documentElement
        while (prt != root) {
          if (self.is(selector, prt)) {
            dom.push(prt)
            break
          }
          else {
            prt = prt.parentNode
          }
        }
      })
      return Sprint(dom)
    },
    each: function(callback) {
      // callback(index, element) where element == this
      var i = -1
      var l = this.length

      while (++i < l) {
        var node = this.get(i)
        callback.call(node, i, node)
      }
      return this
    },
    empty: function() {
      this.each(function() {
        this.innerHTML = ""
      })
      return this
    },
    eq: function(index) {
      var dom = index < this.length ? [this.get(index)] : []
      return Sprint(dom)
    },
    filter: function(selector) {
      var dom = []
      var self = this
      this.each(function() {
        if (self.is(selector, this)) {
          dom.push(this)
        }
      })
      return Sprint(dom)
    },
    find: function(selector) {
      var dom = []
      this.each(function() {
        var nodes = this.querySelectorAll(selector)
        var i = -1
        var l = nodes.length

        while (++i < l) {
          dom.push(nodes.item(i))
        }
      })
      return Sprint(dom)
    },
    get: function(index) {
      return index === undefined ? this.dom : this.dom[index]
    },
    hasClass: function(name) {
      var classFound = false
      this.each(function() {
        if (!classFound && this.classList.contains(name)) {
          classFound = true
        }
      })
      return classFound
    },
    is: function(selector, element) {
      var el = element || this.get(0)
      return el[matchSelector](selector)
    },
    next: function(selector) {
      var dom = []
      if (selector === undefined) {
        this.each(function() {
          dom.push(this.nextElementSibling)
        })
      }
      else {
        var self = this
        this.each(function() {
          var next = this.nextElementSibling
          self.is(selector, next) && dom.push(next)
        })
      }
      return Sprint(dom)
    },
    off: function(type, callback) {
      switch (arguments.length) {
        // .off()
        case 0:
          this.each(function(i, el) {
            if (!this.sprintEventListeners) return
            Object.keys(this.sprintEventListeners).forEach(function(key) {
              el.sprintEventListeners[key].forEach(function(callbackReference) {
                el.removeEventListener(key, callbackReference)
              })
            })
            this.sprintEventListeners = {}
          })
          break

        // .off("click")
        case 1:
          this.each(function(i, el) {
            if (!this.sprintEventListeners) return
            this.sprintEventListeners[type].forEach(function(callbackReference) {
              el.removeEventListener(type, callbackReference)
            })
            this.sprintEventListeners[type] = []
          })
          break

        // .off("click", handler)
        case 2:
          this.each(function() {
            if (!this.sprintEventListeners) return
            var updatedSprintEventListeners = []
            this.sprintEventListeners[type].forEach(function(callbackReference) {
              callback != callbackReference && updatedSprintEventListeners.push(callbackReference)
            })
            this.removeEventListener(type, callback)
            this.sprintEventListeners[type] = updatedSprintEventListeners
          })
          break
      }
      return this
    },
    on: function(type, callback) {
      this.each(function() {
        var callbackReference = callback
        this.sprintEventListeners || (this.sprintEventListeners = {})
        this.sprintEventListeners[type] || (this.sprintEventListeners[type] = [])
        this.sprintEventListeners[type].push(callbackReference)
        this.addEventListener(type, callbackReference)
      })
      return this
    },
    parent: function(selector) {
      var dom = []
      if (selector) {
        var self = this
        this.each(function() {
          var prt = this.parentNode
          if (self.is(selector, prt)) {
            dom.push(prt)
          }
        })
      }
      else {
        this.each(function() {
          dom.push(this.parentNode)
        })
      }
      return Sprint(dom)
    },
    removeAttr: function(name) {
      this.each(function() {
        this.removeAttribute(name)
      })
      return this
    },
    removeClass: function(name) {
      name === undefined
        ? this.removeAttr("class")
        : this.updateClass("remove", name)
      return this
    },
    size: function() {
      return this.get().length
    },
    text: function(content) {
      if (content === undefined) {
        var texts = []
        this.each(function() {
          texts.push(this.textContent)
        })
        return texts.length == 1 ? texts[0] : texts
      }
      else {
        this.each(function() {
          this.textContent = content
        })
        return this
      }
    },
    toggleClass: function(name) {
      this.updateClass("toggle", name)
      return this
    },
    wrap: function(element) {
      if (typeof element == "function") {
        this.each(function() {
          Sprint(this).wrap(element.call(this))
        })
      }
      else {
        toArray(this)

        var sprintElement = element instanceof Init ? element : Sprint(element)
        var outerWrap = sprintElement.get(0)
        var nestedElements = typeof element == "string" && element.match(/</g).length > 2

        this.each(function() {
          var clone = outerWrap.cloneNode(true)
          var prt = this.parentNode
          var next = this.nextSibling
          if (nestedElements) {
            // find most inner child
            var innerWrap = clone.firstChild
            while (innerWrap.firstChild) innerWrap = innerWrap.firstChild
            innerWrap.appendChild(this)
          }
          else {
            duplicateEventListeners(outerWrap, clone)
            clone.appendChild(this)
          }
          prt.insertBefore(clone, next)
        })
      }

      return this
    },

    // undocumented, mostly for internal use
    updateClass: function(method, name) {
      this.each(function() {
        this.classList[method](name)
      })
    }
  }

  // public

  Sprint = function(selector) {
    return new Init(selector)
  }

  window.$ === undefined && (window.$ = Sprint)
})();
<commit_msg>duplicateEventListeners now also checks child element<commit_after>var Sprint;

(function() {
  "use strict"

  var d = document
  var matchSelector = (function() {
    var prefixes = ["m", "webkitM", "msM", "mozM"]
    var i = -1
    var l = prefixes.length
    while (++i < l) {
      var name = prefixes[i] + "atchesSelector"
      if (Element.prototype[name]) {
        return name
      }
    }
  })()

  function toArray(obj) {
    // Converts array-like objects to actual arrays.
    // If obj is a Sprint object, the DOM reference gets updated.

    if (obj instanceof Array) return obj
    var isSprintObj = obj instanceof Init
    var dom = [].map.call(isSprintObj ? obj.get() : obj, function(el) {
      return el
    })
    isSprintObj && (obj.dom = dom)
    return dom
  }

  function selectByTag(tagName) {
    switch (tagName) {
      case "body":
        return [d.body]
      case d:
        return [d]
      default:
        return d.getElementsByTagName(tagName)
    }
  }

  function selectElements(selector) {
    // .class, #id or tagName
    if (/^[\#.]?[\w-]+$/.test(selector)) {
      switch (selector[0]) {
        case ".":
          return d.getElementsByClassName(selector.slice(1))
        case "#":
          return [d.getElementById(selector.slice(1))]
        default:
          return selectByTag(selector)
      }
    }
    return d.querySelectorAll(selector)
  }

  function duplicateEventListeners(el, clone) {
    var elChildren = el.querySelectorAll("*")
    var cloneChildren
    var i = -1
    var l = elChildren.length + 1
    elChildren[l-1] = el
    while (++i < l) {
      var listeners = elChildren[i].sprintEventListeners
      if (listeners) {
        if (!cloneChildren) {
          cloneChildren = clone.querySelectorAll("*")
          cloneChildren[l-1] = clone
        }
        var sprintCloneChild = Sprint(cloneChildren[i])
        Object.keys(listeners).forEach(function(key) {
          listeners[key].forEach(function(callback) {
            sprintCloneChild.on(key, callback)
          })
        })
      }
    }
  }

  function insertHTML(position, content) {
    if (typeof content == "string") {
      this.each(function() {
        this.insertAdjacentHTML(position, content)
      })
    }
    else {
      // DOM node: single existing DOM node, createTextNode() or createElement()
      // Or collection: $("div"), [element1, element2], document.getElementsByTagName, etc.

      var elementsToInsert = content.nodeType ? [content] : toArray(content)
      var clonedElements = []
      var methods = {
        beforeend: function(clone) {
          this.appendChild(clone)
        },
        beforebegin: function(clone) {
          this.parentNode.insertBefore(clone, this)
        }
      }

      this.each(function(index) {
        var self = this
        elementsToInsert.forEach(function(el) {
          var clone = el.cloneNode(true)
          methods[position].call(self, clone)
          duplicateEventListeners(el, clone)
          clonedElements.push(clone)

          if (index > 0) return
          var prt = el.parentNode
          if (prt) prt.removeChild(el)
        })
      })

      if (content instanceof Init) content.dom = clonedElements
      return clonedElements
    }
  }

  function Init(selector) {
    switch (typeof selector) {
      case "string":
        if (selector[0] == "<") {
          var tmp = d.createElement("div")
          tmp.innerHTML = selector.trim()
          this.dom = [tmp.firstChild]
        }
        else {
          this.dom = selectElements(selector)
        }
        this.length = this.dom.length
        break
      case "function":
        this.dom = [d]
        this.length = 1
        this.on("DOMContentLoaded", selector)
        break
      default:
        var selectorLength = selector.length
        if (selectorLength === 0) {
          this.dom = selector
          this.length = selectorLength
        }
        else {
          this.dom = selectorLength ? selector : [selector]
          this.length = selectorLength || 1
        }
    }
  }

  Init.prototype = {
    addClass: function(name) {
      this.updateClass("add", name)
      return this
    },
    append: function(content) {
      insertHTML.call(this, "beforeend", content)
      return this
    },
    appendTo: function(selector) {
      selector instanceof Init || (selector = Sprint(selector))
      return Sprint(insertHTML.call(selector, "beforeend", this))
    },
    attr: function(name, value) {
      if (value === undefined) {
        var attributes = []
        this.each(function() {
          attributes.push(this.getAttribute(name))
        })
        return attributes.length == 1 ? attributes[0] : attributes
      }
      else {
        this.each(function() {
          this.setAttribute(name, value)
        })
        return this
      }
    },
    before: function(content) {
      insertHTML.call(this, "beforebegin", content)
      return this
    },
    children: function(selector) {
      var dom = []
      if (selector) {
        var self = this
        this.each(function() {
          var nodes = this.childNodes
          var i = -1
          var l = nodes.length

          while (++i < l) {
            var node = nodes[i]
            if (node.nodeType == 1 && self.is(selector, node)) {
              dom.push(node)
            }
          }
        })
      }
      else
        this.each(function() {
          var nodes = this.childNodes
          var i = -1
          var l = nodes.length

          while (++i < l) {
            var node = nodes[i]
            node.nodeType == 1 && dom.push(node)
          }
        })
      return Sprint(dom)
    },
    closest: function(selector) {
      var dom = []
      var self = this
      this.each(function() {
        var prt = this.parentNode
        var root = d.documentElement
        while (prt != root) {
          if (self.is(selector, prt)) {
            dom.push(prt)
            break
          }
          else {
            prt = prt.parentNode
          }
        }
      })
      return Sprint(dom)
    },
    each: function(callback) {
      // callback(index, element) where element == this
      var i = -1
      var l = this.length

      while (++i < l) {
        var node = this.get(i)
        callback.call(node, i, node)
      }
      return this
    },
    empty: function() {
      this.each(function() {
        this.innerHTML = ""
      })
      return this
    },
    eq: function(index) {
      var dom = index < this.length ? [this.get(index)] : []
      return Sprint(dom)
    },
    filter: function(selector) {
      var dom = []
      var self = this
      this.each(function() {
        if (self.is(selector, this)) {
          dom.push(this)
        }
      })
      return Sprint(dom)
    },
    find: function(selector) {
      var dom = []
      this.each(function() {
        var nodes = this.querySelectorAll(selector)
        var i = -1
        var l = nodes.length

        while (++i < l) {
          dom.push(nodes.item(i))
        }
      })
      return Sprint(dom)
    },
    get: function(index) {
      return index === undefined ? this.dom : this.dom[index]
    },
    hasClass: function(name) {
      var classFound = false
      this.each(function() {
        if (!classFound && this.classList.contains(name)) {
          classFound = true
        }
      })
      return classFound
    },
    is: function(selector, element) {
      var el = element || this.get(0)
      return el[matchSelector](selector)
    },
    next: function(selector) {
      var dom = []
      if (selector === undefined) {
        this.each(function() {
          dom.push(this.nextElementSibling)
        })
      }
      else {
        var self = this
        this.each(function() {
          var next = this.nextElementSibling
          self.is(selector, next) && dom.push(next)
        })
      }
      return Sprint(dom)
    },
    off: function(type, callback) {
      switch (arguments.length) {
        // .off()
        case 0:
          this.each(function(i, el) {
            if (!this.sprintEventListeners) return
            Object.keys(this.sprintEventListeners).forEach(function(key) {
              el.sprintEventListeners[key].forEach(function(callbackReference) {
                el.removeEventListener(key, callbackReference)
              })
            })
            this.sprintEventListeners = {}
          })
          break

        // .off("click")
        case 1:
          this.each(function(i, el) {
            if (!this.sprintEventListeners) return
            this.sprintEventListeners[type].forEach(function(callbackReference) {
              el.removeEventListener(type, callbackReference)
            })
            this.sprintEventListeners[type] = []
          })
          break

        // .off("click", handler)
        case 2:
          this.each(function() {
            if (!this.sprintEventListeners) return
            var updatedSprintEventListeners = []
            this.sprintEventListeners[type].forEach(function(callbackReference) {
              callback != callbackReference && updatedSprintEventListeners.push(callbackReference)
            })
            this.removeEventListener(type, callback)
            this.sprintEventListeners[type] = updatedSprintEventListeners
          })
          break
      }
      return this
    },
    on: function(type, callback) {
      this.each(function() {
        var callbackReference = callback
        this.sprintEventListeners || (this.sprintEventListeners = {})
        this.sprintEventListeners[type] || (this.sprintEventListeners[type] = [])
        this.sprintEventListeners[type].push(callbackReference)
        this.addEventListener(type, callbackReference)
      })
      return this
    },
    parent: function(selector) {
      var dom = []
      if (selector) {
        var self = this
        this.each(function() {
          var prt = this.parentNode
          if (self.is(selector, prt)) {
            dom.push(prt)
          }
        })
      }
      else {
        this.each(function() {
          dom.push(this.parentNode)
        })
      }
      return Sprint(dom)
    },
    removeAttr: function(name) {
      this.each(function() {
        this.removeAttribute(name)
      })
      return this
    },
    removeClass: function(name) {
      name === undefined
        ? this.removeAttr("class")
        : this.updateClass("remove", name)
      return this
    },
    size: function() {
      return this.get().length
    },
    text: function(content) {
      if (content === undefined) {
        var texts = []
        this.each(function() {
          texts.push(this.textContent)
        })
        return texts.length == 1 ? texts[0] : texts
      }
      else {
        this.each(function() {
          this.textContent = content
        })
        return this
      }
    },
    toggleClass: function(name) {
      this.updateClass("toggle", name)
      return this
    },
    wrap: function(element) {
      if (typeof element == "function") {
        this.each(function() {
          Sprint(this).wrap(element.call(this))
        })
      }
      else {
        toArray(this)

        var sprintElement = element instanceof Init ? element : Sprint(element)
        var outerWrap = sprintElement.get(0)
        var nestedElements = typeof element == "string" && element.match(/</g).length > 2

        this.each(function() {
          var clone = outerWrap.cloneNode(true)
          var prt = this.parentNode
          var next = this.nextSibling
          if (nestedElements) {
            // find most inner child
            var innerWrap = clone.firstChild
            while (innerWrap.firstChild) innerWrap = innerWrap.firstChild
            innerWrap.appendChild(this)
          }
          else {
            duplicateEventListeners(outerWrap, clone)
            clone.appendChild(this)
          }
          prt.insertBefore(clone, next)
        })
      }

      return this
    },

    // undocumented, mostly for internal use
    updateClass: function(method, name) {
      this.each(function() {
        this.classList[method](name)
      })
    }
  }

  // public

  Sprint = function(selector) {
    return new Init(selector)
  }

  window.$ === undefined && (window.$ = Sprint)
})();
<|endoftext|>


<commit_before>import {points}         from './primitiveTypes/points';
import {lines}          from './primitiveTypes/lines';
import {linesLoop}      from './primitiveTypes/linesLoop';
import {linesStrip}     from './primitiveTypes/linesStrip';
import {triangles}      from './primitiveTypes/triangles';
import {trianglesStrip} from './primitiveTypes/trianglesStrip';
import {trianglesFan}   from './primitiveTypes/trianglesFan';
import {drawTriangles}  from './draw/drawTriangles';
import {x as px, y as py, z as pz } from './point';

/**
 * @author Stefan Nieke / http://niekes.com/
 */

export default function() {

    var origin          = [0, 0],
        scale           = 1,
        angleX          = 0,
        angleY          = 0,
        angleZ          = 0,
        x               = px,
        y               = py,
        z               = pz,
        primitiveType   = 'POINTS',
        processData = {
            POINTS          : points,
            LINES           : lines,
            LINES_LOOP      : linesLoop,
            LINES_STRIP     : linesStrip,
            TRIANGLES       : triangles,
            TRIANGLES_STRIP : trianglesStrip,
            TRIANGLES_FAN   : trianglesFan
        },
        draw = {
            TRIANGLES   : drawTriangles
        };

    function _3d(data){
        return processData[primitiveType](data, {scale: scale, origin: origin}, {x: x, y: y, z: z}, {x: angleX, y: angleY, z: angleZ });
    }

    _3d.origin = function(_){
        return arguments.length ? (origin = _, _3d) : origin;
    };

    _3d.scale = function(_){
        return arguments.length ? (scale = _, _3d) : scale;
    };

    _3d.rotateX = function(_){
        return arguments.length ? (angleX = _, _3d) : angleX;
    };

    _3d.rotateY = function(_){
        return arguments.length ? (angleY = _, _3d) : angleY;
    };

    _3d.rotateZ = function(_){
        return arguments.length ? (angleZ = _, _3d) : angleZ;
    };

    _3d.primitiveType = function(_){
        return arguments.length ? (primitiveType = _, _3d) : primitiveType;
    };

    _3d.x = function(_){
        return arguments.length ? (x = typeof _ === 'function' ? _ : +_, _3d) : x;
    };

    _3d.y = function(_){
        return arguments.length ? (y = typeof _ === 'function' ? _ : +_, _3d) : y;
    };

    _3d.z = function(_){
        return arguments.length ? (z = typeof _ === 'function' ? _ : +_, _3d) : z;
    };

    _3d.draw = function(d){
        if(!((primitiveType === 'POINTS') || (primitiveType === 'LINES'))){
            return draw[primitiveType](d);
        }
    };

    return _3d;
}
<commit_msg>break lines for arguments<commit_after>import {points}         from './primitiveTypes/points';
import {lines}          from './primitiveTypes/lines';
import {linesLoop}      from './primitiveTypes/linesLoop';
import {linesStrip}     from './primitiveTypes/linesStrip';
import {triangles}      from './primitiveTypes/triangles';
import {trianglesStrip} from './primitiveTypes/trianglesStrip';
import {trianglesFan}   from './primitiveTypes/trianglesFan';
import {drawTriangles}  from './draw/drawTriangles';
import {x as px, y as py, z as pz } from './point';

/**
 * @author Stefan Nieke / http://niekes.com/
 */

export default function() {

    var origin          = [0, 0],
        scale           = 1,
        angleX          = 0,
        angleY          = 0,
        angleZ          = 0,
        x               = px,
        y               = py,
        z               = pz,
        primitiveType   = 'POINTS',
        processData = {
            POINTS          : points,
            LINES           : lines,
            LINES_LOOP      : linesLoop,
            LINES_STRIP     : linesStrip,
            TRIANGLES       : triangles,
            TRIANGLES_STRIP : trianglesStrip,
            TRIANGLES_FAN   : trianglesFan
        },
        draw = {
            TRIANGLES   : drawTriangles
        };

    function _3d(data){
        return processData[primitiveType](
            data,
            {scale: scale, origin: origin},
            {x: x, y: y, z: z},
            {x: angleX, y: angleY, z: angleZ
        });
    }

    _3d.origin = function(_){
        return arguments.length ? (origin = _, _3d) : origin;
    };

    _3d.scale = function(_){
        return arguments.length ? (scale = _, _3d) : scale;
    };

    _3d.rotateX = function(_){
        return arguments.length ? (angleX = _, _3d) : angleX;
    };

    _3d.rotateY = function(_){
        return arguments.length ? (angleY = _, _3d) : angleY;
    };

    _3d.rotateZ = function(_){
        return arguments.length ? (angleZ = _, _3d) : angleZ;
    };

    _3d.primitiveType = function(_){
        return arguments.length ? (primitiveType = _, _3d) : primitiveType;
    };

    _3d.x = function(_){
        return arguments.length ? (x = typeof _ === 'function' ? _ : +_, _3d) : x;
    };

    _3d.y = function(_){
        return arguments.length ? (y = typeof _ === 'function' ? _ : +_, _3d) : y;
    };

    _3d.z = function(_){
        return arguments.length ? (z = typeof _ === 'function' ? _ : +_, _3d) : z;
    };

    _3d.draw = function(d){
        if(!((primitiveType === 'POINTS') || (primitiveType === 'LINES'))){
            return draw[primitiveType](d);
        }
    };

    return _3d;
}
<|endoftext|>


<commit_before>/*
 *	ab.js JavaScript A/B-testing tool v0.3
 *
 *	Copyright 2010, Anders Mattson
 *	Licensed under the MIT or GPL Version 2 licenses.
 *
 *  AB.vary( Settings (Object), Variation #1 (Object) [, Variation #2 (Object) [, ...]] )
 *
 *  AB.vary({
 *  		name: 'unique_query_and_cookie_parameter_name',
 *  		elem: 'id_string_or_DOM_Node',
 *  		url: 'http://validation.url.for/variation'
 *  	},{
 *  		html: 'html_content',
 *  		style: {
 *  			'css-attribute': 'css-attribute-value',
 *  			...
 *  		},
 *  		fn: function(){...},
 *  		href: 'http://variation.url' (starting with a plus appends the string to the urls)
 *  	},{
 *  		...
 *  });
 */

var AB = window.AB = {

	vary: function(d){

		var j = d.elem || d,
			e = typeof j == "string" ? document.getElementById(j) : j,
			o, p, c, h = document.location.href, A = arguments, P = (d.name || 'ab_test');

		// Applying the variation to multiple DOM elements
		if(!e || e.constructor == Array) {

			for(j in e) {
				if(!d.elem)
					d = {};
				d.elem = e[j];
				A[0] = d;
				AB.vary.apply(this, A);
			}

			return;

		}

		// Check if the variation is for a specific url and if so, check that it matches the current one.
		if(d.url && ((d.url.exec && !h.match(d.url)) || (typeof d.url == "string" && AB.suri(d.url) != AB.suri(h))))
			return;

		// Pick out which variation to use from cookie, url or random (in that order)
		var i = (c = document.cookie.match(new RegExp("(?:^|[^a-z0-9_\-]+)" + P + "=([0-9]+)", "i"))) ?
				c[1]*1 : (c = h.match(new RegExp("[\&\?]+"+P+"=([0-9]+)", "i"))) ?
				c[1]*1 : Math.floor(A.length*Math.random());

		o = i == 0 || i > A.length - 1 ? {} : A[i];

		document.cookie = P + '=' + i + '; path=/';

		if(o.html)
			e.innerHTML = o.html;

		if(o.style)
			for(j in o.style)
				e.style[j] = o.style[j];

		// Last we replace all the links found in the changed document fragment.
		A = e.nodeName == 'A' ? [e] : e.getElementsByTagName('A');

		for(var j in A) {
			h = A[j].getAttribute('href');
			if(h) {
				if(o.href)
					h = o.href[0] == '+' ? h + o.href.substring(1) : o.href;
				else {
					if(h.indexOf('#') > -1) {
						p = h.split("#");
						h = p[0] + ((h.indexOf('?') > -1) ? '&' : '?') + P + '=' + i + '#' + p[1];
					}
					else
						h += ((h.indexOf('?') > -1) ? '&' : '?') + P + '=' + i;
				}
				A[j].setAttribute('href', h);
			}
		}

		if(o.fn)
			fn.apply(this, arguments);
	},

	/*
	 * A modified version of parseUri 1.2.2 by (c) Steven Levithan <stevenlevithan.com> released under MIT License
	 *
	 * Parses and recompiles urls for better comparison
	 */
	suri: function(str, fragment) {
		var	m = AB.reg[0].exec(str), p = [];

		(m[12] || '').replace(AB.reg[1], function (a, b, c) {
			if (a) p.push(b);
		});

		m[12] = p.sort().join("&");
		return m[1] + '://' + (m[4] || '') + (m[5] ? ':' + m[5] : '') + (m[4] || m[5] ? '@' : '') + (m[6] || '') + (m[7] ? ':' + m[7] : '') + (m[9] || '') + (m[12] ? '?' + m[12] : '') + (m[13] && fragment ? '#' + m[13] : '');
	},

	// Putting regexp here to precompile them.
	reg: [
		/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,
		/(?:^|&)([^&=]*=?[^&]*)/g
	]
};<commit_msg>Refinements to the url parsing function suri(). Addedd support for getting just one part of the url as well.<commit_after>/*
 *	ab.js JavaScript A/B-testing tool v0.3
 *
 *	Copyright 2010, Anders Mattson
 *	Licensed under the MIT or GPL Version 2 licenses.
 *
 *  For more info, see https://github.com/andersmattson/ab.js
 */

var AB = window.AB = {

	vary: function(d){
		var j = d.elem || d,
			e = typeof j == "string" ? document.getElementById(j) : j,
			o, p, c, h = document.location.href, A = arguments, P = (d.name || 'ab_test');

		// Applying the variation to multiple DOM elements
		if(!e || e.constructor == Array) {
			for(j in e) {
				if(!d.elem)
					d = {};
				d.elem = e[j];
				A[0] = d;
				AB.vary.apply(this, A);
			}
			return;
		}

		// Check if the variation is for a specific url and if so, check that it matches the current one.
		if(d.url && ((d.url.exec && !h.match(d.url)) || (typeof d.url == "string" && AB.suri(d.url) != AB.suri(h))))
			return;

		// Pick out which variation to use from cookie, url or random (in that order)
		var i = (c = document.cookie.match(new RegExp("(?:^|[^a-z0-9_\-]+)" + P + "=([0-9]+)", "i"))) ?
				c[1]*1 : (c = h.match(new RegExp("[\&\?]+"+P+"=([0-9]+)", "i"))) ?
				c[1]*1 : Math.floor(A.length*Math.random());

		o = i == 0 || i > A.length - 1 ? {} : A[i];

		document.cookie = P + '=' + i + '; path=/';

		if(o.html)
			e.innerHTML = o.html;

		if(o.style)
			for(j in o.style)
				e.style[j] = o.style[j];

		// Last we replace all the links found in the changed document fragment.
		A = e.nodeName == 'A' ? [e] : e.getElementsByTagName('A');

		for(var j in A) {
			h = A[j].getAttribute('href');
			if(h) {
				if(o.href)
					h = o.href[0] == '+' ? h + o.href.substring(1) : o.href;
				else {
					if(h.indexOf('#') > -1) {
						p = h.split("#");
						h = p[0] + ((h.indexOf('?') > -1) ? '&' : '?') + P + '=' + i + '#' + p[1];
					}
					else
						h += ((h.indexOf('?') > -1) ? '&' : '?') + P + '=' + i;
				}
				A[j].setAttribute('href', h);
			}
		}

		if(o.fn)
			fn.apply(this, arguments);
	},

	/*
	 * A modified version of parseUri 1.2.2 by (c) Steven Levithan <stevenlevithan.com> released under MIT License
	 *
	 * Parses and recompiles urls for better comparison. If i is set the function returns a specific part of the url:
	 * 0 - source, 1 - protocol, 2 - authority, 3 - userInfo, 4 - user, 5 - password, 6 - host,
	 * 7 - port, 8 - relative, 9 - path, 10 - directory, 11 - file, 12 - query, 13 - anchor.
	 */
	suri: function(str, fragment, i) {
		var	m = AB.reg[0].exec(str), p = [];

		if(m[12] && (i == 12 || (i !== 0 && !i))){
			m[12].replace(AB.reg[1], function (a, b) {
				if (a) p.push(b);
			});
			m[12] = p.sort().join("&");
		}

		return i === 0 || i ? m[i] : m[1] + '://' + (m[4] || '') + (m[5] ? ':' + m[5] : '') + (m[4] || m[5] ? '@' : '') + (m[6] || '') + (m[7] ? ':' + m[7] : '') + (m[9] || '') + (m[12] ? '?' + m[12] : '') + (m[13] && fragment ? '#' + m[13] : '');
	},

	// Putting regexp here to precompile them.
	reg: [
		/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,
		/(?:^|&)([^&=]*=?[^&]*)/g
	]
};<|endoftext|>


<commit_before>{
  const words = '[⎕\\]\\)\\.A-Z_a-zÀ-ÖØ-Ýß-öø-üþ∆⍙Ⓐ-Ⓩ\\d]';
  const prefixRE = new RegExp(`^(${words}*)${words}*(?: \\1${words}*)*$`);

  D.ac = (me) => {
    me.tabComplete = 0;
    me.onDidChangeModelContent((e) => {
      const pk = D.prf.prefixKey();
      if (!me.dyalogBQ && e.changes.length === 1
        && e.changes[0].text === pk) {
        D.commands.BQC(me);
      } else {
        setTimeout(() => {
          const sw = me.contentWidgets['editor.widget.suggestWidget'];
          const swv = sw.widget.suggestWidgetVisible.get();
          const r = e.changes[0].range;
          const l = me.model.getLineContent(r.startLineNumber).toLowerCase();
          const bq2 = e.changes.length === 1 && RegExp(`${pk}${pk}\\w*`, 'i').test(l);
          if (swv && sw.widget.list.length === 1) {
            const t = sw.widget.focusedItem.suggestion.insertText.toLowerCase();
            if (l.slice(r.startColumn - t.length, r.startColumn) === t) {
              me.trigger('editor', 'hideSuggestWidget');
            } else {
              me.trigger('editor', 'editor.action.triggerSuggest');
            }
          } else if (swv && !sw.widget.list.length) {
            me.trigger('editor', 'hideSuggestWidget');
          } else if (swv && !bq2 && sw.widget.list.length > 1) {
            me.trigger('editor', 'editor.action.triggerSuggest');
          }
        }, 50);
      }
    });
    return (x) => { // win:editor or session instance to set up autocompletion in
      const { ac } = me.model;
      if (ac && ac.complete) {
        const l = ac.position.lineNumber;
        const c = ac.position.column;
        const manual = me.tabComplete;
        if (D.prf.autocompletion() === 'shell' && manual) {
          const [,prefix] = x.options.join(' ').match(prefixRE);
          if (prefix && prefix.length > x.skip) {
            const endCol = (c - x.skip) + prefix.length;
            me.executeEdits(
              'D',
              [{ range: new monaco.Range(l, c - x.skip, l, c), text: prefix }],
              [new monaco.Selection(l, endCol, l, endCol)],
            );
            me.trigger('editor', 'hideSuggestWidget');
            me.tabComplete = 0;
            return;
          } else if (me.tabComplete < 2) {
            me.trigger('editor', 'hideSuggestWidget');
            return;
          }
          me.tabComplete = 0;
        }
        if (!x.options.length || (D.prf.autocompletion() === 'shell' && !manual)) {
          me.trigger('editor', 'hideSuggestWidget');
          return;
        }
        ac.complete(x.options.map(i => ({
          label: i,
          range: new monaco.Range(l, c - x.skip, l, c),
        })));
      }
    };
  };
}
<commit_msg>fix deadlock with prefix key followed by space<commit_after>{
  const words = '[⎕\\]\\)\\.A-Z_a-zÀ-ÖØ-Ýß-öø-üþ∆⍙Ⓐ-Ⓩ\\d]';
  const prefixRE = new RegExp(`^(${words}*)${words}*(?: \\1${words}*)*$`);

  D.ac = (me) => {
    me.tabComplete = 0;
    me.onDidChangeModelContent((e) => {
      const pk = D.prf.prefixKey();
      if (me.listen && !me.dyalogBQ && e.changes.length === 1
        && e.changes[0].text === pk) {
        D.commands.BQC(me);
      } else {
        setTimeout(() => {
          const sw = me.contentWidgets['editor.widget.suggestWidget'];
          const swv = sw.widget.suggestWidgetVisible.get();
          const r = e.changes[0].range;
          const l = me.model.getLineContent(r.startLineNumber).toLowerCase();
          const bq2 = e.changes.length === 1 && RegExp(`${pk}${pk}\\w*`, 'i').test(l);
          if (swv && sw.widget.list.length === 1) {
            const t = sw.widget.focusedItem.suggestion.insertText.toLowerCase();
            if (l.slice(r.startColumn - t.length, r.startColumn) === t) {
              me.trigger('editor', 'hideSuggestWidget');
            } else {
              me.trigger('editor', 'editor.action.triggerSuggest');
            }
          } else if (swv && !sw.widget.list.length) {
            me.trigger('editor', 'hideSuggestWidget');
          } else if (swv && !bq2 && sw.widget.list.length > 1) {
            me.trigger('editor', 'editor.action.triggerSuggest');
          }
        }, 50);
      }
    });
    return (x) => { // win:editor or session instance to set up autocompletion in
      const { ac } = me.model;
      if (ac && ac.complete) {
        const l = ac.position.lineNumber;
        const c = ac.position.column;
        const manual = me.tabComplete;
        if (D.prf.autocompletion() === 'shell' && manual) {
          const [,prefix] = x.options.join(' ').match(prefixRE);
          if (prefix && prefix.length > x.skip) {
            const endCol = (c - x.skip) + prefix.length;
            me.executeEdits(
              'D',
              [{ range: new monaco.Range(l, c - x.skip, l, c), text: prefix }],
              [new monaco.Selection(l, endCol, l, endCol)],
            );
            me.trigger('editor', 'hideSuggestWidget');
            me.tabComplete = 0;
            return;
          } else if (me.tabComplete < 2) {
            me.trigger('editor', 'hideSuggestWidget');
            return;
          }
          me.tabComplete = 0;
        }
        if (!x.options.length || (D.prf.autocompletion() === 'shell' && !manual)) {
          me.trigger('editor', 'hideSuggestWidget');
          return;
        }
        ac.complete(x.options.map(i => ({
          label: i,
          range: new monaco.Range(l, c - x.skip, l, c),
        })));
      }
    };
  };
}
<|endoftext|>


<commit_before>/* Monster action functions. */

var AI = AI || {};

AI.randomWalk =  function(callback) {
    this.moveBy(R.random(-1, 2), R.random(-1, 2));
    callback();
};

AI.huntMelee = function(callback) {
    var path = [];
    var player = world.player;
    if (!this.awake && this.dist(player) > 10) {
        AI.randomWalk.call(this, callback); /* Wander */
        return;
    } else {
        this.awake = true; /* Wake up. */
    }
    var that = this;
    var visible = function(x, y) {
        if (world.isSolid(x, y)) {
            return false;
        } else {
            var dist = Math.max(Math.abs(x - that.x), Math.abs(y - that.y));
            if (dist === 1) {
                return !world.monsterAt(x, y);
            } else {
                return true;
            }
        }
    };
    var astar = new ROT.Path.AStar(player.x, player.y, visible);
    astar.compute(this.x, this.y, function(x, y) {
        path.push({x: x, y: y});
    });
    if (path.length < 2) {
        //debug(100, 'AI.huntMelee: monster is stuck');
    } else if (path.length === 2) {
        this.melee(player);
    } else if (path.length > 2) {
        this.tryMove(path[1].x, path[1].y);
    }
    callback();
};

AI.huntRanged = function(callback) {
    if (world.isVisible(this.x, this.y)) {
        this.ranged(world.player);
        callback();
    } else {
        AI.huntMelee.call(this, callback);
    }
};

/* Make the melee hunter the default. */
Monster.prototype.act = AI.huntMelee;
<commit_msg>Don't do anything when melee is asleep.<commit_after>/* Monster action functions. */

var AI = AI || {};

AI.randomWalk =  function(callback) {
    this.moveBy(R.random(-1, 2), R.random(-1, 2));
    callback();
};

AI.huntMelee = function(callback) {
    var path = [];
    var player = world.player;
    if (!this.awake && this.dist(player) > 10) {
        callback(); /* Do nothing */
        return;
    } else {
        this.awake = true; /* Wake up. */
    }
    var that = this;
    var visible = function(x, y) {
        if (world.isSolid(x, y)) {
            return false;
        } else {
            var dist = Math.max(Math.abs(x - that.x), Math.abs(y - that.y));
            if (dist === 1) {
                return !world.monsterAt(x, y);
            } else {
                return true;
            }
        }
    };
    var astar = new ROT.Path.AStar(player.x, player.y, visible);
    astar.compute(this.x, this.y, function(x, y) {
        path.push({x: x, y: y});
    });
    if (path.length < 2) {
        //debug(100, 'AI.huntMelee: monster is stuck');
    } else if (path.length === 2) {
        this.melee(player);
    } else if (path.length > 2) {
        this.tryMove(path[1].x, path[1].y);
    }
    callback();
};

AI.huntRanged = function(callback) {
    if (world.isVisible(this.x, this.y)) {
        this.ranged(world.player);
        callback();
    } else {
        AI.huntMelee.call(this, callback);
    }
};

/* Make the melee hunter the default. */
Monster.prototype.act = AI.huntMelee;
<|endoftext|>