<commit_before>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/util/rsvp',
	'goo/scripts/OrbitCamControlScript',
	'goo/scripts/OrbitNPanControlScript',
	'goo/scripts/FlyControlScript',
	'goo/scripts/WASDControlScript',
	'goo/scripts/BasicControlScript',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil',
	'goo/entities/SystemBus',

	'goo/scripts/ScriptUtils',
	'goo/scripts/Scripts'
],
/** @lends */
function(
	ConfigHandler,
	RSVP,
	OrbitCamControlScript,
	OrbitNPanControlScript,
	FlyControlScript,
	WASDControlScript,
	BasicControlScript,
	PromiseUtil,
	_,
	SystemBus,

	ScriptUtils,
	Scripts
) {
	"use strict";

	/**
	* @class
	* @private
	*/
	function ScriptHandler() {
		ConfigHandler.apply(this, arguments);
		this._bodyCache = {};
		this._currentScriptLoading = null;
		this._addGlobalErrorListener();
	}

	ScriptHandler.prototype = Object.create(ConfigHandler.prototype);
	ScriptHandler.prototype.constructor = ScriptHandler;
	ConfigHandler._registerClass('script', ScriptHandler);

	/**
	 * Fills out script config with default parameters from the declarations in
	 * the script code. Also adds externals config to data model config, so
	 * that Create can read them.
	 */
	ScriptHandler.prototype._specialPrepare = function(script, config) {
		config.options = config.options || {};
		// fill the rest of the parameters with default values
		if (script.externals && script.externals.parameters) {
			ScriptUtils.fillDefaultValues(config.options, script.externals.parameters);
		}
		if (config.body) {
			config._externals = script.externals;
		}
	};

	/**
	 * Creates a script data wrapper object to be used in the engine
	 */
	ScriptHandler.prototype._create = function() {
		return {
			externals: {},
			setup: null,
			update: null,
			run: null,
			cleanup: null,
			parameters: {},
			name: null
		};
	};

	/**
	 * Remove this script from the cache, and runs the cleanup method of the script.
	 * @param {string} ref the script guid
	 */
	ScriptHandler.prototype._remove = function(ref) {
		var script = this._objects[ref];
		if (script && script.cleanup && script.context) {
			script.cleanup(script.parameters, script.context, Scripts.getClasses());
			delete this._objects[ref];
		}
	};

	/**
	 * Update a user-defined script (not a script available in the engine).
	 * If the new body (in the data model config) differs from the cached body,
	 * the script will be reloaded (by means of a script tag).
	 *
	 * @param {object} script the cached engine script object
	 * @param {object} config the data model config
	 */
	ScriptHandler.prototype._updateFromCustom = function(script, config) {
		// No change, do nothing
		if (this._bodyCache[config.id] === config.body) {return script;}

		delete script.externals.errors;
		this._bodyCache[config.id] = config.body;

		// delete the old script tag and add a new one
		var oldScriptElement = document.getElementById(ScriptHandler.DOM_ID_PREFIX + config.id);
		if (oldScriptElement) {
			oldScriptElement.parentNode.removeChild(oldScriptElement);
		}

		// create this script collection if it does not exist yet
		if (!window._gooScriptFactories) {
			// this holds script factories in 'compiled' form
			window._gooScriptFactories = {};
		}

		// get a script factory in string form
		var scriptFactoryStr = [
			"window._gooScriptFactories['" + config.id + "'] = function () { 'use strict';",
			config.body,
			' var obj = {',
			'  externals: {}',
			' };',
			' if (typeof parameters !== "undefined") {',
			'  obj.externals.parameters = parameters;',
			' }',
			' if (typeof setup !== "undefined") {',
			'  obj.setup = setup;',
			' }',
			' if (typeof cleanup !== "undefined") {',
			'  obj.cleanup = cleanup;',
			' }',
			' if (typeof update !== "undefined") {',
			'  obj.update = update;',
			' }',
			' return obj;',
			'};'
		].join('\n');

		// create the element and add it to the page so the user can debug it
		// addition and execution of the script happens synchronously
		var newScriptElement = document.createElement('script');
		newScriptElement.id = ScriptHandler.DOM_ID_PREFIX + config.id;
		newScriptElement.innerHTML = scriptFactoryStr;
		this._currentScriptLoading = config.id;
		document.body.appendChild(newScriptElement);

		var newScript = window._gooScriptFactories[config.id];
		if (newScript) {
			try {
				newScript = newScript();
				script.id = config.id;
				script.externals = safeUp(newScript.externals);
				script.setup = newScript.setup;
				script.update = newScript.update;
				script.cleanup = newScript.cleanup;
				script.parameters = {};
				script.enabled = false;
			} catch(e) {
				var err = {
					message: e.toString()
				};
				// TODO Test if this works across browsers
				/**/
				var m = e.stack.split('\n')[1].match(/(\d+):\d+\)$/);
				if (m) {
					err.line = parseInt(m[1], 10) - 1;
				}
				/**/
				setError(script, err);
			}
			this._currentScriptLoading = null;
		}
		// generate names from external variable names
		if (script.externals) {
			ScriptUtils.fillDefaultNames(script.externals.parameters);
		}
		return script;
	};

	/**
	 * Update a script that is from the engine. Checks if the class name has changed
	 * and if so, creates a new script object from the new class.
	 * @param {object} script needs to have a className property
	 * @param {object} config data model config
	 */
	ScriptHandler.prototype._updateFromClass = function(script, config) {
		if (!script.externals || script.externals.name !== config.className) {
			var newScript = Scripts.create(config.className);
			if (!newScript) {
				throw 'Unrecognized script name';
			}
			script.id = config.id;
			script.externals = newScript.externals;
			script.setup = newScript.setup;
			script.update = newScript.update;
			script.run = newScript.run;
			script.cleanup = newScript.cleanup;
			script.parameters = newScript.parameters || {};
			script.enabled = false;

			// generate names from external variable names
			ScriptUtils.fillDefaultNames(script.externals.parameters);
		}

		return script;
	};

	ScriptHandler.prototype._update = function(ref, config, options) {
		var that = this;
		return ConfigHandler.prototype._update.call(this, ref, config, options).then(function(script) {
			if (!script) { return; }
			var promises = [];
			if (config.body && config.dependencies) {
				delete script.externals.dependencyErrors;
				for (var url in config.dependencies) {
					promises.push(that._addDependency(script, url, config.id));
				}
			}
			return RSVP.all(promises).then(function() {
				if (config.className) {
					that._updateFromClass(script, config, options);
				} else if (config.body) {
					that._updateFromCustom(script, config, options);
				}
				that._specialPrepare(script, config);
				script.name = config.name;
				if (script.externals.errors || script.externals.dependencyErrors) {
					SystemBus.emit('scriptError', {
						id: ref,
						errors: script.externals.errors,
						dependencyErrors: script.externals.dependencyErrors});
					return script;
				}
				else {
					SystemBus.emit('scriptError', {id: ref, errors: null});
				}
				_.extend(script.parameters, config.options);
				return script;
			});
		});
	};

	/**
	 * Loads an external javascript lib as a dependency to this script (if it's
	 * not already loaded). If the dependency fails to load, an error is set
	 * on the script.
	 * @param {object} script config
	 * @param {string} url location of the javascript lib
	 * @param {string} scriptId the guid of the script
	 * @return {RSVP.Promise} a promise that resolves when the dependency is loaded
	 */
	ScriptHandler.prototype._addDependency = function(script, url, scriptId) {
		var scriptElem = document.querySelector('script[src="'+url+'"]');
		if (scriptElem) {
			return PromiseUtil.createDummyPromise();
		}

		scriptElem = document.createElement('script');
		scriptElem.src = url;
		scriptElem.setAttribute('data-script-id', scriptId);

		var promise = new RSVP.Promise();
		scriptElem.onload = function() {
			promise.resolve();
		};
		scriptElem.onerror = function() {
			var err = {
				message: 'Could not load dependency',
				file: url
			};
			setError(script, err);
			scriptElem.parentNode.removeChild(scriptElem);
			promise.resolve();
		};
		document.body.appendChild(scriptElem);

		return promise;
	};

	/**
	 * Add a global error listener that catches script errors, and tries to match
	 * them to scripts loaded with this handler. If an error is registered, the
	 * script is reset and an error message is appended to it.
	 * @private
	 *
	 */
	ScriptHandler.prototype._addGlobalErrorListener = function() {
		var that = this;
		window.addEventListener('error', function(evt) {
			if (evt.filename) {
				var scriptElem = document.querySelector('script[src="'+evt.filename+'"]');
				if (scriptElem) {
					var scriptId = scriptElem.getAttribute('data-script-id');
					var script = that._objects[scriptId];
					if (script) {
						var error = {
							message: evt.message,
							line: evt.lineno,
							file: evt.filename
						};
						setError(script, error);
					}
					scriptElem.parentNode.removeChild(scriptElem);
				}
			}
			if (that._currentScriptLoading) {
				var oldScriptElement = document.getElementById(ScriptHandler.DOM_ID_PREFIX + that._currentScriptLoading);
				if (oldScriptElement) {
					oldScriptElement.parentNode.removeChild(oldScriptElement);
				}
				delete window._gooScriptFactories[that._currentScriptLoading];
				var script = that._objects[that._currentScriptLoading];
				var error = {
					message: evt.message,
					line: evt.lineno - 1
				};
				setError(script, error);
				that._currentScriptLoading = null;
			}
		});
	};

	var types = ['string', 'float', 'int', 'vec3', 'boolean'];
	/**
	 * Validate external parameters
	 * @private
	 */
	function safeUp(externals) {
		var	obj = {};
		var errors = externals.errors || [];
		if (typeof externals !== 'object') {
			return obj;
		}
		if (externals.parameters && !(externals.parameters instanceof Array)) {
			errors.push('externals.parameters needs to be an array');
		}
		if (errors.length) {
			obj.errors = errors;
			return obj;
		}
		if(!externals.parameters) {
			return obj;
		}
		obj.parameters = [];
		for (var i = 0; i < externals.parameters.length; i++) {
			var param = externals.parameters[i];
			if (typeof param.key !== 'string' || param.key.length === 0) {
				errors.push('parameter key needs to be string');
				continue;
			}
			if (param.name && typeof param.name !== 'string') {
				errors.push('parameter name needs to be string');
				continue;
			}
			if (types.indexOf(param.type) === -1) {
				errors.push('parameter type needs to be one of (' + types.join(', ') + ')');
				continue;
			}
			if (param.control && typeof param.control !== 'string') {
				errors.push('parameter control needs to be string');
				continue;
			}
			if (param.options && !(param.options instanceof Array)) {
				errors.push('parameter key needs to be array');
				continue;
			}
			if (param.min && isNaN(param.min)) {
				errors.push('parameter min needs to be number');
				continue;
			}
			if (param.max && isNaN(param.max)) {
				errors.push('parameter max needs to be number');
				continue;
			}
			if (param.scale && isNaN(param.scale)) {
				errors.push('parameter scale needs to be number');
				continue;
			}
			if (param.decimals && isNaN(param.decimals)) {
				errors.push('parameter decimals needs to be number');
				continue;
			}
			if (param.exponential !== undefined && typeof param.exponential !== 'boolean') {
				errors.push('parameter exponential needs to be boolean');
				continue;
			}
			if (param['default'] === undefined) {
				errors.push('parameter default is missing');
				continue;
			}
			obj.parameters.push(param);
		}
		if (errors.length) {
			obj.errors = errors;
		}
		return obj;
	}

	/**
	 * Flag a script with an error. The script will be disabled.
	 * @param {object} script
	 * @param {object} error
	 * @param {string} error.message
	 * @param {Number} [error.line]
	 * @param {string} [error.file]
	 * @private
	 */
	function setError(script, error) {
		if (error.file) {
			var message = error.message;
			if (error.line) {
				message += ' - on line ' + error.line;
			}
			script.externals.dependencyErrors = script.externals.dependencyErrors || {};
			script.externals.dependencyErrors[error.file] = message;
		} else {
			script.externals.errors = script.externals.errors || [];
			var message = error.message;
			if (error.line) {
				message += ' - on line ' + error.line;
			}
			script.externals.errors.push(message);

			script.setup = null;
			script.update = null;
			script.run = null;
			script.cleanup = null;

			script.parameters = {};
			script.enabled = false;
		}

	}

	ScriptHandler.DOM_ID_PREFIX = '_script_';

	return ScriptHandler;
});<commit_msg>Minor updates during review of #1123<commit_after>define([
	'goo/loaders/handlers/ConfigHandler',
	'goo/util/rsvp',
	'goo/scripts/OrbitCamControlScript',
	'goo/scripts/OrbitNPanControlScript',
	'goo/scripts/FlyControlScript',
	'goo/scripts/WASDControlScript',
	'goo/scripts/BasicControlScript',
	'goo/util/PromiseUtil',
	'goo/util/ObjectUtil',
	'goo/entities/SystemBus',

	'goo/scripts/ScriptUtils',
	'goo/scripts/Scripts'
],
/** @lends */
function(
	ConfigHandler,
	RSVP,
	OrbitCamControlScript,
	OrbitNPanControlScript,
	FlyControlScript,
	WASDControlScript,
	BasicControlScript,
	PromiseUtil,
	_,
	SystemBus,

	ScriptUtils,
	Scripts
) {
	"use strict";

	/**
	* @class
	* @private
	*/
	function ScriptHandler() {
		ConfigHandler.apply(this, arguments);
		this._bodyCache = {};
		this._currentScriptLoading = null;
		this._addGlobalErrorListener();
	}

	ScriptHandler.prototype = Object.create(ConfigHandler.prototype);
	ScriptHandler.prototype.constructor = ScriptHandler;
	ConfigHandler._registerClass('script', ScriptHandler);

	/**
	 * Fills out script config with default parameters from the declarations in
	 * the script code. Also adds externals config to data model config, so
	 * that Create can read them.
	 */
	ScriptHandler.prototype._specialPrepare = function(script, config) {
		config.options = config.options || {};
		// fill the rest of the parameters with default values
		if (script.externals && script.externals.parameters) {
			ScriptUtils.fillDefaultValues(config.options, script.externals.parameters);
		}
		if (config.body) {
			config._externals = script.externals;
		}
	};

	/**
	 * Creates a script data wrapper object to be used in the engine
	 */
	ScriptHandler.prototype._create = function() {
		return {
			externals: {},
			setup: null,
			update: null,
			run: null,
			cleanup: null,
			parameters: {},
			name: null
		};
	};

	/**
	 * Remove this script from the cache, and runs the cleanup method of the script.
	 * @param {string} ref the script guid
	 */
	ScriptHandler.prototype._remove = function(ref) {
		var script = this._objects[ref];
		if (script && script.cleanup && script.context) {
			script.cleanup(script.parameters, script.context, Scripts.getClasses());
			delete this._objects[ref];
		}
	};

	/**
	 * Update a user-defined script (not a script available in the engine).
	 * If the new body (in the data model config) differs from the cached body,
	 * the script will be reloaded (by means of a script tag).
	 *
	 * @param {object} script the cached engine script object
	 * @param {object} config the data model config
	 */
	ScriptHandler.prototype._updateFromCustom = function(script, config) {
		// No change, do nothing
		if (this._bodyCache[config.id] === config.body) {return script;}

		delete script.externals.errors;
		this._bodyCache[config.id] = config.body;

		// delete the old script tag and add a new one
		var oldScriptElement = document.getElementById(ScriptHandler.DOM_ID_PREFIX + config.id);
		if (oldScriptElement) {
			oldScriptElement.parentNode.removeChild(oldScriptElement);
		}

		// create this script collection if it does not exist yet
		if (!window._gooScriptFactories) {
			// this holds script factories in 'compiled' form
			window._gooScriptFactories = {};
		}

		// get a script factory in string form
		var scriptFactoryStr = [
			"window._gooScriptFactories['" + config.id + "'] = function () { 'use strict';",
			config.body,
			' var obj = {',
			'  externals: {}',
			' };',
			' if (typeof parameters !== "undefined") {',
			'  obj.externals.parameters = parameters;',
			' }',
			' if (typeof setup !== "undefined") {',
			'  obj.setup = setup;',
			' }',
			' if (typeof cleanup !== "undefined") {',
			'  obj.cleanup = cleanup;',
			' }',
			' if (typeof update !== "undefined") {',
			'  obj.update = update;',
			' }',
			' return obj;',
			'};'
		].join('\n');

		// create the element and add it to the page so the user can debug it
		// addition and execution of the script happens synchronously
		var newScriptElement = document.createElement('script');
		newScriptElement.id = ScriptHandler.DOM_ID_PREFIX + config.id;
		newScriptElement.innerHTML = scriptFactoryStr;
		this._currentScriptLoading = config.id;
		document.body.appendChild(newScriptElement);

		var newScript = window._gooScriptFactories[config.id];
		if (newScript) {
			try {
				newScript = newScript();
				script.id = config.id;
				script.externals = safeUp(newScript.externals);
				script.setup = newScript.setup;
				script.update = newScript.update;
				script.cleanup = newScript.cleanup;
				script.parameters = {};
				script.enabled = false;
			} catch(e) {
				var err = {
					message: e.toString()
				};
				// TODO Test if this works across browsers
				/**/
				var m = e.stack.split('\n')[1].match(/(\d+):\d+\)$/);
				if (m) {
					err.line = parseInt(m[1], 10) - 1;
				}
				/**/
				setError(script, err);
			}
			this._currentScriptLoading = null;
		}
		// generate names from external variable names
		if (script.externals) {
			ScriptUtils.fillDefaultNames(script.externals.parameters);
		}
		return script;
	};

	/**
	 * Update a script that is from the engine. Checks if the class name has changed
	 * and if so, creates a new script object from the new class.
	 * @param {object} script needs to have a className property
	 * @param {object} config data model config
	 */
	ScriptHandler.prototype._updateFromClass = function(script, config) {
		if (!script.externals || script.externals.name !== config.className) {
			var newScript = Scripts.create(config.className);
			if (!newScript) {
				throw 'Unrecognized script name';
			}
			script.id = config.id;
			script.externals = newScript.externals;
			script.setup = newScript.setup;
			script.update = newScript.update;
			script.run = newScript.run;
			script.cleanup = newScript.cleanup;
			script.parameters = newScript.parameters || {};
			script.enabled = false;

			// generate names from external variable names
			ScriptUtils.fillDefaultNames(script.externals.parameters);
		}

		return script;
	};

	ScriptHandler.prototype._update = function(ref, config, options) {
		var that = this;
		return ConfigHandler.prototype._update.call(this, ref, config, options).then(function(script) {
			if (!script) { return; }
			var promises = [];
			if (config.body && config.dependencies) {
				delete script.externals.dependencyErrors;
				for (var url in config.dependencies) {
					promises.push(that._addDependency(script, url, config.id));
				}
			}
			return RSVP.all(promises).then(function() {
				if (config.className) {
					that._updateFromClass(script, config, options);
				} else if (config.body) {
					that._updateFromCustom(script, config, options);
				}
				that._specialPrepare(script, config);
				script.name = config.name;
				if (script.externals.errors || script.externals.dependencyErrors) {
					SystemBus.emit('scriptError', {
						id: ref,
						errors: script.externals.errors,
						dependencyErrors: script.externals.dependencyErrors});
					return script;
				}
				else {
					SystemBus.emit('scriptError', {id: ref, errors: null});
				}
				_.extend(script.parameters, config.options);
				return script;
			});
		});
	};

	/**
	 * Loads an external javascript lib as a dependency to this script (if it's
	 * not already loaded). If the dependency fails to load, an error is set
	 * on the script.
	 * @param {object} script config
	 * @param {string} url location of the javascript lib
	 * @param {string} scriptId the guid of the script
	 * @return {RSVP.Promise} a promise that resolves when the dependency is loaded
	 */
	ScriptHandler.prototype._addDependency = function(script, url, scriptId) {
		var scriptElem = document.querySelector('script[src="'+url+'"]');
		if (scriptElem) {
			return PromiseUtil.createDummyPromise();
		}

		scriptElem = document.createElement('script');
		scriptElem.src = url;
		scriptElem.setAttribute('data-script-id', scriptId);

		var promise = new RSVP.Promise();
		scriptElem.onload = function() {
			promise.resolve();
		};
		scriptElem.onerror = function() {
			var err = {
				message: 'Could not load dependency',
				file: url
			};
			setError(script, err);
			scriptElem.parentNode.removeChild(scriptElem);
			promise.resolve();
		};
		document.body.appendChild(scriptElem);

		return promise;
	};

	/**
	 * Add a global error listener that catches script errors, and tries to match
	 * them to scripts loaded with this handler. If an error is registered, the
	 * script is reset and an error message is appended to it.
	 * @private
	 *
	 */
	ScriptHandler.prototype._addGlobalErrorListener = function() {
		var that = this;
		window.addEventListener('error', function(evt) {
			if (evt.filename) {
				var scriptElem = document.querySelector('script[src="'+evt.filename+'"]');
				if (scriptElem) {
					var scriptId = scriptElem.getAttribute('data-script-id');
					var script = that._objects[scriptId];
					if (script) {
						var error = {
							message: evt.message,
							line: evt.lineno,
							file: evt.filename
						};
						setError(script, error);
					}
					scriptElem.parentNode.removeChild(scriptElem);
				}
			}
			if (that._currentScriptLoading) {
				var oldScriptElement = document.getElementById(ScriptHandler.DOM_ID_PREFIX + that._currentScriptLoading);
				if (oldScriptElement) {
					oldScriptElement.parentNode.removeChild(oldScriptElement);
				}
				delete window._gooScriptFactories[that._currentScriptLoading];
				var script = that._objects[that._currentScriptLoading];
				var error = {
					message: evt.message,
					line: evt.lineno - 1
				};
				setError(script, error);
				that._currentScriptLoading = null;
			}
		});
	};

	var types = ['string', 'float', 'int', 'vec3', 'boolean'];
	/**
	 * Validate external parameters
	 * @private
	 */
	function safeUp(externals) {
		var	obj = {};
		var errors = externals.errors || [];
		if (typeof externals !== 'object') {
			return obj;
		}
		if (externals.parameters && !(externals.parameters instanceof Array)) {
			errors.push('externals.parameters needs to be an array');
		}
		if (errors.length) {
			obj.errors = errors;
			return obj;
		}
		if(!externals.parameters) {
			return obj;
		}
		obj.parameters = [];
		for (var i = 0; i < externals.parameters.length; i++) {
			var param = externals.parameters[i];
			if (typeof param.key !== 'string' || param.key.length === 0) {
				errors.push('parameter key needs to be string');
				continue;
			}
			if (param.name && typeof param.name !== 'string') {
				errors.push('parameter name needs to be string');
				continue;
			}
			if (types.indexOf(param.type) === -1) {
				errors.push('parameter type needs to be one of (' + types.join(', ') + ')');
				continue;
			}
			if (param.control && typeof param.control !== 'string') {
				errors.push('parameter control needs to be string');
				continue;
			}
			if (param.options && !(param.options instanceof Array)) {
				errors.push('parameter key needs to be array');
				continue;
			}
			if (param.min && isNaN(param.min)) {
				errors.push('parameter min needs to be number');
				continue;
			}
			if (param.max && isNaN(param.max)) {
				errors.push('parameter max needs to be number');
				continue;
			}
			if (param.scale && isNaN(param.scale)) {
				errors.push('parameter scale needs to be number');
				continue;
			}
			if (param.decimals && isNaN(param.decimals)) {
				errors.push('parameter decimals needs to be number');
				continue;
			}
			if (param.exponential !== undefined && typeof param.exponential !== 'boolean') {
				errors.push('parameter exponential needs to be boolean');
				continue;
			}
			if (param['default'] === undefined) {
				errors.push('parameter default is missing');
				continue;
			}
			obj.parameters.push(param);
		}
		if (errors.length) {
			obj.errors = errors;
		}
		return obj;
	}

	/**
	 * Flag a script with an error. The script will be disabled.
	 * @param {object} script
	 * @param {object} error
	 * @param {string} error.message
	 * @param {Number} [error.line]
	 * @param {string} [error.file]
	 * @private
	 */
	function setError(script, error) {
		if (error.file) {
			var message = error.message;
			if (error.line) {
				message += ' - on line ' + error.line;
			}
			script.externals.dependencyErrors = script.externals.dependencyErrors || {};
			script.externals.dependencyErrors[error.file] = error;
		} else {
			script.externals.errors = script.externals.errors || [];
			var message = error.message;
			if (error.line) {
				message += ' - on line ' + error.line;
			}
			script.externals.errors.push(error);

			script.setup = null;
			script.update = null;
			script.run = null;
			script.cleanup = null;

			script.parameters = {};
			script.enabled = false;
		}

	}

	ScriptHandler.DOM_ID_PREFIX = '_script_';

	return ScriptHandler;
});<|endoftext|>


<commit_before>/**
 *
 * @licstart  The following is the entire license notice for the JavaScript code in this file.
 *
 * Base prototypes of processors, converters, record store and record set for recordLoader
 *
 * Copyright (c) 2015 University Of Helsinki (The National Library Of Finland)
 *
 * This file is part of record-loader-prototypes
 *
 * record-loader-prototypes is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @licend  The above is the entire license notice
 * for the JavaScript code in this file.
 *
 **/

/* istanbul ignore next: umd wrapper */
(function (root, factory) {

    'use strict';

    if (typeof define === 'function' && define.amd) {
        define(['es6-polyfills/lib/polyfills/promise'], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory(require('es6-polyfills/lib/polyfills/promise'));
    }

}(this, factory));

function factory(Promise) {

    'use strict';

    return function(parameters) {
	return {
	    run: function()
	    {
		return Promise.resolve();
	    },
	    setRecordSet: function(record_set)
	    {
	    },
	    setRecordStore: function(record_store)
	    {
	    },
	    setConverter: function(converter)
	    {
	    },
	    setResults: function(result, persist)
	    {
	    },
	    setLogger: function(logger)
	    {
	    }
	};
    };

}<commit_msg>Remove obsoleted 'setResults' method (Results get passed in as arguments to 'run' method).<commit_after>/**
 *
 * @licstart  The following is the entire license notice for the JavaScript code in this file.
 *
 * Base prototypes of processors, converters, record store and record set for recordLoader
 *
 * Copyright (c) 2015 University Of Helsinki (The National Library Of Finland)
 *
 * This file is part of record-loader-prototypes
 *
 * record-loader-prototypes is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @licend  The above is the entire license notice
 * for the JavaScript code in this file.
 *
 **/

/* istanbul ignore next: umd wrapper */
(function (root, factory) {

    'use strict';

    if (typeof define === 'function' && define.amd) {
        define(['es6-polyfills/lib/polyfills/promise'], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory(require('es6-polyfills/lib/polyfills/promise'));
    }

}(this, factory));

function factory(Promise) {

    'use strict';

    return function(parameters) {
	return {
	    run: function()
	    {
		return Promise.resolve();
	    },
	    setRecordSet: function(record_set)
	    {
	    },
	    setRecordStore: function(record_store)
	    {
	    },
	    setConverter: function(converter)
	    {
	    },
	    setLogger: function(logger)
	    {
	    }
	};
    };

}<|endoftext|>


<commit_before>var crossbow  = require("../index");
var yaml      = require("../lib/yaml");
var utils     = crossbow.utils;
var through2  = require("through2");
var gutil     = require("gulp-util");
var File      = gutil.File;
var path      = require("path");
var Immutable = require("immutable");
var Q         = require("q");
var _         = require("lodash");
var errors    = require("../lib/errors").fails;

/**
 * @returns {Function}
 */
module.exports = function (opts) {

    opts        = opts        || {};
    opts.config = opts.config || {};

    var files = {};
    var stream;

    var site = opts.builder || crossbow.builder(opts);

    return through2.obj(function (file, enc, cb) {

        stream = this;

        if (file._contents) {
            var contents        = file._contents.toString();
            var relFilePath     = file.path.replace(file.cwd, "");
            relFilePath         = relFilePath.replace(/^\//, "");
            files[relFilePath]  = {stat: file.stat, content: contents};
        }
        cb();

    }, function (cb) {

        var queue      = [];
        var compileAll = false;

        Object.keys(files).forEach(function (key) {

            var existing = site.cache._items.has(key);
            var front;

            if (existing) {
                front = site.cache.byKey(key).get("front");
            }

            var added = site.add({
                type:    site.getType(key),
                key:     key,
                content: files[key].content,
                stat:    files[key].stat
            });

            if (front && !added.get("front").equals(front)) {
                compileAll = true;
            }

            queue.push(added);
        });

        if (!queue.length) {
            return;
        } else {
            if (queue.some(function (item) {
                return item.get("type") === "partial";
            }) || compileAll) {
                site.logger.info("Re-compiling all items");
                site.freeze();
                site.compileAll({
                    cb: function (err, out) {
                        if (err) {
                            return console.log("ERROR");
                        }
                        streampush(out, stream);
                        cb();
                    }
                });
            } else {
                var timestart = new Date().getTime();
                site.freeze();
                site.compileMany({
                    collection: queue,
                    cb: function (err, out) {
                        if (err) {
                            return console.log("ERROR");
                        }
                        site.logger.info("Compiling {yellow:%s} item%s took {yellow:%sms}",
                            queue.length,
                            queue.length > 1 ? "s" : "",
                            new Date().getTime() - timestart
                        );
                        streampush(out, stream);
                        cb();
                    }
                });
            }
        }
    });
};

/**
 * Push multiple files down stream
 * @param collection
 * @param stream
 */
function streampush (collection, stream) {
    collection.forEach(function (item) {
        stream.push(new File({
            cwd:  "./",
            base: "./",
            path: item.get("filepath"),
            contents: new Buffer(item.get("compiled"))
        }));
    });
}<commit_msg>Don't log compile times by default<commit_after>var crossbow  = require("../index");
var yaml      = require("../lib/yaml");
var utils     = crossbow.utils;
var through2  = require("through2");
var gutil     = require("gulp-util");
var File      = gutil.File;
var path      = require("path");
var Immutable = require("immutable");
var Q         = require("q");
var _         = require("lodash");
var errors    = require("../lib/errors").fails;

/**
 * @returns {Function}
 */
module.exports = function (opts) {

    opts        = opts        || {};
    opts.config = opts.config || {};

    var files = {};
    var stream;

    var site = opts.builder || crossbow.builder(opts);

    return through2.obj(function (file, enc, cb) {

        stream = this;

        if (file._contents) {
            var contents        = file._contents.toString();
            var relFilePath     = file.path.replace(file.cwd, "");
            relFilePath         = relFilePath.replace(/^\//, "");
            files[relFilePath]  = {stat: file.stat, content: contents};
        }
        cb();

    }, function (cb) {

        var queue      = [];
        var compileAll = false;

        Object.keys(files).forEach(function (key) {

            var existing = site.cache._items.has(key);
            var front;

            if (existing) {
                front = site.cache.byKey(key).get("front");
            }

            var added = site.add({
                type:    site.getType(key),
                key:     key,
                content: files[key].content,
                stat:    files[key].stat
            });

            if (front && !added.get("front").equals(front)) {
                compileAll = true;
            }

            queue.push(added);
        });

        if (!queue.length) {
            return;
        } else {
            if (queue.some(function (item) {
                return item.get("type") === "partial";
            }) || compileAll) {
                site.logger.debug("Re-compiling all items");
                site.freeze();
                site.compileAll({
                    cb: function (err, out) {
                        if (err) {
                            return console.log("ERROR");
                        }
                        streampush(out, stream);
                        cb();
                    }
                });
            } else {
                var timestart = new Date().getTime();
                site.freeze();
                site.compileMany({
                    collection: queue,
                    cb: function (err, out) {
                        if (err) {
                            return console.log("ERROR");
                        }
                        site.logger.debug("Compiling {yellow:%s} item%s took {yellow:%sms}",
                            queue.length,
                            queue.length > 1 ? "s" : "",
                            new Date().getTime() - timestart
                        );
                        streampush(out, stream);
                        cb();
                    }
                });
            }
        }
    });
};

/**
 * Push multiple files down stream
 * @param collection
 * @param stream
 */
function streampush (collection, stream) {
    collection.forEach(function (item) {
        stream.push(new File({
            cwd:  "./",
            base: "./",
            path: item.get("filepath"),
            contents: new Buffer(item.get("compiled"))
        }));
    });
}<|endoftext|>


<commit_before>define([
	'goo/math/Vector3',
	'goo/math/MathUtils',
	'goo/renderer/bounds/BoundingVolume',
	'goo/renderer/MeshData'
], function (
	Vector3,
	MathUtils,
	BoundingVolume,
	MeshData
) {
	'use strict';

	/**
	 * BoundingSphere defines a sphere that defines a container for a group of vertices of a particular piece of geometry. This
	 *        sphere defines a radius and a center.

	 *

	 *        A typical usage is to allow the class define the center and radius by calling either containAABB or
	 *        averagePoints. A call to computeFramePoint in turn calls containAABB.
	 */
	function BoundingSphere(center, radius) {
		BoundingVolume.call(this, center);
		this.radius = radius !== undefined ? radius : 1;

		// #ifdef DEBUG
		Object.seal(this);
		// #endif
	}

	var tmpVec = new Vector3();

	BoundingSphere.prototype = Object.create(BoundingVolume.prototype);
	BoundingSphere.prototype.constructor = BoundingSphere;

	BoundingSphere.prototype.computeFromPoints = function (verts) {
		var l = verts.length;
		if (l < 3) {
			return;
		}

		var min = this.min;
		var max = this.max;
		var vec = tmpVec;

		min.setDirect(verts[0], verts[1], verts[2]);
		max.setDirect(verts[0], verts[1], verts[2]);
		var x, y, z;
		for (var i = 0; i < l; i += 3) {
			x = verts[i + 0];
			y = verts[i + 1];
			z = verts[i + 2];
			min.data[0] = x < min.data[0] ? x : min.data[0];
			min.data[1] = y < min.data[1] ? y : min.data[1];
			min.data[2] = z < min.data[2] ? z : min.data[2];
			max.data[0] = x > max.data[0] ? x : max.data[0];
			max.data[1] = y > max.data[1] ? y : max.data[1];
			max.data[2] = z > max.data[2] ? z : max.data[2];
		}
		var newCenter = max.addVector(min).div(2.0);
		var size = 0, test;
		for (var i = 0; i < l; i += 3) {
			vec.setDirect(verts[i], verts[i + 1], verts[i + 2]);
			test = vec.subVector(newCenter).lengthSquared();
			if (test > size) {
				size = test;
			}
		}

		this.radius = Math.sqrt(size);
		this.center.setVector(newCenter);
	};

	(function () {
		var relativePoint = new Vector3();

		/**
		 * Method to test whether a point is inside the bounding box or not
		 * @param {Vector3} point
		 * @returns {boolean}
		 */
		BoundingSphere.prototype.containsPoint = function (point) {
			return relativePoint.setVector(point).subVector(this.center).lengthSquared() <= Math.pow(this.radius, 2);
		};
	})();

	BoundingSphere.prototype.computeFromPrimitives = function (data, section, indices, start, end) {
		if (end - start <= 0) {
			return;
		}

		var vertList = [];
		var store = [];
		var vertsPerPrimitive = MeshData.getVertexCount(data.indexModes[section]);

		var count = 0;
		for (var i = start; i < end; i++) {
			store = data.getPrimitiveVertices(indices[i], section, store);
			for (var j = 0; j < vertsPerPrimitive; j++) {
				vertList[count++] = new Vector3().set(store[j]);
			}
		}

		this.averagePoints(vertList);
	};

	BoundingSphere.prototype.averagePoints = function (points) {
		this.center.set(points[0]);

		for (var i = 1; i < points.length; i++) {
			this.center.add(points[i]);
		}

		var quantity = 1.0 / points.length;
		this.center.scale(quantity);

		var maxRadiusSqr = 0.0;
		for (var i = 0; i < points.length; i++) {
			var diff = Vector3.sub(points[i], this.center, tmpVec);
			var radiusSqr = diff.lengthSquared();
			if (radiusSqr > maxRadiusSqr) {
				maxRadiusSqr = radiusSqr;
			}
		}

		this.radius = Math.sqrt(maxRadiusSqr) + 0.00001;
	};

	BoundingSphere.prototype.transform = function (transform, bound) {
		if (bound === null) {
			bound = new BoundingSphere();
		}

		transform.applyForward(this.center, bound.center);

		var scale = transform.scale;
		bound.radius = Math.abs(this._maxAxis(scale) * this.radius);

		return bound;
	};

	BoundingSphere.prototype.whichSide = function (plane) {
		var planeData = plane.normal.data;
		var pointData = this.center.data;
		var distance = planeData[0] * pointData[0] + planeData[1] * pointData[1] + planeData[2] * pointData[2] - plane.constant;

		if (distance < -this.radius) {
			return BoundingVolume.Inside;
		} else if (distance > this.radius) {
			return BoundingVolume.Outside;
		} else {
			return BoundingVolume.Intersects;
		}
	};

	BoundingSphere.prototype._pseudoDistance = function (plane, point) {
		return plane.normal.x * point.x + plane.normal.y * point.y + plane.normal.z * point.z - plane.constant;
	};

	BoundingSphere.prototype._maxAxis = function (scale) {
		return Math.max(Math.abs(scale.x), Math.max(Math.abs(scale.y), Math.abs(scale.z)));
	};

	BoundingSphere.prototype.toString = function () {
		var x = Math.round(this.center.x * 10) / 10;
		var y = Math.round(this.center.y * 10) / 10;
		var z = Math.round(this.center.z * 10) / 10;
		var radius = Math.round(this.radius * 10) / 10;

		return '[' + x + ',' + y + ',' + z + ']' + ' - ' + radius;
	};

	BoundingSphere.prototype.intersects = function (bv) {
		return bv.intersectsSphere(this);
	};

	BoundingSphere.prototype.intersectsBoundingBox = function (bb) {
		// bb.min/max aren't updated properly; have to do it here for now
		bb.min.x = bb.center.x - bb.xExtent;
		bb.min.y = bb.center.y - bb.yExtent;
		bb.min.z = bb.center.z - bb.zExtent;

		bb.max.x = bb.center.x + bb.xExtent;
		bb.max.y = bb.center.y + bb.yExtent;
		bb.max.z = bb.center.z + bb.zExtent;

		var rs = Math.pow(this.radius, 2);
		var dmin = 0;

		if (this.center.x < bb.min.x) {
			dmin += Math.pow(this.center.x - bb.min.x, 2);
		} else if (this.center.x > bb.max.x) {
			dmin += Math.pow(this.center.x - bb.max.x, 2);
		}

		if (this.center.y < bb.min.y) {
			dmin += Math.pow(this.center.y - bb.min.y, 2);
		} else if (this.center.y > bb.max.y) {
			dmin += Math.pow(this.center.y - bb.max.y, 2);
		}

		if (this.center.z < bb.min.z) {
			dmin += Math.pow(this.center.z - bb.min.z, 2);
		} else if (this.center.z > bb.max.z) {
			dmin += Math.pow(this.center.z - bb.max.z, 2);
		}

		return dmin <= rs;
	};

	BoundingSphere.prototype.intersectsSphere = function (bs) {
		var diff = tmpVec.setVector(this.center).subVector(bs.center);
		var rsum = this.radius + bs.radius;
		return diff.dot(diff) <= rsum * rsum;
		//return this.center.distanceSquared(bs.center) <= rsum * rsum;
	};

	BoundingSphere.prototype.intersectsRay = function (ray) {
		if (!this.center) {
			return false;
		}

		var diff = new Vector3().copy(ray.origin).sub(this.center);
		var a = diff.dot(diff) - this.radius * this.radius;
		if (a <= 0.0) {
			// in sphere
			return true;
		}

		// outside sphere
		var b = ray.direction.dot(diff);
		if (b >= 0.0) {
			return false;
		}
		return b * b >= a;
	};

	BoundingSphere.prototype.intersectsRayWhere = function (ray) {
		var diff = new Vector3().copy(ray.origin).sub(this.center);
		var a = diff.dot(diff) - this.radius * this.radius;
		var a1, discr, root;
		if (a <= 0.0) {
			// inside sphere
			a1 = ray.direction.dot(diff);
			discr = a1 * a1 - a;
			root = Math.sqrt(discr);
			var distances = [root - a1];
			var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin)];
			return {
				"distances": distances,
				"points": points
			};
		}

		a1 = ray.direction.dot(diff);
		if (a1 >= 0.0) {
			// No intersection
			return null;
		}

		discr = a1 * a1 - a;
		if (discr < 0.0) {
			return null;
		} else if (discr >= 0.00001) {
			root = Math.sqrt(discr);
			var distances = [-a1 - root, -a1 + root];
			var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin),
				new Vector3().copy(ray.direction).scale(distances[1]).add(ray.origin)];
			return {
				"distances": distances,
				"points": points
			};
		}

		var distances = [-a1];
		var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin)];
		return {
			"distances": distances,
			"points": points
		};
	};

	BoundingSphere.prototype.merge = function (bv) {
		if (bv instanceof BoundingSphere) {
			return this.mergeSphere(bv.center, bv.radius, this);
		} else {
			var boxRadius = tmpVec.setDirect(bv.xExtent, bv.yExtent, bv.zExtent).length();
			return this.mergeSphere(bv.center, boxRadius, this);
		}
	};

	BoundingSphere.prototype.mergeSphere = function (center, radius, store) {
		if (!store) {
			store = new BoundingSphere();
		}

		var diff = tmpVec.setVector(center).subVector(this.center);
		var lengthSquared = diff.lengthSquared();
		var radiusDiff = radius - this.radius;
		var radiusDiffSqr = radiusDiff * radiusDiff;

		// if one sphere wholly contains the other
		if (radiusDiffSqr >= lengthSquared) {
			// if we contain the other
			if (radiusDiff <= 0.0) {
				store.center.setVector(this.center);
				store.radius = this.radius;
				return store;
			}
			// else the other contains us
			else {
				store.center.setVector(center);
				store.radius = radius;
				return store;
			}
		}

		// distance between sphere centers
		var length = Math.sqrt(lengthSquared);

		// init a center var using our center
		var rCenter = store.center;

		// if our centers are at least a tiny amount apart from each other...
		if (length > MathUtils.EPSILON) {
			// place us between the two centers, weighted by radii
			var coeff = (length + radiusDiff) / (2.0 * length);
			rCenter.addVector(diff.scale(coeff));
		}

		// Set radius
		store.radius = 0.5 * (length + this.radius + radius);

		return store;
	};

	/**
	 * Copies data from another bounding sphere
	 * @param {BoundingSphere} source bounding sphere to copy from
	 * @returns {BoundingSphere} Returns self to allow chaining
	 */
	BoundingSphere.prototype.copy = function (source) {
		BoundingVolume.prototype.copy.call(this, source);
		this.radius = source.radius;
		return this;
	};

	// ---
	var warned = false;

	/**
	 * Returns a clone of this bounding sphere
	 * @returns {BoundingSphere}
	 */
	BoundingSphere.prototype.clone = function () {
		if (arguments.length > 0 && !warned) {
			warned = true;
			console.warn(
				'BoundingSphere::clone no longer takes an optional "store" parameter; ' +
				'please use BoundingSphere::copy instead'
			);
		}
		// center appears to be shared but it really isn't since the BoundingVolume constructor clones it
		// when/if that ever changes this needs adapted accordingly
		return new BoundingSphere(this.center, this.radius);
	};

	return BoundingSphere;
});<commit_msg>Forgot to add index opt<commit_after>define([
	'goo/math/Vector3',
	'goo/math/MathUtils',
	'goo/renderer/bounds/BoundingVolume',
	'goo/renderer/MeshData'
], function (
	Vector3,
	MathUtils,
	BoundingVolume,
	MeshData
) {
	'use strict';

	/**
	 * BoundingSphere defines a sphere that defines a container for a group of vertices of a particular piece of geometry. This
	 *        sphere defines a radius and a center.

	 *

	 *        A typical usage is to allow the class define the center and radius by calling either containAABB or
	 *        averagePoints. A call to computeFramePoint in turn calls containAABB.
	 */
	function BoundingSphere(center, radius) {
		BoundingVolume.call(this, center);
		this.radius = radius !== undefined ? radius : 1;

		// #ifdef DEBUG
		Object.seal(this);
		// #endif
	}

	var tmpVec = new Vector3();

	BoundingSphere.prototype = Object.create(BoundingVolume.prototype);
	BoundingSphere.prototype.constructor = BoundingSphere;

	BoundingSphere.prototype.computeFromPoints = function (verts) {
		var l = verts.length;
		if (l < 3) {
			return;
		}

		var min = this.min;
		var max = this.max;
		var vec = tmpVec;

		min.setDirect(verts[0], verts[1], verts[2]);
		max.setDirect(verts[0], verts[1], verts[2]);
		var x, y, z;
		for (var i = 3; i < l; i += 3) {
			x = verts[i + 0];
			y = verts[i + 1];
			z = verts[i + 2];
			min.data[0] = x < min.data[0] ? x : min.data[0];
			min.data[1] = y < min.data[1] ? y : min.data[1];
			min.data[2] = z < min.data[2] ? z : min.data[2];
			max.data[0] = x > max.data[0] ? x : max.data[0];
			max.data[1] = y > max.data[1] ? y : max.data[1];
			max.data[2] = z > max.data[2] ? z : max.data[2];
		}
		var newCenter = max.addVector(min).div(2.0);
		var size = 0, test;
		for (var i = 0; i < l; i += 3) {
			vec.setDirect(verts[i], verts[i + 1], verts[i + 2]);
			test = vec.subVector(newCenter).lengthSquared();
			if (test > size) {
				size = test;
			}
		}

		this.radius = Math.sqrt(size);
		this.center.setVector(newCenter);
	};

	(function () {
		var relativePoint = new Vector3();

		/**
		 * Method to test whether a point is inside the bounding box or not
		 * @param {Vector3} point
		 * @returns {boolean}
		 */
		BoundingSphere.prototype.containsPoint = function (point) {
			return relativePoint.setVector(point).subVector(this.center).lengthSquared() <= Math.pow(this.radius, 2);
		};
	})();

	BoundingSphere.prototype.computeFromPrimitives = function (data, section, indices, start, end) {
		if (end - start <= 0) {
			return;
		}

		var vertList = [];
		var store = [];
		var vertsPerPrimitive = MeshData.getVertexCount(data.indexModes[section]);

		var count = 0;
		for (var i = start; i < end; i++) {
			store = data.getPrimitiveVertices(indices[i], section, store);
			for (var j = 0; j < vertsPerPrimitive; j++) {
				vertList[count++] = new Vector3().set(store[j]);
			}
		}

		this.averagePoints(vertList);
	};

	BoundingSphere.prototype.averagePoints = function (points) {
		this.center.set(points[0]);

		for (var i = 1; i < points.length; i++) {
			this.center.add(points[i]);
		}

		var quantity = 1.0 / points.length;
		this.center.scale(quantity);

		var maxRadiusSqr = 0.0;
		for (var i = 0; i < points.length; i++) {
			var diff = Vector3.sub(points[i], this.center, tmpVec);
			var radiusSqr = diff.lengthSquared();
			if (radiusSqr > maxRadiusSqr) {
				maxRadiusSqr = radiusSqr;
			}
		}

		this.radius = Math.sqrt(maxRadiusSqr) + 0.00001;
	};

	BoundingSphere.prototype.transform = function (transform, bound) {
		if (bound === null) {
			bound = new BoundingSphere();
		}

		transform.applyForward(this.center, bound.center);

		var scale = transform.scale;
		bound.radius = Math.abs(this._maxAxis(scale) * this.radius);

		return bound;
	};

	BoundingSphere.prototype.whichSide = function (plane) {
		var planeData = plane.normal.data;
		var pointData = this.center.data;
		var distance = planeData[0] * pointData[0] + planeData[1] * pointData[1] + planeData[2] * pointData[2] - plane.constant;

		if (distance < -this.radius) {
			return BoundingVolume.Inside;
		} else if (distance > this.radius) {
			return BoundingVolume.Outside;
		} else {
			return BoundingVolume.Intersects;
		}
	};

	BoundingSphere.prototype._pseudoDistance = function (plane, point) {
		return plane.normal.x * point.x + plane.normal.y * point.y + plane.normal.z * point.z - plane.constant;
	};

	BoundingSphere.prototype._maxAxis = function (scale) {
		return Math.max(Math.abs(scale.x), Math.max(Math.abs(scale.y), Math.abs(scale.z)));
	};

	BoundingSphere.prototype.toString = function () {
		var x = Math.round(this.center.x * 10) / 10;
		var y = Math.round(this.center.y * 10) / 10;
		var z = Math.round(this.center.z * 10) / 10;
		var radius = Math.round(this.radius * 10) / 10;

		return '[' + x + ',' + y + ',' + z + ']' + ' - ' + radius;
	};

	BoundingSphere.prototype.intersects = function (bv) {
		return bv.intersectsSphere(this);
	};

	BoundingSphere.prototype.intersectsBoundingBox = function (bb) {
		// bb.min/max aren't updated properly; have to do it here for now
		bb.min.x = bb.center.x - bb.xExtent;
		bb.min.y = bb.center.y - bb.yExtent;
		bb.min.z = bb.center.z - bb.zExtent;

		bb.max.x = bb.center.x + bb.xExtent;
		bb.max.y = bb.center.y + bb.yExtent;
		bb.max.z = bb.center.z + bb.zExtent;

		var rs = Math.pow(this.radius, 2);
		var dmin = 0;

		if (this.center.x < bb.min.x) {
			dmin += Math.pow(this.center.x - bb.min.x, 2);
		} else if (this.center.x > bb.max.x) {
			dmin += Math.pow(this.center.x - bb.max.x, 2);
		}

		if (this.center.y < bb.min.y) {
			dmin += Math.pow(this.center.y - bb.min.y, 2);
		} else if (this.center.y > bb.max.y) {
			dmin += Math.pow(this.center.y - bb.max.y, 2);
		}

		if (this.center.z < bb.min.z) {
			dmin += Math.pow(this.center.z - bb.min.z, 2);
		} else if (this.center.z > bb.max.z) {
			dmin += Math.pow(this.center.z - bb.max.z, 2);
		}

		return dmin <= rs;
	};

	BoundingSphere.prototype.intersectsSphere = function (bs) {
		var diff = tmpVec.setVector(this.center).subVector(bs.center);
		var rsum = this.radius + bs.radius;
		return diff.dot(diff) <= rsum * rsum;
		//return this.center.distanceSquared(bs.center) <= rsum * rsum;
	};

	BoundingSphere.prototype.intersectsRay = function (ray) {
		if (!this.center) {
			return false;
		}

		var diff = new Vector3().copy(ray.origin).sub(this.center);
		var a = diff.dot(diff) - this.radius * this.radius;
		if (a <= 0.0) {
			// in sphere
			return true;
		}

		// outside sphere
		var b = ray.direction.dot(diff);
		if (b >= 0.0) {
			return false;
		}
		return b * b >= a;
	};

	BoundingSphere.prototype.intersectsRayWhere = function (ray) {
		var diff = new Vector3().copy(ray.origin).sub(this.center);
		var a = diff.dot(diff) - this.radius * this.radius;
		var a1, discr, root;
		if (a <= 0.0) {
			// inside sphere
			a1 = ray.direction.dot(diff);
			discr = a1 * a1 - a;
			root = Math.sqrt(discr);
			var distances = [root - a1];
			var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin)];
			return {
				"distances": distances,
				"points": points
			};
		}

		a1 = ray.direction.dot(diff);
		if (a1 >= 0.0) {
			// No intersection
			return null;
		}

		discr = a1 * a1 - a;
		if (discr < 0.0) {
			return null;
		} else if (discr >= 0.00001) {
			root = Math.sqrt(discr);
			var distances = [-a1 - root, -a1 + root];
			var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin),
				new Vector3().copy(ray.direction).scale(distances[1]).add(ray.origin)];
			return {
				"distances": distances,
				"points": points
			};
		}

		var distances = [-a1];
		var points = [new Vector3().copy(ray.direction).scale(distances[0]).add(ray.origin)];
		return {
			"distances": distances,
			"points": points
		};
	};

	BoundingSphere.prototype.merge = function (bv) {
		if (bv instanceof BoundingSphere) {
			return this.mergeSphere(bv.center, bv.radius, this);
		} else {
			var boxRadius = tmpVec.setDirect(bv.xExtent, bv.yExtent, bv.zExtent).length();
			return this.mergeSphere(bv.center, boxRadius, this);
		}
	};

	BoundingSphere.prototype.mergeSphere = function (center, radius, store) {
		if (!store) {
			store = new BoundingSphere();
		}

		var diff = tmpVec.setVector(center).subVector(this.center);
		var lengthSquared = diff.lengthSquared();
		var radiusDiff = radius - this.radius;
		var radiusDiffSqr = radiusDiff * radiusDiff;

		// if one sphere wholly contains the other
		if (radiusDiffSqr >= lengthSquared) {
			// if we contain the other
			if (radiusDiff <= 0.0) {
				store.center.setVector(this.center);
				store.radius = this.radius;
				return store;
			}
			// else the other contains us
			else {
				store.center.setVector(center);
				store.radius = radius;
				return store;
			}
		}

		// distance between sphere centers
		var length = Math.sqrt(lengthSquared);

		// init a center var using our center
		var rCenter = store.center;

		// if our centers are at least a tiny amount apart from each other...
		if (length > MathUtils.EPSILON) {
			// place us between the two centers, weighted by radii
			var coeff = (length + radiusDiff) / (2.0 * length);
			rCenter.addVector(diff.scale(coeff));
		}

		// Set radius
		store.radius = 0.5 * (length + this.radius + radius);

		return store;
	};

	/**
	 * Copies data from another bounding sphere
	 * @param {BoundingSphere} source bounding sphere to copy from
	 * @returns {BoundingSphere} Returns self to allow chaining
	 */
	BoundingSphere.prototype.copy = function (source) {
		BoundingVolume.prototype.copy.call(this, source);
		this.radius = source.radius;
		return this;
	};

	// ---
	var warned = false;

	/**
	 * Returns a clone of this bounding sphere
	 * @returns {BoundingSphere}
	 */
	BoundingSphere.prototype.clone = function () {
		if (arguments.length > 0 && !warned) {
			warned = true;
			console.warn(
				'BoundingSphere::clone no longer takes an optional "store" parameter; ' +
				'please use BoundingSphere::copy instead'
			);
		}
		// center appears to be shared but it really isn't since the BoundingVolume constructor clones it
		// when/if that ever changes this needs adapted accordingly
		return new BoundingSphere(this.center, this.radius);
	};

	return BoundingSphere;
});<|endoftext|>


<commit_before>import { httpErrorToStr, getOidcToken, checkDicomFile } from '../utils/helpers';
const DICOM = require('dicomweb-client');

class DicomUploadService {
  setOidcStorageKey(oidcStorageKey) {
    /* eslint-disable */
    if (!oidcStorageKey) console.error('OIDC storage key is empty');
    this.oidcStorageKey = oidcStorageKey;
  }

  async smartUpload(files, url, authToken, uploadCallback, cancellationToken) {
    /* eslint-disable */
    const CHUNK_SIZE = 1; // Only one file per request is supported so far
    const MAX_PARALLEL_JOBS = 50; // FIXME: tune MAX_PARALLEL_JOBS number
    //
    let filesArray = Array.from(files);
    if (filesArray.length === 0) {
      console.warn('No files are supplied for uploading');
      return;
    }
    let parallelJobsCount = Math.min(filesArray.length, MAX_PARALLEL_JOBS);
    let completed = false;

    const processJob = async (resolve, reject) => {
      while (filesArray.length > 0) {
        if (cancellationToken.get()) return;
        let chunk = filesArray.slice(0, CHUNK_SIZE);
        filesArray = filesArray.slice(CHUNK_SIZE);
        let error = null;
        try {
          if (chunk.length > 1) throw new Error('Not implemented');
          if (chunk.length === 1)
            await this.simpleUpload(chunk[0], url, authToken);
        } catch (err) {
          // It looks like a stupid bug of Babel that err is not an actual Exception object
          error = httpErrorToStr(err);
        }
        chunk.forEach(file => uploadCallback(file.fileId, error));
        if (!completed && filesArray.length === 0) {
          completed = true;
          resolve();
          return;
        }
      }
    };

    await new Promise(resolve => {
      for (let i = 0; i < parallelJobsCount; i++) {
        processJob(resolve);
      }
    });
  }

  async simpleUpload(file, url, authToken) {
    /* eslint-disable */
    const client = this.getClient(url);
    const loadedFile = await this.readFile(file);
    const content = loadedFile.content;
    if (!checkDicomFile(content))
      throw new Error('The file has a wrong DICOM header');
    await client.storeInstances({ datasets: [content] });
  }

  readFile(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => {
        resolve({
          name: file.name,
          size: file.size,
          type: file.type,
          content: reader.result,
        });
      };
      reader.onerror = error => reject(error);
      reader.readAsArrayBuffer(file);
    });
  }

  getClient(url) {
    if (!this.oidcStorageKey) throw new Error('OIDC storage key is not set');
    const accessToken = getOidcToken(this.oidcStorageKey);
    if (!accessToken) throw new Error('OIDC access_token is not set');
    return new DICOM.api.DICOMwebClient({
      url,
      headers: { Authorization: 'Bearer ' + accessToken },
    });
  }
}

export default new DicomUploadService();
<commit_msg>fix(DicomUploadService): Remove require usage (#649)<commit_after>import { httpErrorToStr, getOidcToken, checkDicomFile } from '../utils/helpers';
import { api } from 'dicomweb-client';

class DicomUploadService {
  setOidcStorageKey(oidcStorageKey) {
    /* eslint-disable */
    if (!oidcStorageKey) console.error('OIDC storage key is empty');
    this.oidcStorageKey = oidcStorageKey;
  }

  async smartUpload(files, url, authToken, uploadCallback, cancellationToken) {
    /* eslint-disable */
    const CHUNK_SIZE = 1; // Only one file per request is supported so far
    const MAX_PARALLEL_JOBS = 50; // FIXME: tune MAX_PARALLEL_JOBS number
    //
    let filesArray = Array.from(files);
    if (filesArray.length === 0) {
      console.warn('No files are supplied for uploading');
      return;
    }
    let parallelJobsCount = Math.min(filesArray.length, MAX_PARALLEL_JOBS);
    let completed = false;

    const processJob = async (resolve, reject) => {
      while (filesArray.length > 0) {
        if (cancellationToken.get()) return;
        let chunk = filesArray.slice(0, CHUNK_SIZE);
        filesArray = filesArray.slice(CHUNK_SIZE);
        let error = null;
        try {
          if (chunk.length > 1) throw new Error('Not implemented');
          if (chunk.length === 1)
            await this.simpleUpload(chunk[0], url, authToken);
        } catch (err) {
          // It looks like a stupid bug of Babel that err is not an actual Exception object
          error = httpErrorToStr(err);
        }
        chunk.forEach(file => uploadCallback(file.fileId, error));
        if (!completed && filesArray.length === 0) {
          completed = true;
          resolve();
          return;
        }
      }
    };

    await new Promise(resolve => {
      for (let i = 0; i < parallelJobsCount; i++) {
        processJob(resolve);
      }
    });
  }

  async simpleUpload(file, url, authToken) {
    /* eslint-disable */
    const client = this.getClient(url);
    const loadedFile = await this.readFile(file);
    const content = loadedFile.content;
    if (!checkDicomFile(content))
      throw new Error('The file has a wrong DICOM header');
    await client.storeInstances({ datasets: [content] });
  }

  readFile(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => {
        resolve({
          name: file.name,
          size: file.size,
          type: file.type,
          content: reader.result,
        });
      };
      reader.onerror = error => reject(error);
      reader.readAsArrayBuffer(file);
    });
  }

  getClient(url) {
    if (!this.oidcStorageKey) throw new Error('OIDC storage key is not set');
    const accessToken = getOidcToken(this.oidcStorageKey);
    if (!accessToken) throw new Error('OIDC access_token is not set');
    return new api.DICOMwebClient({
      url,
      headers: { Authorization: 'Bearer ' + accessToken },
    });
  }
}

export default new DicomUploadService();
<|endoftext|>


<commit_before>/*
 *
 * https://github.com/arnogues/html-components
 *
 * Copyright (c) 2014 Arnaud Gueras
 * Licensed under the MIT license.
 *
 * TODO : add possibility to use collections via specifics nodes
 * TODO : add option for template language (possibility to replace handlebars by another template engine)
 * TODO : documentation
 */

'use strict';

var cheerio = require('cheerio'),
    _ = require('lodash'),
    path = require('path'),
    Handlebars = require('handlebars'),
    mkdirp = require('mkdirp'),
    glob = require('glob-all'),
    fs = require('fs');

var HTMLComponents = function () {
    this.init.apply(this, arguments);
};

HTMLComponents.prototype = {
    constructor: HTMLComponents.prototype.constructor,
    options: {
        componentsFolder: 'components',
        srcDir: '',
        destDir: '',
        attrNodePrefix: '_',
        files: ['*.html']
    },

    /**
     * Constructor
     */
    init: function (options) {
        this.options = _.merge({}, this.options, options);
    },

    /**
     * Generate list of tags from a directory
     * @returns {Array}
     */
    tags: null,
    initTags: function () {
        if (!this.tags) {
            var filesList = fs.readdirSync(this.options.componentsFolder);
            this.tags = filesList.map(function (filename) {
                return filename.replace(/\.hbs$/, '');
            }).sort();
        }
    },

    /**
     * Parse all html files of srcDir and write them into destDir
     * @param {String} srcDir source directory
     * @param {String} destDir destination directory
     * @param {Array} patterns files mask, it follows https://github.com/isaacs/node-glob options
     */
    processDirectory: function (patterns, srcDir, destDir) {
        var _this = this;
        var files = glob.sync(patterns, {cwd: srcDir});
        files.forEach(function (file) {
            _this.processFile(file, srcDir, destDir);
        });
    },

    /**
     * Process one file
     * @param filePath the relative file path from srcFolder,
     * @param srcFolder the source folder
     * @param destFolder the destination folder
     *
     * @example
     *      htmlComponents.processFile('myfile.html', 'test', '.tmp');
     *      htmlComponents.processFile('mysubfolder/folder/myfile.html', 'app', 'dist');
     *
     */
    processFile: function (filePath, srcFolder, destFolder) {
        var html = fs.readFileSync(path.join(srcFolder, filePath), {encoding: 'utf-8'});
        var newHTML = this.processHTML(html);
        mkdirp.sync(path.dirname(path.join(destFolder, filePath)));
        fs.writeFileSync(path.join(destFolder, filePath), newHTML, {encoding: 'utf-8'});
    },

    /**
     * Process the HTML by using list of tags
     * @param {String} html String of HTML
     * @returns {String}
     */
    processHTML: function (html) {
        var _this = this;
        this.initTags();
        var $ = cheerio.load(html);
        var nodesToProcess = $(this.tags.join(','));
        nodesToProcess.each(function () {
            var html = _this.processNode($(this), $);
            html = _this.processHTML(html);
            $(this).replaceWith(html);
        });
        return $.html();
    },

    /**
     * Transform HTML custom tag into parsed html from template
     * @param node element
     * @param $ cheerio object
     */
    processNode: function (node, $) {
        var context = this.processAttributes(node, $);
        var nodeName = node[0].name;
        var type = context.type;
        var template = Handlebars.compile(this.getTemplate(nodeName, type));
        return template(context);
    },

    processAttributes: function (node, $) {
        var attr = node.attr();
        var nodesAttr = this.processNodesAsAttributes(node, $);
        attr = _.merge(attr, nodesAttr);
        attr = this.fixAttributesObject(attr);
        //merge data object from attr into a string
        if (attr.data) {
            attr.dataStr = this.objectToAttributeString('data-', attr.data);
        }
        return attr;
    },

    /**
     * transform all children nodes of the object into attributes. The nodes must begin by a specific string. By default it's _
     * @param node
     * @param $ (cheerio object)
     * @returns {{}}
     */
    processNodesAsAttributes: function (node, $) {
        var obj = {};
        var regexp = new RegExp('^' + this.options.attrNodePrefix.replace(/([\[\]\$])/g, '\\$1'));
        node.children().each(function () {
            var $this = $(this);
            if (regexp.test(this.name)) {
                var name = this.name.replace(regexp, '');
                obj[name] = $this.html();
                $this.remove();
            }
            //process items
            if(this.name==='item') {
                if(!obj.items) {
                    obj.items = [];
                }
                obj.items.push({
                    html:$this.html(),
                    value:$this.attr('value')
                });
                $this.remove();
            }
        });
        obj.html = node.html();
        return obj;
    },

    /**
     *
     * @param attr
     * @returns {{}}
     */
    fixAttributesObject: function (attr) {
        var obj = {};
        for (var name in attr) {
            if (attr.hasOwnProperty(name)) {
                var dataRegExp = /^data-/;
                if (dataRegExp.test(name)) {
                    if (!obj.data) {
                        obj.data = {};
                    }
                    var dataName = name.replace(dataRegExp, '');
                    obj.data[dataName] = attr[name];
                }
                obj[name] = attr[name];
            }
        }

        return obj;
    },

    getTemplate: function (name, type) {
        var filepath = path.normalize(path.join(this.options.componentsFolder, name + (type ? '/' + type : '') + '.hbs'));
        return fs.readFileSync(filepath, {encoding: 'utf-8'});
    },

    objectToAttributeString: function (prefix, obj) {
        var str = [];
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                str.push(prefix + key + '="' + obj[key] + '"');
            }
        }
        return str.join(' ');
    }
};

module.exports = HTMLComponents;<commit_msg>small fix<commit_after>/*
 *
 * https://github.com/arnogues/html-components
 *
 * Copyright (c) 2014 Arnaud Gueras
 * Licensed under the MIT license.
 *
 * TODO : add possibility to use collections via specifics nodes
 * TODO : add option for template language (possibility to replace handlebars by another template engine)
 * TODO : documentation
 */

'use strict';

var cheerio = require('cheerio'),
    _ = require('lodash'),
    path = require('path'),
    Handlebars = require('handlebars'),
    mkdirp = require('mkdirp'),
    glob = require('glob-all'),
    fs = require('fs');

var HTMLComponents = function () {
    this.init.apply(this, arguments);
};

HTMLComponents.prototype = {
    constructor: HTMLComponents.prototype.constructor,
    options: {
        componentsFolder: 'components',
        srcDir: '',
        destDir: '',
        attrNodePrefix: '_',
        files: ['*.html']
    },

    /**
     * Constructor
     */
    init: function (options) {
        this.options = _.merge({}, this.options, options);
    },

    /**
     * Generate list of tags from a directory
     * @returns {Array}
     */
    tags: null,
    initTags: function () {
        if (!this.tags) {
            var filesList = fs.readdirSync(this.options.componentsFolder);
            this.tags = filesList.map(function (filename) {
                return filename.replace(/\.hbs$/, '');
            }).sort();
        }
    },

    /**
     * Parse all html files of srcDir and write them into destDir
     * @param {String} srcDir source directory
     * @param {String} destDir destination directory
     * @param {Array} patterns files mask, it follows https://github.com/isaacs/node-glob options
     */
    processDirectory: function (patterns, srcDir, destDir) {
        var _this = this;
        var files = glob.sync(patterns, {cwd: srcDir});
        files.forEach(function (file) {
            _this.processFile(file, srcDir, destDir);
        });
    },

    /**
     * Process one file
     * @param filePath the relative file path from srcFolder,
     * @param srcFolder the source folder
     * @param destFolder the destination folder
     *
     * @example
     *      htmlComponents.processFile('myfile.html', 'test', '.tmp');
     *      htmlComponents.processFile('mysubfolder/folder/myfile.html', 'app', 'dist');
     *
     */
    processFile: function (filePath, srcFolder, destFolder) {
        var html = fs.readFileSync(path.join(srcFolder, filePath), {encoding: 'utf-8'});
        var newHTML = this.processHTML(html);
        mkdirp.sync(path.dirname(path.join(destFolder, filePath)));
        fs.writeFileSync(path.join(destFolder, filePath), newHTML, {encoding: 'utf-8'});
    },

    /**
     * Process the HTML by using list of tags
     * @param {String} html String of HTML
     * @returns {String}
     */
    processHTML: function (html) {
        var _this = this;
        this.initTags();
        var $ = cheerio.load(html);
        var nodesToProcess = $(this.tags.join(','));
        nodesToProcess.each(function () {
            var html = _this.processNode($(this), $);
            html = _this.processHTML(html);
            $(this).replaceWith(html);
        });
        return $.html();
    },

    /**
     * Transform HTML custom tag into parsed html from template
     * @param node element
     * @param $ cheerio object
     */
    processNode: function (node, $) {
        var context = this.processAttributes(node, $);
        var nodeName = node[0].name;
        var type = context.type;
        var template = Handlebars.compile(this.getTemplate(nodeName, type));
        return template(context);
    },

    processAttributes: function (node, $) {
        var attr = node.attr();
        var nodesAttr = this.processNodesAsAttributes(node, $);
        attr = _.merge(attr, nodesAttr);
        attr = this.fixAttributesObject(attr);
        return attr;
    },

    /**
     * transform all children nodes of the object into attributes. The nodes must begin by a specific string. By default it's _
     * @param node
     * @param $ (cheerio object)
     * @returns {{}}
     */
    processNodesAsAttributes: function (node, $) {
        var obj = {};
        var regexp = new RegExp('^' + this.options.attrNodePrefix.replace(/([\[\]\$])/g, '\\$1'));
        node.children().each(function () {
            var $this = $(this);
            if (regexp.test(this.name)) {
                var name = this.name.replace(regexp, '');
                obj[name] = $this.html();
                $this.remove();
            }
            //process items
            if(this.name==='item') {
                if(!obj.items) {
                    obj.items = [];
                }
                obj.items.push({
                    html:$this.html(),
                    value:$this.attr('value')
                });
                $this.remove();
            }
        });
        obj.html = node.html();
        return obj;
    },

    /**
     *
     * @param attr
     * @returns {{}}
     */
    fixAttributesObject: function (attr) {
        var obj = {};
        for (var name in attr) {
            if (attr.hasOwnProperty(name)) {
                var dataRegExp = /^data-/;
                if (dataRegExp.test(name)) {
                    if (!obj.data) {
                        obj.data = {};
                    }
                    var dataName = name.replace(dataRegExp, '');
                    obj.data[dataName] = attr[name];
                }
                obj[name] = attr[name];
            }
        }

        //merge data object from attr into a string
        if (obj.data) {
            obj.dataStr = this.objectToAttributeString('data-', obj.data);
        }

        return obj;
    },

    getTemplate: function (name, type) {
        var filepath = path.normalize(path.join(this.options.componentsFolder, name + (type ? '/' + type : '') + '.hbs'));
        return fs.readFileSync(filepath, {encoding: 'utf-8'});
    },

    objectToAttributeString: function (prefix, obj) {
        var str = [];
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                str.push(prefix + key + '="' + obj[key] + '"');
            }
        }
        return str.join(' ');
    }
};

module.exports = HTMLComponents;<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs       = require("fs");
var stitch   = require("stitch");
var UglifyJS = require("./vendor/uglifyjs");
var uglify   = UglifyJS.uglify;

var package = stitch.createPackage({
  paths: [__dirname + "/vendor/uglifyjs/lib"]
});

package.compile(function(err, source) {
  if (err) throw err;

 source = "(function(global) {" +
    source + ";" +
    "global.UglifyJS = {};" +
    "global.UglifyJS.parser = this.require('parse-js');" +
    "global.UglifyJS.uglify = this.require('process');" +
    "}).call({}, this)";

  var ast = UglifyJS.parser.parse(source);
  source  = uglify.gen_code(uglify.ast_squeeze(uglify.ast_mangle(ast)));

  fs.writeFile(__dirname + "/lib/uglify.js", source, function(err) {
    if (err) throw err;
  });
});
<commit_msg>Don't minify vendored uglify source<commit_after>#!/usr/bin/env node

var fs     = require("fs");
var stitch = require("stitch");

var package = stitch.createPackage({
  paths: [__dirname + "/vendor/uglifyjs/lib"]
});

package.compile(function(err, source) {
  if (err) throw err;

 source = "(function(global) {" +
    source + ";\n" +
    "global.UglifyJS = {};\n" +
    "global.UglifyJS.parser = this.require('parse-js');\n" +
    "global.UglifyJS.uglify = this.require('process');\n" +
    "}).call({}, this);\n";

  fs.writeFile(__dirname + "/lib/uglify.js", source, function(err) {
    if (err) throw err;
  });
});
<|endoftext|>


<commit_before>'use strict';

/**!
 * [square]
 * @copyright (c) 2012 observe.it (observe.it) <opensource@observe.it>
 * MIT Licensed
 */
var Plugin = require('../plugin')
  , canihaz = require('canihaz')('square')
  , async = require('async')
  , _ = require('lodash')
  , url = require('url')
  , request = require('request')
  , fs = require('fs');

/**
 * Semver compatible regexp.
 *
 * @type {RegExp}
 * @api private
 */
var semver = [
    "\\s*[v=]*\\s*([0-9]+)"             // major
  , "\\.([0-9]+)"                       // minor
  , "\\.([0-9]+)"                       // patch
  , "(-[0-9]+-?)?"                      // build
  ,  "([a-zA-Z-][a-zA-Z0-9-\\.:]*)?"    // tag
];

semver = new RegExp(semver.join(''), 'gim');

/**
 * Because not all versions are semver compatible
 * we need a silly fall back:
 *
 * @type {RegExp}
 * @api private
 */
var sillyver = [
    "\\s*[v=]*\\s*(\\d)"                // major
  , "\\.([\\d][-\\s]?)"                 // minor
  , "(?:([a-zA-Z-][a-zA-Z0-9-.:]*)?)?"  // silly
];

sillyver = new RegExp(sillyver.join(''), 'gim');

/**
 * Regexp to test for Github hash sources
 *
 * @type {RegExp}
 * @api private
 */
var githubRE = /github.com\/([\w\.\-]+)\/([\w\.\-]+)\/blob(\/[\w\.\-]+)\/(.*)/;

/**
 * Constructor for updating third party modules.
 *
 * Options:
 * - strict: regexp, regexp for search for semver based version numbers
 * - loose: regexp, fall back for strict regexp, for oddly versioned code
 * - lines: number, amount of LOC to scan for version numbers
 *
 * @constructor
 * @param {object} square instance
 * @api public
 */
module.exports = Plugin.extend({
    /**
     * Name of the module.
     *
     * @type {String}
     */
    id: 'update'

    /**
     * Plugin type, only call this plugin once.
     *
     * @type {String}
     */
  , type: Plugin.once

    /**
     * Small description about the module.
     *
     * @type {String}
     */
  , description: 'Update third-party modules by checking the provided version number'
    /**
     * Strict versioning Regular Expression.
     *
     * @type {RegExp}
     */
  , strict: semver

    /**
     * Loose versioning Regular Expression.
     *
     * @type {RegExp}
     */
  , loose: sillyver

    /**
     * Number of lines
     *
     * @type {Number}
     */
  , lines: 10

    /**
     * Required depedencies for all methods to function properly
     *
     * @type {String}
     */
  , requires: 'github'

    /**
     * The module has been initialized.
     */
  , initialize: function initialize() {
      var bundles = this.square.package.bundle
        , files = Object.keys(bundles)
        , self = this;

      // edge case: if package isn't loaded from file, there is nothing to write.
      if (!this.square.package.path) return this.emit('disregard');

      async.forEach(files, function testing (key, cb) {
        var bundle = bundles[key]
          , provider;

        // Not a third party file.
        if (!bundle.latest) return cb();

        // Find the correct update handler.
        if (githubRE.test(bundle.latest)) provider = self.repo;
        if (!provider) provider = self.req;

        provider.call(self, bundle.latest, function test (err, version, content) {
          if (err) return cb(err);
          if (!version) return cb(new Error('unable to find and parse the version for ' + key));
          if (version === bundle.version) return cb();

          self.square.logger.notice(
              '%s is out of date, latest version is %s'
            , key
            , version.green
          );

          /**
           * Handle file upgrades.
           *
           * @param {Mixed} err
           * @param {String} content
           * @api private
           */
          function done(err, content) {
            if (err) return cb(err);

            var code = JSON.parse(self.square.package.source)
              , current = bundle.version
              , source;

            code.bundle[key].version = version;
            bundle.version = version;
            bundle.content = content;

            // now that we have updated the shizzle, we can write a new file
            // also update the old source with the new version
            source = JSON.stringify(code, null, 2);
            self.square.package.source = source;

            try {
              async.parallel([
                  async.apply(fs.writeFile, self.square.package.location, source)
                , async.apply(fs.writeFile, bundle.meta.location, content)
              ], function (err, results) {
                self.square.logger.notice(
                    'sucessfully updated %s from version %s to %s'
                  , key
                  , current.grey
                  , version.green
                );

                cb(err);
              });
            } catch (e) { err = e; }
          }

          if (content) return done(undefined, content);

          // find the correct location where we can download the actual source
          // code for this bundle
          var data = bundle.download || provider === self.repo
            ? self.raw(bundle.latest)
            : bundle.latest;

          self.download(data, done);
        });
      }, function finished(err, data) {
          if (err && err.forEach) {
            err.forEach(function failed (err) {
              self.logger.error(err);
            });
          }

          // Just pretend we are acting on data to continue processing.
          process.nextTick(self.emit.bind(self, 'data', ''));
      });
    }

    /**
     * Transforms a regular git url, to a raw file location.
     *
     * @param {String} uri
     * @returns {String}
     * @api private
     */
  , raw: function raw(uri) {
      var user, repo, branch, file
        , chunks = githubRE.exec(uri);

      user = chunks[1];
      repo = chunks[2];
      branch = chunks[3].substr(1); // remove the first /
      file = chunks[4];

      return 'https://raw.github.com/' + user + '/' + repo + '/' + branch + '/'+ file;
    }

    /**
     * Download the data.
     *
     * @param {String} uri
     * @param {Function} fn
     * @api private
     */
  , download: function download(uri, fn) {
      request.get(uri, function requested(err, res, body) {
        if (err) return fn(err);
        if (res.statusCode !== 200) return fn(new Error('Invalid status code'));

        fn(null, body.toString('utf8'));
      });
    }

    /**
     * See if the string matches a version number.
     *
     * @param {String} content
     * @returns {Mixed}
     * @api private
     */
  , version: function version(content) {
      var result;

      // a "feature" of calling exec on a regexp with a global flag is that it
      // renders it useless for new calls as it will do checks based on the new
      // matches. We can bypass this behavior by recompiling regexps
      [
          new RegExp(this.strict.source)
        , new RegExp(this.loose.source)
      ].some(function some (regexp) {
        var match = regexp.exec(content);

        if (match && match.length) {
          result = [
              match[1] ? match[1].trim() : 0
            , match[2] ? match[2].trim() : 0
            , match[3] ? match[3].trim() : 0
          ].join('.');
        }

        return !!result;
      });

      return result;
    }

    /**
     * Find the version number based on a SHA1 commit
     *
     * @param {Object} uri
     * @param {Function} fn
     * @api private
     */
  , repo: function repo(uri, fn) {
      var user, repository, branch, file
        , chunks = githubRE.exec(uri);

      user = chunks[1];
      repository = chunks[2];
      branch = chunks[3].substr(1); // remove the first /
      file = chunks[4];

      var api = new this.github({ version: "3.0.0" })
        , request = { user: user, repo: repository, path: file, sha: branch };

      api.repos.getCommits(request, function getcommit (err, list) {
        if (err) return fn(err);
        if (!list.length) return fn(new Error('No commits in this repo: ' + uri));

        var commit = list.shift();
        fn(null, commit.sha);
      });
    }

    /**
     * Find the version number somewhere in the first x lines
     *
     * @param {Object} uri
     * @param {Function} fn
     * @api private
     */
  , req: function req(uri, fn) {
      var lines = this.lines
        , version = this.version.bind(this);

      this.download(uri, function downloading(err, content) {
        if (err) return fn(err);
        if (!content) return fn(new Error('No content received from ' + uri));

        lines = content.split(/(\r\n)|\r|\n/).splice(0, lines);
        lines.some(function someline (line) {
          return !!version(line);
        });

        fn(null, version, content);
      });
    }
});
<commit_msg>[fix] do set version, just provide other variable name<commit_after>'use strict';

/**!
 * [square]
 * @copyright (c) 2012 observe.it (observe.it) <opensource@observe.it>
 * MIT Licensed
 */
var Plugin = require('../plugin')
  , canihaz = require('canihaz')('square')
  , async = require('async')
  , _ = require('lodash')
  , url = require('url')
  , request = require('request')
  , fs = require('fs');

/**
 * Semver compatible regexp.
 *
 * @type {RegExp}
 * @api private
 */
var semver = [
    "\\s*[v=]*\\s*([0-9]+)"             // major
  , "\\.([0-9]+)"                       // minor
  , "\\.([0-9]+)"                       // patch
  , "(-[0-9]+-?)?"                      // build
  ,  "([a-zA-Z-][a-zA-Z0-9-\\.:]*)?"    // tag
];

semver = new RegExp(semver.join(''), 'gim');

/**
 * Because not all versions are semver compatible
 * we need a silly fall back:
 *
 * @type {RegExp}
 * @api private
 */
var sillyver = [
    "\\s*[v=]*\\s*(\\d)"                // major
  , "\\.([\\d][-\\s]?)"                 // minor
  , "(?:([a-zA-Z-][a-zA-Z0-9-.:]*)?)?"  // silly
];

sillyver = new RegExp(sillyver.join(''), 'gim');

/**
 * Regexp to test for Github hash sources
 *
 * @type {RegExp}
 * @api private
 */
var githubRE = /github.com\/([\w\.\-]+)\/([\w\.\-]+)\/blob(\/[\w\.\-]+)\/(.*)/;

/**
 * Constructor for updating third party modules.
 *
 * Options:
 * - strict: regexp, regexp for search for semver based version numbers
 * - loose: regexp, fall back for strict regexp, for oddly versioned code
 * - lines: number, amount of LOC to scan for version numbers
 *
 * @constructor
 * @param {object} square instance
 * @api public
 */
module.exports = Plugin.extend({
    /**
     * Name of the module.
     *
     * @type {String}
     */
    id: 'update'

    /**
     * Plugin type, only call this plugin once.
     *
     * @type {String}
     */
  , type: Plugin.once

    /**
     * Small description about the module.
     *
     * @type {String}
     */
  , description: 'Update third-party modules by checking the provided version number'
    /**
     * Strict versioning Regular Expression.
     *
     * @type {RegExp}
     */
  , strict: semver

    /**
     * Loose versioning Regular Expression.
     *
     * @type {RegExp}
     */
  , loose: sillyver

    /**
     * Number of lines
     *
     * @type {Number}
     */
  , lines: 10

    /**
     * Required depedencies for all methods to function properly
     *
     * @type {String}
     */
  , requires: 'github'

    /**
     * The module has been initialized.
     */
  , initialize: function initialize() {
      var bundles = this.square.package.bundle
        , files = Object.keys(bundles)
        , self = this;

      // edge case: if package isn't loaded from file, there is nothing to write.
      if (!this.square.package.path) return this.emit('disregard');

      async.forEach(files, function testing (key, cb) {
        var bundle = bundles[key]
          , provider;

        // Not a third party file.
        if (!bundle.latest) return cb();

        // Find the correct update handler.
        if (githubRE.test(bundle.latest)) provider = self.repo;
        if (!provider) provider = self.req;

        provider.call(self, bundle.latest, function test(err, version, content) {
          if (err) return cb(err);
          if (!version) return cb(new Error('unable to find and parse the version for ' + key));
          if (version === bundle.version) return cb();

          self.square.logger.notice(
              '%s is out of date, latest version is %s'
            , key
            , version.green
          );

          /**
           * Handle file upgrades.
           *
           * @param {Mixed} err
           * @param {String} content
           * @api private
           */
          function done(err, content) {
            if (err) return cb(err);

            var code = JSON.parse(self.square.package.source)
              , current = bundle.version
              , source;

            code.bundle[key].version = version;
            bundle.version = version;
            bundle.content = content;

            // now that we have updated the shizzle, we can write a new file
            // also update the old source with the new version
            source = JSON.stringify(code, null, 2);
            self.square.package.source = source;

            try {
              async.parallel([
                  async.apply(fs.writeFile, self.square.package.location, source)
                , async.apply(fs.writeFile, bundle.meta.location, content)
              ], function (err, results) {
                self.square.logger.notice(
                    'sucessfully updated %s from version %s to %s'
                  , key
                  , current.grey
                  , version.green
                );

                cb(err);
              });
            } catch (e) { err = e; }
          }

          if (content) return done(undefined, content);

          // find the correct location where we can download the actual source
          // code for this bundle
          var data = bundle.download || provider === self.repo
            ? self.raw(bundle.latest)
            : bundle.latest;

          self.download(data, done);
        });
      }, function finished(err, data) {
          if (err && err.forEach) {
            err.forEach(function failed (err) {
              self.logger.error(err);
            });
          }

          // Just pretend we are acting on data to continue processing.
          process.nextTick(self.emit.bind(self, 'data', ''));
      });
    }

    /**
     * Transforms a regular git url, to a raw file location.
     *
     * @param {String} uri
     * @returns {String}
     * @api private
     */
  , raw: function raw(uri) {
      var user, repo, branch, file
        , chunks = githubRE.exec(uri);

      user = chunks[1];
      repo = chunks[2];
      branch = chunks[3].substr(1); // remove the first /
      file = chunks[4];

      return 'https://raw.github.com/' + user + '/' + repo + '/' + branch + '/'+ file;
    }

    /**
     * Download the data.
     *
     * @param {String} uri
     * @param {Function} fn
     * @api private
     */
  , download: function download(uri, fn) {
      request.get(uri, function requested(err, res, body) {
        if (err) return fn(err);
        if (res.statusCode !== 200) return fn(new Error('Invalid status code'));

        fn(null, body.toString('utf8'));
      });
    }

    /**
     * See if the string matches a version number.
     *
     * @param {String} content
     * @returns {Mixed}
     * @api private
     */
  , version: function version(content) {
      var result;

      // a "feature" of calling exec on a regexp with a global flag is that it
      // renders it useless for new calls as it will do checks based on the new
      // matches. We can bypass this behavior by recompiling regexps
      [
          new RegExp(this.strict.source)
        , new RegExp(this.loose.source)
      ].some(function some (regexp) {
        var match = regexp.exec(content);

        if (match && match.length) {
          result = [
              match[1] ? match[1].trim() : 0
            , match[2] ? match[2].trim() : 0
            , match[3] ? match[3].trim() : 0
          ].join('.');
        }

        return !!result;
      });

      return result;
    }

    /**
     * Find the version number based on a SHA1 commit
     *
     * @param {Object} uri
     * @param {Function} fn
     * @api private
     */
  , repo: function repo(uri, fn) {
      var user, repository, branch, file
        , chunks = githubRE.exec(uri);

      user = chunks[1];
      repository = chunks[2];
      branch = chunks[3].substr(1); // remove the first /
      file = chunks[4];

      var api = new this.github({ version: "3.0.0" })
        , request = { user: user, repo: repository, path: file, sha: branch };

      api.repos.getCommits(request, function getcommit (err, list) {
        if (err) return fn(err);
        if (!list.length) return fn(new Error('No commits in this repo: ' + uri));

        var commit = list.shift();
        fn(null, commit.sha);
      });
    }

    /**
     * Find the version number somewhere in the first x lines
     *
     * @param {Object} uri
     * @param {Function} fn
     * @api private
     */
  , req: function req(uri, fn) {
      var lines = this.lines
        , find = this.version.bind(this)
        , version;

      this.download(uri, function downloading(err, content) {
        if (err) return fn(err);
        if (!content) return fn(new Error('No content received from ' + uri));

        lines = content.split(/(\r\n)|\r|\n/).splice(0, lines);
        lines.some(function someline (line) {
          version = find(line);
          return !!version;
        });

        fn(null, version, content);
      });
    }
});
<|endoftext|>