Show:

File: platform/classes/Q.js

"use strict";
/* jshint -W014 */

/**
 * Contains core Qbix functionality.
 * @module Q
 * @main Q
 */
var express = require('express');
var http = require('http');
var https = require('https');
var util = require('util');
var events = require('events');
var path = require('path');
var fs = require('fs');

var root = this;
var QConstructor = function QConstructor() {};
QConstructor.prototype = new events.EventEmitter();
/**
 * The main platform module. Contains basic properties and methods and serves as namespace
 * for more specific sub-classes
 * @class Q
 * @static
 */
var Q = new QConstructor();
module.exports = Q;

Q.VERSION = 0.9;

/**
 * @class Q.Error
 * @description Throw this when throwing errors in Javascript
 */
Q.Error = Error;

/**
 * @class Q
 */

/**
 * Throws Q.Error with complaint if condition evaluates to something falsy
 * @method assert
 * @static
 * @param {Boolean} condition
 * @param {String} complaint
 */
Q.assert = function (condition, complaint) {
	if (!condition) {
		throw new Q.Error(complaint);
	}
};

/**
 * Returns the type of a value
 * @method typeOf
 * @param {mixed} value The value to test type
 * @return {String} String description of the type
 */
Q.typeOf = function _Q_typeOf(value) {
	var s = typeof value, x, l;
	if (s === 'object') {
		if (value === null) {
			return 'null';
		}
		if (root.Element && value instanceof root.Element) {
			return 'Element';
		} else if (value instanceof Array
		|| (value.constructor && value.constructor.name === 'Array')) {
			s = 'array';
		} else if (typeof(value.typename) != 'undefined' ) {
			return value.typename;
		} else if (typeof (l=value.length) == 'number' && (l%1==0)
		&& (!l || ((l-1) in value))) {
			return 'array';
		} else if (typeof(value.constructor) != 'undefined' && typeof(value.constructor.name) != 'undefined') {
			if (value.constructor.name == 'Object') {
				return 'object';
			}
			return value.constructor.name;
		} else if ((x = Object.prototype.toString.apply(value)).substr(0, 8) === "[object ") {
			return x.substring(8, x.length-1);
		} else {
			return 'object';
		}
	}
	return s;
};

/**
 * Walks the tree from the parent, and returns whether the path was defined
 * @method isSet
 * @param {Object} parent
 * @param {Array} keys
 * @param {String} delimiter Optional
 * @return {boolean}
 */
Q.isSet = function _Q_isSet(parent, keys, delimiter) {
	var p = parent;
	if (!p) {
		return false;
	}
	delimiter = delimiter || '.';
	if (typeof keys === 'string') {
		keys = keys.split(delimiter);
	}
	for (var i=0; i<keys.length; i++) {
		if (!(keys[i] in p)) {
			return false;
		}
		p = p[keys[i]];
	}
	return true;
};

function _getProp (/*Array*/parts, /*Boolean*/create, /*Object*/context){
	var p, i = 0;
	if (context === null) return undefined;
	context = context || null;
	if(!parts.length) return context;
	while(context && (p = parts[i++]) !== undefined){
		context = (typeof context === 'object') && (p in context) 
			? context[p] 
			: (create ? context[p] = {} : undefined);
	}
	return context; // mixed
};

/**
 * Set an object from a delimiter-separated string, such as "A.B.C"
 * Useful for longer api chains where you have to test each object in
 * the chain, or when you have an object reference in string format.
 * Objects are created as needed along `path`.
 * Another way to call this function is to pass an object of {name: value} pairs as the first parameter
 * and context as an optional second parameter. Then the return value is an object of the usual return values.
 *
 * @static
 * @method setObject
 * @param {String|Array} name Path to a property, in the form "A.B.C" or ["A", "B", "C"]
 * @param {mixed} value value or object to place at location given by name
 * @param {Object} [context=root] Optional. Object to use as root of path.
 * @param {String} [delimiter='.'] The delimiter to use in the name
 * @return {Object|undefined} Returns the passed value if setting is successful or `undefined` if not.
 */
Q.setObject = function _Q_setObject(name, value, context, delimiter) {
	delimiter = delimiter || '.';
	if (Q.isPlainObject(name)) {
		context = value;
		var result = {};
		for (var k in name) {
			result[k] = Q.setObject(k, name[k], context);
		}
		return result;
	}
	if (typeof name === 'string') {
		name = name.split(delimiter);
	}
	var p = name.pop(),
	obj = _getProp(name, true, context);
	return obj && (p !== undefined) ? (obj[p] = value) : undefined;
};

/**
 * Get a property from a delimiter-separated string, such as "A.B.C"
 * Useful for longer api chains where you have to test each object in
 * the chain, or when you have an object reference in string format.
 * You can also use it to resolve an object where it might be a string or array or something else.
 *
 * @static
 * @method getObject
 * @param {String|Array} name Path to a property, in the form "A.B.C" or ["A", "B", "C"]
 * @param {Object} [context=root] Optional. Object to use as root of path. Null may be passed.
 * @param {String} [delimiter='.'] The delimiter to use in the name
 * @param {mixed} [create=undefined] Pass a value here to set with Q.setObject if nothing was there
 * @return {Object|undefined} Returns the originally stored value, or `undefined` if nothing is there
 */
Q.getObject = function _Q_getObject(name, context, delimiter, create) {
	delimiter = delimiter || '.';
	if (typeof name === 'string') {
		name = name.split(delimiter);
	} else if (!(name instanceof Array)) {
		return name;
	}
	var result = _getProp(name, false, context);
	if (create !== undefined) {
		result = Q.setObject(name, create, context, delimiter);
	}
	return result;
};

/**
 * Used to prevent overwriting the latest results on the client with older ones.
 * Typically, you would call this function before making some sort of request,
 * save the ordinal in a variable, and then pass it to the function again inside
 * a closure. For example:
 * @example
 *     var ordinal = Q.latest(tool);
 *     requestSomeResults(function (err, results) {
 *       if (!Q.latest(tool, ordinal)) return;
 *     // otherwise, show the latest results on the client
 *     });
 * @method latest
 * @param {String|Q.Tool} key
 *  Requests under the same key share the same incrementing ordinal
 * @param {Number|Boolean} ordinal
 *  Pass an ordinal that you obtained from a previous call to the function
 *  Pass true here to get the latest ordinal that has been passed so far
 *  to the method under this key, corresponding to the latest results seen.
 * @return {Number|Boolean}
 *  If only key is provided, returns an ordinal to use.
 *  If ordinal is provided, then returns whether this was the latest ordinal.
 */
Q.latest = function (key, ordinal) {
	if (Q.typeOf(key) === 'Q.Tool')	{
		key = key.id;
	}
	if (ordinal === undefined) {
		return Q.latest.issued[key]
			= ((Q.latest.issued[key] || 0) % Q.latest.max) + 1;
	}
	var seen = Q.latest.seen[key] || 0;
	if (ordinal === true) {
		return seen;
	}
	if (ordinal > seen || ordinal < seen - Q.latest.max * 9/10) {
		Q.latest.seen[key] = ordinal;
		return true;
	}
	return false;
};
Q.latest.issued = {};
Q.latest.seen = {};
Q.latest.max = 10000;

/**
 * Makes an object into an event emitter.
 * @method makeEventEmitter
 * @param {Object} what Can be an object or a function
 * @param {boolean} [isConstructor=false] Whether the object is a constructor function. In this case,
 *  it is not the function that is made an emitter, but the
 *  objects which the function constructs.
 */
Q.makeEventEmitter = function _Q_makeEventEmitter(what, isConstructor) {
	if (isConstructor) {
		what.prototype.__proto__ = events.EventEmitter.prototype;
	} else {
		Q.extend(what, events.EventEmitter.prototype);
	}
};

/**
 * Creates a derived object which you can extend, inheriting from an existing object
 * @method objectWithPrototype
 * @param {Object} original The object to use as the prototype
 * @return {Object} The derived object
 */
Q.objectWithPrototype = function _Q_objectWithPrototype(original) {
	if (!original) {
		return {};
	}
	function Clone() {}
	Clone.prototype = original;
	return new Clone();
};

/**
 * Clones the Base constructor and mixes in the Constructor function
 * @method inherit
 * @param {Function} Base the base function, such as Q.Tool
 * @param {Function} Constructor the constructor function to change
 * @return {Function} The resulting function to be used as a constructor
 */
Q.inherit = function _Q_inherit(Base, Constructor) {
	function InheritConstructor() {
		InheritConstructor.constructors.apply(this, arguments);
	}
	InheritConstructor.prototype = Q.objectWithPrototype(Q.Tool.prototype);
	InheritConstructor.prototype.constructor = InheritConstructor;
	Q.mixin(InheritConstructor, Constructor);
	return InheritConstructor;
};

/**
 * Sets up control flows involving multiple callbacks and dependencies
 * Usage:
 * var p = Q.pipe(['user', 'stream], function (params, subjects) {
 *   // arguments that were passed are in params.user, params.stream
 *   // this objects that were passed are in subjects.user, subjects.stream
 * });
 * mysql("SELECT * FROM user WHERE userId = 2", p.fill('user'));
 * mysql("SELECT * FROM stream WHERE publisherId = 2", p.fill('stream'));
 *
 * The first parameter to p.fill() is the name of the field to fill when it's called
 * You can pass a second parameter to p.fill, which can be either:
 * true - in this case, the current function is ignored during the next times through the pipe
 * a string - in this case, this name is considered unfilled the next times through this pipe
 * an array of strings - in this case, these names are considered unfilled the next times through the pipe
 * Q.Cache constructor
 * @namespace Q
 * @class Q.Pipe
 * @constructor
 * @see {Q.Pipe.prototype.add} for more info on the parameters
 */
Q.Pipe = function _Q_Pipe(requires, maxTimes, callback) {
	if (this === Q) {
		throw new Q.Error("Q.Pipe: omitted keyword new");
	}
	this.callbacks = [];
	this.params = {};
	this.subjects = {};
	this.ignore = {};
	this.finished = false;
	this.add.apply(this, arguments);
};

/**
 * Adds a callback to the pipe
 * @method add
 * @param {String} field
 *  Pass the name of a field to wait for, until it is filled, before calling the callback.
 * @param {Function} callback
 *  This function is called as soon as the field is filled, i.e. when the callback
 *  produced by pipe.fill(field) is finally called by someone.
 *  The "this" and arguments from that call are also passed to the callback.
 *  The callback receives the same "this" and arguments that the original call was made with.
 *  It is passed the "this" and arguments which are passed to the callback.
 *  If you return true from this function, it will delete all the callbacks in the pipe.
 */
Q.Pipe.prototype.on = function _Q_pipe_on(field, callback) {
	return this.add([field], 1, function _Q_pipe_on_callback (params, subjects, field) {
		return callback.apply(subjects[field], params[field], field);
	});
};

/**
 * Adds a callback to the pipe with more flexibility
 * @method add
 * @param {Array} requires
 *  Optional. Pass an array of required field names here.
 *  Alternatively, pass an array of objects, which should be followed by
 *  the name of a Q.Event to wait for.
 * @param {number} maxTimes
 *  Optional. The maximum number of times the callback should be called.
 * @param {Function} callback
 *  Once all required fields are filled, this function is called every time something is piped.
 *  It is passed four arguments: (params, subjects, field, requires)
 *  If you return false from this function, it will no longer be called for future pipe runs.
 *  If you return true from this function, it will delete all the callbacks in the pipe.
 */
Q.Pipe.prototype.add = function _Q_pipe_add(requires, maxTimes, callback) {
	var r = null, n = null, e = null, r2, events, keys;
	for (var i=0; i<arguments.length; i++) {
		var ai = arguments[i];
		if (typeof ai === 'function') {
			if (e) {
				r2 = [];
				events = [];
				keys = [];
				var pipe = this;
				Q.each(r, function (k, item) {
					var event = Q.getObject(e, item);
					if (Q.typeOf(event) === 'Q.Event') {
						keys.push(event.add(pipe.fill(k)));
						r2.push(k);
						events.push(event);
					}
				});
				ai.pipeEvents = events;
				ai.pipeKeys = keys;
				r = r2;
			}
			ai.pipeRequires = r;
			ai.pipeRemaining = n;
			r = n = e = null;
			this.callbacks.push(ai);
		} else {
			switch (Q.typeOf(ai)) {
			case 'array':
				r = ai;
				if (r.length
				&& typeof r[0] !== 'string'
				&& typeof r[0] !== 'number') {
					e = arguments[++i];
				}
				break;
			case 'object':
				r = ai;
				e = arguments[++i];
				break;
			case 'number':
				n = ai;
				break;
			}
			if (e !== null && typeof e !== 'string') {
				debugger;
				throw new Q.Error("Q.Pipe.prototype.add requires event name after array of objects");
			}
		}
	}
	return this;
};

/**
 * Makes a function that fills a particular field in the pipe and can be used as a callback
 * @method fill
 * @param {String} field
 *   For error callbacks, you can use field="error" or field="users.error" for example.
 * @param {boolean|String|Array} ignore
 *   Optional. If true, then ignores the current field in subsequent pipe runs.
 *   Or pass the name (string) or names (array) of the field(s) to ignore in subsequent pipe runs.
 * @return {Function} Returns a callback you can pass to other functions.
 */
Q.Pipe.prototype.fill = function _Q_pipe_fill(field, ignore) {
	if (ignore === true) {
		this.ignore[this.i] = true;
	} else if (typeof ignore === 'string') {
		this.ignore[ignore] = true;
	} else if (Q.isArrayLike(ignore)) {
		for (var i=0; i<ignore.length; ++i) {
			this.ignore[ignore[i]] = true;
		}
	}

	var pipe = this;

	return function _Q_pipe_fill() {
		pipe.params[field] = Array.prototype.slice.call(arguments);
		pipe.subjects[field] = this;
		pipe.run(field);
	};
};

/**
 * Runs the pipe
 * @method run
 * @param {String} field optionally indicate name of the field that was just filled
 * @return {number} the number of pipe callbacks that wound up running
 */
Q.Pipe.prototype.run = function _Q_pipe_run(field) {
	var cb, ret, callbacks = this.callbacks, params = Q.copy(this.params), count = 0;

	cbloop:
	for (var i=0; i<callbacks.length; i++) {
		if (this.ignore[i]) {
			continue;
		}
		this.i = i;
		if (!(cb = callbacks[i]))
			continue;
		if (cb.pipeRequires) {
			for (var j=0; j<cb.pipeRequires.length; j++) {
				if (this.ignore[cb.pipeRequires[j]]) {
					continue;
				}
				if (! (cb.pipeRequires[j] in params)) {
					continue cbloop;
				}
			}
		}
		if (cb.pipeRemaining) {
			if (!--cb.pipeRemaining) {
				delete callbacks[i];
			}
		}
		ret = cb.call(this, this.params, this.subjects, field, cb.pipeRequires);
		if (cb.pipeEvents) {
			for (j=0; j<cb.pipeEvents.length; j++) {
				cb.pipeEvents[j].remove(cb.pipeKeys[j]);
			}
		}
		++count;
		if (ret === false) {
			delete callbacks[i];
		} else if (ret === true) {
			this.callbacks = []; // clean up memory
			this.finished = true;
			break;
		}
	}
	return count;
};

/**
 * @class Q
 */

/**
 * A convenience method for constructing Q.Pipe objects
 * and is really here just for backward compatibility.
 * @method pipe
 * @return {Q.Pipe}
 * @see Q.Pipe
 */
Q.pipe = function _Q_pipe(a, b, c, d) {
	return new Q.Pipe(a, b, c, d);
};

/**
 * This function helps create "batch functions", which can be used in getter functions
 * and other places to accomplish things in batches.
 * @method batcher
 * @param {Function} batch
 *  This is the function you must write to implement the actual batching functionality.
 *  It is passed the subjects, params and callbacks that were collected by Q.batcher
 *  from the individual calls that triggered your batch function to be run.
 *  Your batch function is supposed to cycle through the callbacks array -- where each
 *  entry is the array of (one or more) callbacks the client passed during a particular
 *  call -- and Q.handle the appropriate one.
 *  NOTE: When receiving results from the server, make sure the order in which
 *  results are returned matches the order in which your batch function was provided the
 *  arguments from the individual calls. This will help you call the correct callbacks.
 *  Typically you would serialize the array of arguments e.g. into JSON when sending 
 *  the request down to the server, and the server should also return an array of results
 *  that is in the same order.
 * @param {Object} options 
 *  An optional hash of possible options, which can include:
 * @param {boolean} [options.max=10] When the number of individual calls 
 *  in the queue reaches this number, the batch function is run.
 * @param {boolean} [options.ms=50] When this many milliseconds elapse 
 *  without another call to the same batcher function, the batch function is run.
 * @return {Function} It returns a function that the client can use as usual, but which,
 * behind the scenes, queues up the calls and then runs a batch function that you write.
 */
Q.batcher = function _Q_batch(batch, options) {
	var o = Q.extend({}, Q.batcher.options, options);
	var result = function _Q_batch_result() {
		var requestArguments = arguments;
		function nextRequest() {
			var i, j;
			var callbacks = [], args = [];

			// separate fields and callbacks
			for (i=0; i<requestArguments.length; ++i) {
				if (typeof requestArguments[i] === 'function') {
					callbacks.push(requestArguments[i]);
				} else {
					args.push(requestArguments[i]);
				}
			}
			if (!batch.count) batch.count = 0;
			if (!batch.argmax) batch.argmax = 0;
			if (!batch.cbmax) batch.cbmax = 0;

			++batch.count;
			if (callbacks.length > batch.cbmax) batch.cbmax = callbacks.length;
			if (args.length > batch.argmax) batch.argmax = args.length;

			// collect various arrays for convenience of writing batch functions,
			// at the expense of extra work and memory
			if (!batch.subjects) batch.subjects = [];
			if (!batch.params) batch.params = [];
			if (!batch.callbacks) batch.callbacks = [];

			batch.subjects.push(this);
			batch.params.push(args);
			batch.callbacks.push(callbacks);

			if (batch.timeout) {
				clearTimeout(batch.timeout);
			}
			if (batch.count == o.max) {
				runBatch();
			} else {
				batch.timeout = setTimeout(runBatch, o.ms);
			} 
			
			function runBatch() {
				try {
					if (batch.count) {
						batch.call(this, batch.subjects, batch.params, batch.callbacks);
						batch.subjects = batch.params = batch.callbacks = null;
						batch.count = 0;
						batch.argmax = 0;
						batch.cbmax = 0;
					}
					batch.timeout = null;
				} catch (e) {
					batch.count = 0;
					batch.argmax = 0;
					batch.cbmax = 0;
					batch.timeout = null;
					throw e;
				}
			}
		}
		// Make the batcher re-entrant. Without this technique, if 
		// something is requested while runBatch is calling its callback,
		// that request's information may be wiped out by runBatch.
		// The following statement schedules such requests after runBatch has completed.
		setTimeout(nextRequest, 0);
	};
	result.batch = batch;
	result.cancel = function () {
		clearTimeout(batch.timeout);
	};
	return result;
};

Q.batcher.options = {
	max: 10,
	ms: 50
};

/**
 * Wraps a getter function to provide support for re-entrancy, cache and throttling.
 *  It caches based on all non-function arguments which were passed to the function.
 *  All functions passed in as arguments are considered as callbacks. Getter execution is
 *  considered complete when one of the callbacks is fired. If any other callback is fired,
 *  throttling may be influenced - i.e. throttleSize will increase by number of callbacks fired.
 *  If the original function has a "batch" property, it gets copied as a property of
 *  the wrapper function being returned. This is useful when calling Q.getter(Q.batcher(...))
 *  Call method .forget with the same arguments as original getter to clear cache record
 *  and update it on next call to getter (if it happen)
 *  @method getter
 * @param {Function} original
 *  The original getter function to be wrapped
 *  Can also be an array of [getter, execute] which you can use if
 *  your getter does "batching", and waits a tiny bit before sending the batch request,
 *  to see if any more will be requested. In this case, the execute function
 *  is supposed to execute the batched request without waiting any more.
 *  If the original function returns false, the caching is canceled for that call.
 * @param {Object} [options={}] An optional hash of possible options, which include:
 * @param {Function} [options.prepare] This is a function that is run to copy-construct objects from cached data. It gets (subject, parameters, callback) and is supposed to call callback(subject2, parameters2)
 * @param {String} [options.throttle] an id to throttle on, or an Object that supports the throttle interface:
 * @param {Function} [options.throttleTry] function(subject, getter, args) - applies or throttles getter with subject, args
 * @param {Function} [options.throttleNext] function (subject) - applies next getter with subject
 * @param {Integer} [options.throttleSize=100] The size of the throttle, if it is enabled
 * @param {Q.Cache|Boolean} [options.cache] pass false here to prevent caching, or an object which supports the Q.Cache interface
 * @return {Function}
 *  The wrapper function, which returns an object with a property called "result"
 *  which could be one of Q.getter.CACHED, Q.getter.WAITING, Q.getter.REQUESTING or Q.getter.THROTTLING .
 *  This function also contains Q.Events called onCalled, onResult and onExecuted.
 */
Q.getter = function _Q_getter(original, options) {

	var gw = function Q_getter_wrapper() {
		var i, key, callbacks = [];
		var arguments2 = Array.prototype.slice.call(arguments);

		// separate fields and callbacks
		key = Q.Cache.key(arguments2, callbacks);
		if (callbacks.length === 0) {
			// in case someone forgot to pass a callback
			// pretend they added a callback at the end
			var noop = function _noop() {} ;
			arguments2.push(noop);
			callbacks.push(noop);
		}
		
		var ret = { dontCache: false };
		gw.emit('called', this, arguments2, ret);

		var cached, cbpos, cbi;
		Q.getter.usingCached = false;
		
		function _prepare(subject, params, callback, ret, cached) {
			if (gw.prepare) {
				gw.prepare.call(gw, subject, params, _result, arguments2);
			} else {
				_result(subject, params);
			}
			function _result(subject, params) {
				gw.emit('result', subject, subject, params, arguments2, ret, gw);
				Q.getter.usingCached = cached;
				callback.apply(subject, params);
				gw.emit('executed', subject, subject, params, arguments2, ret, gw);
				Q.getter.usingCached = false;
			}
		}

		// if caching is required check the cache -- maybe the result is there
		if (gw.cache && !ignoreCache) {
			if (cached = gw.cache.get(key)) {
				cbpos = cached.cbpos;
				if (callbacks[cbpos]) {
					_prepare(cached.subject, cached.params, callbacks[cbpos], ret, true);
					ret.result = Q.getter.CACHED;
					return ret; // wrapper found in cache, callback and throttling have run
				}
			}
		}
		ignoreCache = false;

		_waiting[key] = _waiting[key] || [];
		_waiting[key].push({
			callbacks: callbacks,
			ret: ret
		});
		if (_waiting[key].length > 1) {
			gw.emit('executed', this, arguments2, ret);
			ret.result = Q.getter.WAITING;
			return ret; // the request is already in process - let's wait
		}

		// replace the callbacks with smarter functions
		var args = [];
		for (i=0, cbi=0; i<arguments2.length; i++) {

			// we only care about functions
			if (typeof arguments2[i] !== 'function') {
				args.push(arguments2[i]); // regular argument
				continue;
			}

			args.push((function(cb, cbpos) {
				// make a function specifically to call the
				// callbacks in position pos, and then decrement
				// the throttle
				return function _Q_getter_callback() {

					// save the results in the cache
					if (gw.cache && !ret.dontCache) {
						gw.cache.set(key, cbpos, this, arguments);
					}

					// process waiting callbacks
					var wk = _waiting[key];
					delete _waiting[key];
					if (wk) {
						for (i = 0; i < wk.length; i++) {
							try {
								_prepare(this, arguments, wk[i].callbacks[cbpos], wk[i].ret, true);
							} catch (e) {
								debugger;
								console.warn(e);
							}
						}
					}

					// tell throttle to execute the next function, if any
					if (gw.throttle && gw.throttle.throttleNext) {
						gw.throttle.throttleNext(this);
					}
				};
			})(callbacks[cbi], cbi));
			++cbi; // the index in the array of callbacks
		}

		if (!gw.throttle) {
			// no throttling, just run the function
			if (false === original.apply(this, args)) {
				ret.dontCache = true;
			}
			ret.result = Q.getter.REQUESTING;
			gw.emit('executed', this, arguments2, ret);
			return ret;
		}

		if (!gw.throttle.throttleTry) {
			// the throttle object is probably not set up yet
			// so set it up
			var p = {
				size: gw.throttleSize,
				count: 0,
				queue: [],
				args: []
			};
			gw.throttle.throttleTry = function _throttleTry(that, getter, args, ret) {
				++p.count;
				if (p.size === null || p.count <= p.size) {
					if (false === getter.apply(that, args)) {
						ret.dontCache = true;
					}
					return true;
				}
				// throttle is full, so queue this function
				p.queue.push(getter);
				p.args.push(args);
				return false;
			};
			gw.throttle.throttleNext = function _throttleNext(that) {
				if (--p.count < 0) {
					console.warn("Q.getter: throttle count is negative");
				}
				if (p.queue.length) {
					p.queue.shift().apply(that, p.args.shift());
				}
			};
		}
		if (!gw.throttleSize) {
			gw.throttle.throttleSize = function _throttleSize(newSize) {
				if (typeof(newSize) === 'undefined') {
					return p.size;
				}
				p.size = newSize;
			};
		}

		// execute the throttle
		ret.result = gw.throttle.throttleTry(this, original, args, ret)
			? Q.getter.REQUESTING
			: Q.getter.THROTTLING;
		gw.emit('executed', this, arguments2, ret);
		return ret;
	}

	Q.extend(gw, original, Q.getter.options, options);
	gw.original = original;
	Q.makeEventEmitter(gw);

	var _waiting = {};
	if (gw.cache === false) {
		// no cache
		gw.cache = null;
	} else if (gw.cache === true) {
		// create our own Object that will cache locally in the page
		gw.cache = Q.Cache.process(++_Q_getter_i);
	} else {
		// assume we were passed an Object that supports the cache interface
	}

	gw.throttle = gw.throttle || null;
	if (gw.throttle === true) {
		gw.throttle = '';
	}
	if (typeof gw.throttle === 'string') {
		// use our own objects
		if (!Q.getter.throttles[gw.throttle]) {
			Q.getter.throttles[gw.throttle] = {};
		}
		gw.throttle = Q.getter.throttles[gw.throttle];
	}

	gw.forget = function _forget() {
		var key = Q.Cache.key(arguments);
		if (key && gw.cache) {
			return gw.cache.remove(key);
		}
	};
	
	var ignoreCache = false;
	gw.force = function _force() {
		ignoreCache = true;
		gw.apply(this, arguments);
	};
	
	if (original.batch) {
		gw.batch = original.batch;
	}
	return gw;
};
var _Q_getter_i = 0;
Q.getter.options = {
	cache: true,
	throttle: null,
	throttleSize: 100
};
Q.getter.throttles = {};
Q.getter.cache = {};
Q.getter.waiting = {};
Q.getter.CACHED = 0;
Q.getter.REQUESTING = 1;
Q.getter.WAITING = 2;
Q.getter.THROTTLING = 3;

/**
 * Wraps a function and returns a wrapper that will call the function at most once.
 * 
 * @static
 * @method once
 * @param {Function} original The function to wrap
 * @param {Mixed} defaultValue Value to return whenever original function isn't called
 * @return {Function} The wrapper function
 */
Q.once = function (original, defaultValue) {
	var _called = false;
	return function _Q_once_wrapper() {
		if (_called) return defaultValue;
		_called = true;
		return original.apply(this, arguments);
	};
};

/**
 * Wraps a function and returns a wrapper that will call the function
 * at most once every given milliseconds.
 * @static
 * @method throttle
 * @param {Function} original The function to wrap
 * @param {Number} milliseconds The number of milliseconds
 * @param {Boolean} delayedFinal Whether the wrapper should execute the latest function call
 *  after throttle opens again, useful for e.g. following a mouse pointer that stopped.
 * @param {Mixed} defaultValue Value to return whenever original function isn't called
 * @return {Function} The wrapper function
 */
Q.throttle = function (original, milliseconds, delayedFinal, defaultValue) {
	var _lastCalled;
	var _timeout = null;
	return function _Q_throttle_wrapper(e) {
		var t = this, a = arguments;
		var ms = Date.now() - _lastCalled;
		if (ms < milliseconds) {
			if (delayedFinal) {
				if (_timeout) {
					clearTimeout(_timeout);
				}
				_timeout = setTimeout(function () {
					_lastCalled = Date.now();
					original.apply(t, a);
				}, milliseconds - ms);
			}
			return defaultValue;
		}
		_lastCalled = Date.now();
		return original.apply(this, arguments);
	};
};

/**
 * Wraps a function and returns a wrapper that adds the function to a queue
 * of functions to be called one by one at most once every given milliseconds.
 * 
 * @static
 * @method queue
 * @param {Function} original The function to wrap
 * @param {number} milliseconds The number of milliseconds, defaults to 0
 * @return {Function} The wrapper function
 */
Q.queue = function (original, milliseconds) {
	var _queue = [];
	var _timeout = null;
	milliseconds = milliseconds || 0;
	function _Q_queue_next() {
		if (!_queue.length) {
			_timeout = null;
			return 0;
		}
		var p = _queue.shift();
		var ret = original.apply(p[0], p[1]);
		if (ret === false) {
			_timeout = null;
			_queue = [];
		} else {
			_timeout = setTimeout(_Q_queue_next, milliseconds);
		}
	};
	return function _Q_queue_wrapper() {
		var args = Array.prototype.slice.call(arguments, 0);
		var len = _queue.push([this, args]);
		if (!_timeout) {
			_timeout = setTimeout(function () {
				_Q_queue_next();
			}, 0);
		}
		return len;
	};
};

/**
 * Wraps a function and returns a wrapper that will call the function
 * after calls stopped coming in for a given number of milliseconds.
 * If the immediate param is true, the wrapper lets the function be called
 * without waiting if it hasn't been called for the given number of milliseconds.
 * @static
 * @method debounce
 * @param {Function} original The function to wrap
 * @param {number} milliseconds The number of milliseconds
 * @param {Boolean} [immediate=false] if true, the wrapper also lets the function be called
 *   without waiting if it hasn't been called for the given number of milliseconds.
 * @param {Mixed} defaultValue Value to return whenever original function isn't called
 * @return {Function} The wrapper function
 */
Q.debounce = function (original, milliseconds, immediate, defaultValue) {
	var _timeout = null;
	return function _Q_debounce_wrapper() {
		var t = this, a = arguments;
		if (_timeout) {
			clearTimeout(_timeout);
		} else if (immediate) {
			original.apply(t, a);
		}
		_timeout = setTimeout(function _Q_debounce_handler() {
			if (!immediate) {
				original.apply(t, a);
			}
			_timeout = null;
		}, milliseconds);
		return defaultValue;
	};
};

/**
 * Q.Cache constructor
 * @namespace Q
 * @class Q.Cache
 * @constructor
 * @param {Object} [options={}] you can pass the following options:
 * @param {Integer} [options.max=100] the maximum number of items the cache should hold. Defaults to 100.
 * @param {Q.Cache} [options.after] pass an existing cache with max > this cache's max, to look in first
 */
Q.Cache = function  _Q_Cache(options) {
	if (this === Q) {
		throw new Q.Error("Q.Pipe: omitted keyword new");
	}
	options = options || {};
	this.name = options.name;
	this.data = {};
	this.max = options.max || 100;
	this.earliest = this.latest = null;
	this.count = 0;
	if (options.after) {
		var cache = options.after;
		if (!(cache instanceof Q.Cache)) {
			throw new Q.Exception("Q.Cache after option must be a Q.Cache instance");
		}
		if (cache.max < this.max) {
			throw new Q.Exception("Q.Cache after.max cannot be less than this.max");
		}
		var _set = this.set;
		var _get = this.get;
		var _remove = this.remove;
		var _clear = this.clear;
		this.set = function () {
			cache.set.apply(this, arguments);
			return _set.apply(this, arguments);
		};
		this.get = function () {
			cache.get.apply(this, arguments);
			return _get.apply(this, arguments);
		};
		this.remove = function () {
			cache.remove.apply(this, arguments);
			return _remove.apply(this, arguments);
		};
		this.clear = function () {
			this.each([], function () {
				cache.remove.apply(this, arguments);
			});
			return _clear.apply(this, arguments);
		};
	}
};
/**
 * Generates the key under which things will be stored in a cache
 * @static
 * @method key
 * @param {Array} args the arguments from which to generate the key
 * @param {Array} functions  optional array to which all the functions found in the arguments will be pushed
 * @return {String}
 */
Q.Cache.key = function _Cache_key(args, functions) {
	var i, keys = [];
	if (Q.isArrayLike(args)) {
		for (i=0; i<args.length; ++i) {
			if (typeof args[i] !== 'function') {
				keys.push(args[i]);
			} else if (functions && functions.push) {
				functions.push(args[i]);
			}
		}
	} else {
		keys = args;
	}
	return JSON.stringify(keys);
};
/**
 * Accesses the cache and sets an entry in it
 * @method set
 * @param {String} key  the key to save the entry under, or an array of arguments
 * @param {number} cbpos the position of the callback
 * @param {Object} subject The "this" object for the callback
 * @param {Array} params The parameters for the callback
 * @param {Object} options  supports the following options:
 * @param {boolean} [options.dontTouch=false] if true, then doesn't mark item as most recently used
 * @return {Boolean} whether there was an existing entry under that key
 */
Q.Cache.prototype.set = function _Q_Cache_prototype_set(key, cbpos, subject, params, options) {
	if (typeof key !== 'string') {
		key = Q.Cache.key(key);
	}
	var existing = this.data[key], previous;
	if (!options || !options.dontTouch) {
		// marks the item as being recently used, if it existed in the cache already
		existing = this.get(key);
		if (!existing) {
			++this.count;
		}
	}
	var value = {
		cbpos: cbpos,
		subject: subject,
		params: (params instanceof Array) ? params : Array.prototype.slice.call(params||[]),
		prev: (options && options.prev) ? options.prev : (existing ? existing.prev : this.latest),
		next: (options && options.next) ? options.next : (existing ? existing.next : null)
	};
	this.data[key] = value;
	if (!existing || (!options || !options.dontTouch)) {
		if (previous = this.data[value.prev]) {
			previous.next = key;
		}
		this.latest = key;
		if (this.count === 1) {
			this.earliest = key;
		}
	}
	if (this.count > this.max) {
		this.remove(this.earliest);
	}
	return existing ? true : false;
};
/**
 * Accesses the cache and gets an entry from it
 * @method get
 * @param {String} key
 * @param {Object} options supports the following options:
 * @param {boolean} [options.dontTouch=false] if true, then doesn't mark item as most recently used
 * @return {mixed} whatever is stored there, or else returns undefined
 */
Q.Cache.prototype.get = function _Q_Cache_prototype_get(key, options) {
	if (typeof key !== 'string') {
		key = Q.Cache.key(key);
	}
	if (!(key in this.data)) {
		return undefined;
	}
	var existing = this.data[key], previous;
	if ((!options || !options.dontTouch) && this.latest !== key) {
		if (this.earliest == key) {
			this.earliest = existing.next;
		}
		existing.prev = this.latest;
		existing.next = null;
		if (previous = this.data[existing.prev]) {
			previous.next = key;
		}
		this.latest = key;
	}
	return existing;
};
/**
 * Accesses the cache and removes an entry from it.
 * @method remove
 * @param {String} key the key of the entry to remove
 * @return {Boolean} whether there was an existing entry under that key
 */
Q.Cache.prototype.remove = function _Q_Cache_prototype_remove(key) {
	if (typeof key !== 'string') {
		key = Q.Cache.key(key);
	}
	if (!(key in this.data)) {
		return false;
	}
	var existing = this.data[key];
	--this.count;
	if (this.latest === key) {
		this.latest = existing.prev;
	}
	if (this.earliest === key) {
		this.earliest = existing.next;
	}
	delete this.data[key];
	return true;
};
/**
 * Clears Cache data and sets it to {}
 * @method clear
 * @param {String} key
 */
Q.Cache.prototype.clear = function _Q_Cache_prototype_clear(key) {
	this.data = {};
};
/**
 * Cycles through all the entries in the cache
 * @method each
 * @param {Array} args An array consisting of some or all the arguments that form the key
 * @param {Function} callback Is passed two parameters: key, value, with this = the cache
 */
Q.Cache.prototype.each = function _Q_Cache_prototype_clear(args, callback) {
	var cache = this;
	var prefix = null;
	if (typeof args === 'function') {
		callback = args;
		args = undefined;
	} else {
		var json = Q.Cache.key(args);
		prefix = json.substring(0, json.length-1);
	}
	if (!callback) {
		return;
	}
	return Q.each(this.data, function (k, v) {
		if (prefix && !k.startsWith(prefix)) {
			return;
		}
		if (callback.call(cache, k, v) === false) {
			return false;
		}
	});
};
/**
 * @method process
 * @static
 * @param {String} name
 * @param {Object} options for Q.Cache constructor
 * @return {mixed}
 */
Q.Cache.process = function _Q_Cache_local(name, options) {
	if (!Q.Cache.process.caches[name]) {
		var c = Q.Cache.process.caches[name] = new Q.Cache(options);
		c.name = name;
	}
	return Q.Cache.process.caches[name];
};
Q.Cache.process.caches = {};

/**
 * @class Q
 */
/**
 * Used for handling callbacks, whether they come as functions,
 * strings referring to functions (if evaluated), arrays or hashes.
 * @method handle
 * @param {callable} callables The callables to call
 * @param {Object} context The context in which to call them
 * @param {Array} args An array of arguments to pass to them
 * @return {number} The number of handlers executed
 */
Q.handle = function _Q_handle(callables, context, args) {
	if (!callables) {
		return 0;
	}
	var i=0, count= 0, result;
	switch (Q.typeOf(callables)) {
	 case 'function':
		result = callables.apply(
			context ? context : null,
			args ? args : []
		);
		if (result === false) return false;
		return 1;
	 case 'array':
		for (i=0; i<callables.length; ++i) {
			result = Q.handle(callables[i], context, args);
			if (result === false) return false;
			count += result;
		}
		return count;
	 case 'object':
		for (var k in callables) {
			result = Q.handle(callables[k], context, args);
			if (result === false) return false;
			count += result;
		}
		return count;
	 case 'string': 
		var c = Q.getObject(callables, context) || Q.getObject(callables);
		return Q.handle(c, context, args);
	 default:
		return 0;
	}
};

/**
 * Iterates over elements in a container, and calls the callback.
 * Use this if you want to avoid problems with loops and closures.
 * @method each
 * @param {Array|Object|String|Number} container, which can be an array, object or string.
 *  You can also pass up to three numbers here: from, to and optional step
 * @param {Function|String} callback
 *  A function which will receive two parameters
 *	index: the index of the current item
 *	value: the value of the current item
 *  Also can be a string, which would be the name of a method to invoke on each item, if possible.
 *  In this case the callback should be followed by an array of arguments to pass to the method calls.
 * @param {Object} options
 *  ascending: Optional. Pass true here to traverse in ascending key order, false in descending.
 *  numeric: Optional. Used together with ascending. Use numeric sort instead of string sort.
 *  sort: Optional. Pass a compare Function here to be used when sorting object keys before traversal. Also can pass a String naming the property on which to sort.
 *  hasOwnProperty: Optional. Set to true to skip properties found on the prototype chain.
 * @throws {Q.Exception} If container is not array, object or string
 */
Q.each = function _Q_each(container, callback, options) {
	function _byFields(a, b) { 
		return container[a][s] > container[b][s] ? 1
			: (container[a][s] < container[b][s] ? -1 : 0); 
	}
	function _byKeysNumeric(a, b) { 
		return Number(a) - Number(b); 
	}
	function _byFieldsNumeric(a, b) { 
		return Number(container[a][s]) - Number(container[b][s]); 
	}
	var i, k, c, length, r, t, args;
	if (typeof callback === 'string' && Q.isArrayLike(arguments[2])) {
		args = arguments[2];
		options = arguments[3];
	}
	switch (t = Q.typeOf(container)) {
		case 'array':
		default:
			// Assume it is an array-like structure.
			// Make a copy in case it changes during iteration. Then iterate.
			c = Array.prototype.slice.call(container, 0);
			length = c.length;
			if (!c || !length || !callback) return;
			if (options && options.ascending === false) {
				for (i=length-1; i>=0; --i) {
					r = Q.handle(callback, c[i], args || [i, c[i]], c);
					if (r === false) return false;
				}
			} else {
				for (i=0; i<length; ++i) {
					r = Q.handle(callback, c[i], args || [i, c[i]], container);
					if (r === false) return false;
				}
			}
			break;
		case 'object':
			if (!container || !callback) return;
			if (options && ('ascending' in options || 'sort' in options)) {
				var keys = [], key;
				for (k in container) {
					if (options.hasOwnProperty && !Q.has(container, k)) {
						continue;
					}
					if (container.hasOwnProperty && container.hasOwnProperty(k)) {
						keys.push(options.numeric ? Number(k) : k);
					}
				}
				var s = options.sort;
				var t = typeof(s);
				var _byKeys = undefined;
				var compare = (t === 'function') ? s : (t === 'string'
					? (options.numeric ? _byFieldsNumeric : _byFields)
					: (options.numeric ? _byKeysNumeric : _byKeys));
				keys.sort(compare);
				if (options.ascending === false) {
					for (i=keys.length-1; i>=0; --i) {
						key = keys[i];
						r = Q.handle(callback, container[key], args || [key, container[key]], container);
						if (r === false) return false;
					}
				} else {
					for (i=0; i<keys.length; ++i) {
						key = keys[i];
						r = Q.handle(callback, container[key], args || [key, container[key]], container);
						if (r === false) return false;
					}
				}
			} else {
				for (k in container) {
					if (container.hasOwnProperty && container.hasOwnProperty(k)) {
						r = Q.handle(callback, container[k], args || [k, container[k]], container);
						if (r === false) return false;
					}
				}
			}
			break;
		case 'string':
			var c;
			if (!container || !callback) return;
			if (options && options.ascending === false) {
				for (i=0; i<container.length; ++i) {
					c = container.charAt(i);
					r = Q.handle(callback, c, args || [i, c], container);
					if (r === false) return false;
				}
			} else {
				for (i=container.length-1; i>=0; --i) {
					c = container.charAt(i);
					r = Q.handle(callback, c, args || [i, c, container]);
					if (r === false) return false;
				}
			}
			break;
		case 'number':
			var from = 0, to=container, step;
			if (typeof arguments[1] === 'number') {
				from = arguments[0];
				to = arguments[1];
				if (typeof arguments[2] === 'number') {
					step = arguments[2];
					callback = arguments[3];
					options = arguments[4];
				} else {
					callback = arguments[2];
					options = arguments[3];
				}
			}
			if (!callback) return;
			if (!step || (to-from)*step<0) {
				return 0;
			}
			if (from <= to) {
				for (i=from; i<=to; i+=step) {
					r = Q.handle(callback, this, args || [i], container);
					if (r === false) return false;
					if (step < 0) return 0;
				}
			} else {
				for (i=from; i>=to; i+=step) {
					r = Q.handle(callback, this, args || [i], container);
					if (r === false) return false;
					if (step > 0) return 0;
				}
			}
			break;
		case 'function':
		case 'boolean':
			if (container === false) break;
			throw new Q.Error("Q.each: does not support iterating a " + t);
		case 'null':
		case 'undefined':
			break;
	}
}

/**
 * Returns the first non-undefined value found in a container
 * @method first
 * @param {Array|Object|String} container
 * @param {Object} options
 *  "nonEmptyKey": return the first non-empty key
 * @return {mixed} the value in the container, or undefined
 * @throws {Q.Exception} If container is not array, object or string
 */
Q.first = function _Q_first(container, options) {
	var fk = Q.firstKey(container, options);
	return fk != null ? container[fk] : undefined;
};

/**
 * Returns the first key or index in a container with a value that's not undefined
 * @method firstKey
 * @param {Array|Object|String} container
 * @param {boolean} [options.nonEmptyKey] return the first non-empty key
 * @return {Number|String} the index in the container, or null
 * @throws {Q.Exception} If container is not array, object or string
 */
Q.firstKey = function _Q_firstKey(container, options) {
	if (!container) {
		return null;
	}
	switch (typeof container) {
		case 'array':
			for (var i=0; i<container.length; ++i) {
				if (container[i] !== undefined) {
					return i;
				}
			}
			break;
		case 'object':
			for (var k in container) {
				if (container.hasOwnProperty(k)
				&& container[k] !== undefined) {
					if (k || !options || !options.nonEmptyKey) {
						return k;
					}
				}
			}
			break;
		case 'string':
			return 0;
		default:
			throw new Q.Exception("Q.first: container has to be an array, object or string");
	}
	return null;
};

/**
 * Returns a container with the items in the first parameter that are not in the others
 * @method first
 * @param {Array|Object} container to subtract items from to form the result
 * @param {Array|Object} container whose items are subtracted in the result
 * @param {Function} comparator accepts item1, item2, index1, index2) and returns whether two items are equal
 * @return {Array|Object} a container of the same type as container1, but without elements of container2
 */
Q.diff = function _Q_diff(container1, container2 /*, ... comparator */) {
	if (!container1 || !container2) {
		return container1;
	}
	var len = arguments.length;
	var args = arguments;
	var comparator = arguments[len-1];
	if (typeof comparator !== 'function') {
		throw new Q.Exception("Q.diff: comparator must be a function");
	}
	var isArr = Q.isArrayLike(container1);
	var result = isArr ? [] : {};
	Q.each(container1, function (k, v1) {
		var found = false;
		for (var i=1; i<len-1; ++i) {
			Q.each(args[i], function (j, v2) {
				if (comparator(v1, v2, i, j)) {
					found = true;
					return false;
				}
			});
			if (found) {
				return;
			}
		}
		if (isArr) {
			result.push(v1);
		} else {
			result[k] = v1;
		}
	});
	return result;
};

/**
 * Tests whether a variable contains a falsy value,
 * or an empty object or array.
 * @method isEmpty
 * @param {mixed} o The object to test.
 */
Q.isEmpty = function _Q_isEmpty(o) {
	if (!o) {
		return true;
	}
	var i, v, t;
	t = Q.typeOf(o);
	if (t === 'array') {
		return (o.length === 0);
	}
	if (t === 'object') {
		for (i in o) {
			v = o[i];
			if (v !== undefined) {
				return false;
			}
		}
		return true;
	}
	return false;
};

/**
 * Tests if the value is an integer
 * @static
 * @method isInteger
 * @param {mixed} value 
 *  The value to test
 * @param {boolean} [strictComparison=true]
 *  Whether to test strictly for a number
 * @return {boolean}
 *	Whether it is an integer
 */
Q.isInteger = function _Q_isInteger(value, strictComparison) {
	if (strictComparison) {
		return value > 0 ? Math.floor(value) === value : Math.ceil(value) === value;
	}
	return value > 0 ? Math.floor(value) == value : Math.ceil(value) == value;
};

/**
 * Tests if the value is an array
 * @static
 * @method isArray
 * @param {mixed} value The value to test
 * @return {boolean} Whether it is an array
 */
Q.isArrayLike = function _Q_isArrayLike(value) {
	return (Q.typeOf(value) === 'array');
};

/**
 * Determines whether something is a plain object created within Javascript,
 * or something else, like a DOMElement or Number
 * @method isPlainObject
 * @return {boolean} Returns true only for a non-null plain object
 */
Q.isPlainObject = function (x) {
	if (x === null || typeof x !== 'object') {
		return false;
	}
	if (Object.prototype.toString.apply(x) !== "[object Object]") {
		return false;
	}
	return true;
};

/**
 * Use this instead of instanceof, it works with Q.mixin
 * @static
 * @method instanceOf
 * @param {mixed} testing
 * @param {Function} Constructor
 */
Q.instanceOf = function (testing, Constructor) {
	if (!testing || typeof testing !== 'object') {
		return false;
	}
	if (testing instanceof Constructor) {
		return true;
	}
	if (Constructor.__mixins) {
		for (var mixin in Constructor.__mixins) {
			if (testing instanceof mixin) {
				return true;
			}
		}
	}
	return false;
};

/**
 * Makes a shallow copy of an object. But, if any property is an object with a "copy" method,
 * or levels > 0, it recursively calls that method to copy the property.
 * @static
 * @method copy
 * @param {Array} fields
 *  Optional array of fields to copy. Otherwise copy all that we can.
 * @param {number} levels
 *  Optional. Copy this many additional levels inside x if it is a plain object.
 * @return {Object}
 *  Returns the shallow copy where some properties may have deepened the copy
 */
Q.copy = function _Q_copy(x, fields, levels) {
	if (Buffer && (x instanceof Buffer)) {
		var v = process.version.substr(1).split('.')
		.map(function (x) { return parseInt(x) });
		return v < [5, 10] ? new Buffer(x) : Buffer.from(x);
	}
	if (Q.isArrayLike(x)) {
		var result = Array.prototype.slice.call(x, 0);
		var keys = Object.keys(x);
		for (var i=0, l=keys.length; i<l; ++i) {
			result[keys[i]] = x[keys[i]];
		}
		return result;
	}
	if (x && typeof x.copy === 'function') {
		return x.copy();
	}
	if (x === null || !Q.isPlainObject(x)) {
		return x;
	}
	var result = Q.objectWithPrototype(Object.getPrototypeOf(x)), i, k, l;
	if (fields) {
		for (i=0, l = fields.length; i<l; ++i) {
			k = fields[i];
			if (!(k in x)) {
				continue;
			}
			result[k] = levels ? Q.copy(x[k], null, levels-1) : x[k];
		}
	} else {
		for (k in x) {
			if (!Q.has(x, k)) {
				continue;
			}
			result[k] = levels ? Q.copy(x[k], null, levels-1) : x[k];
		}
	}
	return result;
};

/**
 * Extends an object by merging other objects on top. Among other things,
 *  Q.Events can be extended with Q.Events or objects of {key: handler} pairs,
 *  Arrays can be extended by other arrays or objects.
 *  (If an array is being extended by an object with a "replace" property,
 *   the array is replaced by the value of that property.)
 *  You can also extend recursively, see the levels parameter.
 * @method extend
 * @param {Object} target
 *  This is the first object. It winds up being modified, and also returned
 *  as the return value of the function.
 * @param {number} levels
 *  Optional. Precede any Object with an integer to indicate that we should 
 *  also copy that many additional levels inside the object.
 * @param {Boolean|Number} deep
 *  Optional. Precede any Object with a boolean true to indicate that we should
 *  also copy the properties it inherits through its prototype chain.
 * @param {Object} anotherObject
 *  Put as many objects here as you want, and they will extend the original one.
 * @return {Object} The extended object.
 */
Q.extend = function _Q_extend(target /* [[deep,] [levels,] anotherObject], ... */ ) {
	var length = arguments.length;
	var namespace = undefined;
	if (typeof arguments[length-1] === 'string') {
		namespace = arguments[length-1];
		--length;
	}
	if (length === 0) {
		return {};
	}
	var deep = false, levels = 0, arg;
	var type = Q.typeOf(target);
	for (var i=1; i<length; ++i) {
		arg = arguments[i];
		if (!arg) {
			continue;
		}
		if (arg === true) {
			deep = true;
			continue;
		}
		if (typeof(arg) === 'number' && arg) {
			levels = arg;
			continue;
		}
		if (target === undefined) {
			if (Q.isArrayLike(arg)) {
				target = [];
				type = 'array';
			} else {
				target = {};
				type = 'object';
			}
		}
		if (Q.isArrayLike(target) && Q.isArrayLike(arg)) {
			target = target.concat(arg);
		} else {
			for (var k in arg) {
				if (deep !== true 
				&& (!arg.hasOwnProperty || !arg.hasOwnProperty(k))
				&& (arg.hasOwnProperty && (k in arg))) {
					continue;
				}
				var argk = arg[k];
				var ttk = Q.typeOf(target[k]);
				var tak = Q.typeOf(argk);
				if (levels 
				&& target[k]
				&& (typeof target[k] === 'object' || typeof target[k] === 'function') 
				&& (Q.isPlainObject(argk) || (ttk === 'array' && tak === 'array'))) {
					target[k] = (ttk === 'array' && ('replace' in argk))
						? Q.copy(argk.replace)
						: Q.extend(target[k], deep, levels-1, argk);
				} else {
					target[k] = Q.extend.dontCopy[Q.typeOf(argk)]
						? argk
						: Q.copy(argk, null, levels-1);
				}
				if (target[k] === undefined) {
					delete target[k];
				}
			}
		}
		deep = false;
		levels = 0;
	}
	return target;
};

Q.extend.dontCopy = { "Q.Tool": true };

/**
 * Mixes in one or more classes. Useful for inheritance and multiple inheritance.
 * @method mixin
 * @static
 * @param {Function} A
 *  The constructor corresponding to the "class" we are mixing functionality into
 *  This function will get the following members set:
 *  __mixins: an array of [B, C, ...]
 *  constructors(subject, params): a method to call the constructor of all mixin classes, in order. Pass "this" as the first argument.
 *  staticProperty(property): a method for getting a property name
 * @param {Function} B
 *  One or more constructors representing "classes" to mix functionality from
 *  They will be tried in the order they are provided, meaning methods from earlier ones
 *  override methods from later ones.
 */
Q.mixin = function _Q_mixin(A, B) {
	var __mixins = (A.__mixins || (A.__mixins = []));
	var mixin, i, k, l;
	for (i = 1, l = arguments.length; i < l; ++i) {
		mixin = arguments[i];
		if (typeof mixin !== 'function') {
			throw new Q.Error("Q.mixin: argument " + i + " is not a function");
		}
		var p = mixin.prototype, Ap = A.prototype;
		for (k in p) {
			if (!(k in Ap)) {
				Ap[k] = p[k];
			}
		}
		for (k in mixin) {
			if (!(k in A)) {
				A[k] = mixin[k];
			}
		}
		__mixins.push(arguments[i]);
	}

	A.staticProperty = function _staticProperty(propName) {
		for (var i=0; i<A.__mixins.length; ++i) {
			if (propName in A.__mixins[i]) {
				return A.__mixins[i].propName;
			}
		}
		return undefined;
	};
	
	A.constructors = function _constructors() {
		var mixins = A.__mixins;
		var i;
		for (i = mixins.length - 1; i >= 0; --i) {
			mixins[i].apply(this, arguments);
		}
	};

	A.prototype.constructors = function _prototype_constructors() {
		A.constructors.apply(this, arguments);
	};
};

/**
 * Copies a subset of the fields in an object
 * @method take
 * @static
 * @param {Object} source An Object from which to take things
 * @param {Array|Object} An array of fields to take or an object of fieldname: default pairs
 * @return {Object} a new Object
 */
Q.take = function _Q_take(source, fields) {
	var result = {};
	if (!source) return result;
	if (Q.isArrayLike(fields)) {
		for (var i = 0; i < fields.length; ++i) {
			if (fields[i] in source) {
				result [ fields[i] ] = source [ fields[i] ];
			}
		}
	} else {
		for (var k in fields) {
			result[k] = (k in source) ? source[k] : fields[k];
		}
	}
	return result;
};

/**
 * Returns whether an object contains a property directly
 * @method has
 * @static
 * @param {Object} obj
 * @param {String} key
 * @return {boolean}
 */
Q.has = function _Q_has(obj, key) {
	return Object.prototype.hasOwnProperty.call(obj, key);
};

/**
 * Shuffles an array
 * @method shuffle
 * @static
 * @param {Array} arr The array taht gets passed here is shuffled in place
 */
Q.shuffle = function _Q_shuffle( arr ) {
	var i = arr.length;
	if ( !i ) return false;
	while ( --i ) {
	var j = Math.floor( Math.random() * ( i + 1 ) );
		var tempi = arr[i];
		var tempj = arr[j];
		arr[i] = tempj;
		arr[j] = tempi;
	}
};

/**
 * Returns the number of milliseconds since the first call to this function
 * i.e. since this script was parsed.
 * @method milliseconds
 * @param {Boolean} sinceEpoch
 *  Defaults to false. If true, just returns the number of milliseconds in the UNIX timestamp.
 * @return {number}
 *  The number of milliseconds, with fractional part
 */
Q.milliseconds = function _Q_microtime(sinceEpoch) {
	var now = Date.now();
	if (sinceEpoch) {
		return now;
	}
	Q.milliseconds.started = Q.milliseconds.started || now;
	return now - Q.milliseconds.started;
};
Q.milliseconds();

/**
 * Returns the number of milliseconds since the
 * first call to this function (i.e. since script started).
 * @method milliseconds
 * @static
 * @param {Boolean} sinceEpoch
 *  Defaults to false. If true, just returns the number of milliseconds in the UNIX timestamp.
 * @return {float}
 *  The number of milliseconds, with fractional part
 */
Q.milliseconds = function (sinceEpoch) {
	var result = Date.now();
	if (sinceEpoch) return result;
	return result - Q.milliseconds.start;
};
Q.milliseconds.start = Date.now();

/**
 * Default exception handler for Q
 * @method exceptionHandler
 * @param {Exception} exception
 **/
Q.exceptionHandler = function _Q_exceptionHandler(exception) {
	debugger; // pause here if debugging
	// print the exception in the log and keep going
	var name = Q.Config
		? Q.Config.get(['Q', 'exception', 'nodeLogName'], null)
		: null;
	Q.log("UNCAUGHT EXCEPTION:", name);
	Q.log(exception, name);
	process.exit(1);
};
process.on('uncaughtException', Q.exceptionHandler);

/**
 * Search for directory and passes to callback if found. Passes an error if not directory
 * @method dir
 * @param {String} start Directory path
 * @param {Function} [callback=null] Callback functions with arguments "error", "result" where result is an object `{dirs: [...], files: [...]}`
 */
Q.dir = function _Q_dir(start, callback) {
	// Use lstat to resolve symlink if we are passed a symlink
	fs.lstat(start, function(err, stat) {
		if(err) {
			callback && callback(err);
			return;
		}
		var found = {dirs: [], files: []};
		var total = 0;
		var processed = 0;
		function _loadPath(abspath) {
			fs.stat(abspath, function(err, stat) {
				if(stat.isDirectory()) {
					found.dirs.push(abspath);
					// If we found a directory, recurse!
					Q.dir(abspath, function(err, data) {
						found.dirs = found.dirs.concat(data.dirs);
						found.files = found.files.concat(data.files);
						if(++processed == total) {
							callback && callback(null, found);
						}
					});
				} else {
					found.files.push(abspath);
					if(++processed == total) {
						callback && callback(null, found);
					}
				}
			});
		}
		// Read through all the files in this directory
		if(stat.isDirectory()) {
			fs.readdir(start, function (err, files) {
				if (files.length) {
					total = files.length;
					for(var x=0, l=files.length; x<l; x++) {
						_loadPath(path.join(start, files[x]));
					}					
				} else {
					callback && callback(null, {dirs: [], files: []});
				}
			});
		} else {
			callback && callback(new Error("path: " + start + " is not a directory"));
		}
	});
};

/**
 * Normalizes text by converting it to lower case, and
 * replacing all non-accepted characters with underscores.
 * @method normalize
 * @param {String} text The text to normalize
 * @param {String} replacement
 *  Defaults to '_'. A string to replace one or more unacceptable characters.
 *  You can also change this default using the config Db/normalize/replacement
 * @param {String} characters
 *  Defaults to '/[^A-Za-z0-9]+/'. A regexp characters that are not acceptable.
 *  You can also change this default using the config Db/normalize/characters
 * @param {number} numChars
 *  The maximum length of a normalized string. Default is 200.
 * @param {boolean} [keepCaseIntact=false] If true, doesn't convert to lowercase
 * @return {String} the normalized string
 */
Q.normalize = function _Q_normalize(text, replacement, characters, numChars, keepCaseIntact) {
	if (!numChars) numChars = 200;
	if (replacement === undefined) replacement = '_';
	if (text instanceof Buffer) {
		text = text.toString();
	}
	characters = characters || /[^A-Za-z0-9]+/g;
	if (text === undefined) {
		debugger; // pause here if debugging
	}
	if (!keepCaseIntact) {
		text = text.toLowerCase();
	}
	var result = text.replace(characters, replacement);
	if (result.length > numChars) {
		result = result.substr(0, numChars-11) + '_'
			+ Math.abs(result.substr(numChars-11).hashCode());
	}
	return result;
};

/*
 * A collection of HTTP servers started with Q.listen
 * @property servers
 * @type Object
 * @default {}
 */
Q.servers = {};

/**
 * Starts internal server to listen for messages from PHP processes and other things.
 * Uses the Q/node/port and Q/node/host config fields.
 * Make sure to protect the communication using a firewall.
 * @static
 * @method listen
 * @param {Object} [options={}] Options can include:
 * @param {String} [options.port] the port to listen on
 * @param {String} [options.host] the hostname to listen on
 * @param {Array} [options.attach] an array of additional listeners to attach. Each member is a name of a class (e.g. "Q.Socket", "Q.Dispatcher" and "Db") which has the listen(options) method.
 * @param {Object} [options.https] To start an https server, pass options to https.createServer here, to override the ones in the "Q"/"node"/"https" config options, if any.
 * @param {String|Buffer} [options.https.key] Content of the private key file
 * @param {String|Buffer} [options.https.cert] Content of the certificate file
 * @param {String|Buffer} [options.https.ca] Content of the certificate authority file
 * @param {String|Buffer} [options.https.dhparam] Contains the DH parameters for Perfect Forward Secrecy
 * @param {Function} [callback=null] fired when the server actually starts listening.
 *	The callback receives server address as argument
 * @throws {Q.Exception} if config field Q/nodeInternal/port or Q/nodeInternal/host are missing
 */
Q.listen = function _Q_listen(options, callback) {
	options = options || {};
	var internalPort = Q.Config.get(['Q', 'nodeInternal', 'port'], null);
	var internalHost = Q.Config.get(['Q', 'nodeInternal', 'host'], null);
	var port = options.port || internalPort;
	var host = options.host || internalHost;
	var info;

	if (port === null)
		throw new Q.Exception("Q.listen: Missing config field: Q/nodeInternal/port");
	if (host === null)
		throw new Q.Exception("Q.listen: Missing config field: Q/nodeInternal/host");

	var server = Q.getObject([port, host], Q.servers) || null;
	if (server) {
		var address = server.address();
		if (address) callback && callback(address);
		else server.once('listening', function () {
			callback && callback(server.address());
		});
		return server;
	}
	var _express;
	if (express.version === undefined
	|| parseInt(express.version) >= 3) {
		_express = express();
		if (!Q.isEmpty(options.https)) {
			var h = Q.Config.get(['Q', 'node', 'https'], false) || {};
			var keys = ['key', 'cert', 'ca', 'dhparam'];
			keys.forEach(function (k) {
				if (h[k]) {
					h[k] = fs.readFileSync(h[k]).toString();
				}
			});
			if (Q.isPlainObject(options.https)) {
				Q.extend(h, options.https);
			}
			server = https.createServer(h, _express);
		} else {
			server = http.createServer(_express);
		}
	} else {
		server = express.createServer();
		_express = server;
	}
	server.host = host;
	server.port = port;
	server.attached = {
		express: _express
	};
	
	var app = server.attached.express;
	app.use(express.bodyParser());
	
	var use = app.use;
	app.use = function _app_use() {
		console.log("Adding request handler under " + server.host + ":" + server.port + " :", arguments[0].name);
		use.apply(this, Array.prototype.slice.call(arguments));
	};
	var methods = {
		"get": "GET",
		"post": "POST",
		"put": "PUT",
		"del": "DELETE",
		"options": "OPTIONS",
		"all": "ALL"
	};
	Q.each(methods, function (k) {
		var f = app[k];
		app[k] = function () {
			var w, h;
			if (arguments.length > 1) {
				w = arguments[0];
				h = arguments[1];
			} else if (typeof arguments[0] === 'function') {
				w = '';
				h = arguments[0];
			} else {
				return;
			}
			if (typeof h === 'function') {
				h = h.name;
			} else if (typeof h !== 'string') {
				h = h.toString();
			}
			console.log("Adding " + methods[k] + " handler under "
				+ server.host + ":" + server.port
				+ w + " :", h);
			f.apply(this, Array.prototype.slice.call(arguments));
		};
	});
	app.use(function Q_request_handler (req, res, next) {
		// WARNING: the following per-request log may be a bottleneck in high-traffic sites:
		var a = server.address();
		if (Q.Config.get('Q', 'node', 'logRequests', true)) {
			Q.log(req.method+" "+req.socket.remoteAddress+ " -> "+a.address+":"+a.port+req.url.split('?', 2)[0] + (req.body['Q/method'] ? ", method: '"+req.body['Q/method']+"'" : ''));
		}
		req.info = {
			port: port,
			host: host
		};
		var headers;
		if (headers = Q.Config.get(['Q', 'node', 'headers'], false)) {
			res.header(headers);
		}
		if (internalHost == host && internalPort == port) {
			Q.Utils.validate(req, res, _requested);
		} else {
			_requested();
		}
		function _requested () {
			/**
			 * Http request
			 * @event request
			 * @param {http.Request} req The request object
			 * @param {http.Response} res The response object
			 */
			Q.emit('request', req, res);
			next();
		}
	});
	server.listen(port, host, function () {
		var internalString = (internalHost == host && internalPort == port) ? ' (internal requests)' : '';
		console.log('Q: listening at ' + host + ':' + port + internalString);
		callback && callback(server.address());
	});

	if (!Q.servers[port]) {
		Q.servers[port] = {};
	}
	Q.servers[port][host] = server;
	return server;
};

/**
 * This should be called from Q.inc.js
 * @method init
 * @param {Object} app An object that MUST contain one key:
 * @param {Object} app.DIR the directory of the app
 * @param {boolean} [notListen=false] Indicate wheather start http server. Useful for forking parallel processes.
 * @throws {Q.Exception} if app is not provided or does not contain DIR field
 */
Q.init = function _Q_init(app, notListen) {
	if (!app) { throw new Q.Exception("Q.init: app is required"); }
	if (!app.DIR) { throw new Q.Exception("Q.init: app.DIR is required"); }

	var path = require('path');
	var Q_dir = path.normalize(__dirname+'/..');
	
	if (require('os').type().toLowerCase().indexOf('windows') === -1) {
		/**
		 * Directory separator
		 * @property DS
		 * @type string
		 */
		Q.DS = '/';
		/**
		 * Path separator
		 * @property PS
		 * @type string
		 */
		Q.PS = ':';
	} else {
		Q.DS = '\\';
		Q.PS = ';';
	}
	/**
	 * App data for your scripts
	 * @property app
	 * @type object
	 */
	Q.app = Q.copy(app);
	
	//
	// constants
	//
	var dirs = {
		/**
		 * Directory for platform classes. Also Q.app.CLASSES_DIR is defined for application classes
		 * @property CLASSES_DIR
		 * @type string
		 */
		CLASSES_DIR: 'classes',
		/**
		 * Directory for platform config. Also Q.app.CONFIG_DIR is defined for application config
		 * @property CONFIG_DIR
		 * @type string
		 */
		CONFIG_DIR: 'config',
		/**
		 * Directory for platform local config. Also Q.app.LOCAL_DIR is defined for application local config
		 * @property LOCAL_DIR
		 * @type string
		 */
		LOCAL_DIR: 'local',
		/**
		 * Directory for platform files. Also Q.app.FILES_DIR is defined for application files
		 * @property FILES_DIR
		 * @type string
		 */
		FILES_DIR: 'files',
		/**
		 * Directory for platform handlers. Also Q.app.HANDLERS_DIR is defined for application handlers
		 * @property HANDLERS_DIR
		 * @type string
		 */
		HANDLERS_DIR: 'handlers',
		/**
		 * Directory for platform plugins. Also Q.app.PLUGINS_DIR is defined for application plugins
		 * @property PLUGINS_DIR
		 * @type string
		 */
		PLUGINS_DIR: 'plugins',
		/**
		 * Directory for platform scripts. Also Q.app.SCRIPTS_DIR is defined for application scripts
		 * @property SCRIPTS_DIR
		 * @type string
		 */
		SCRIPTS_DIR: 'scripts',
		/**
		 * Directory for platform test dir. Also Q.app.TESTS_DIR is defined for application test dir
		 * @property TESTS_DIR
		 * @type string
		 */
		TESTS_DIR: 'tests',
		/**
		 * Directory for platform views. Also Q.app.VIEWS_DIR is defined for application views
		 * @property VIEWS_DIR
		 * @type string
		 */
		VIEWS_DIR: 'views'
	};
	var k;
	for (k in dirs) {
		Q[k] = Q_dir  + '/' + dirs[k];
	}
	for (k in dirs) {
		if (!(k in Q.app)) {
			Q.app[k] = Q.app.DIR  + '/' + dirs[k];
		}
	}
	
	//
	// modules
	//
	/**
	 * Reference to Q.Exception class
	 * @property Exception
	 * @type {object}
	 */
	Q.Exception = require('./Q/Exception');
	/**
	 * Reference to Q.Tree class
	 * @property Tree
	 * @type {object}
	 */
	Q.Tree = require('./Q/Tree');
	/**
	 * Reference to Q.Config class
	 * @property Config
	 * @type {object}
	 */
	Q.Config = require('./Q/Config');
	/**
	 * Reference to Q.Text class
	 * @property Text
	 * @type {object}
	 */
	Q.Text = require('./Q/Text');
	/**
	 * Reference to Q.Bootstrap class
	 * @property Bootstrap
	 * @type {object}
	 */
	Q.Bootstrap = require('./Q/Bootstrap');
	/**
	 * Reference to Q.Request class
	 * @property Request
	 * @type {object}
	 */
	Q.Request = require('./Q/Request');
	/**
	 * Reference to Q.Socket class
	 * @property Socket
	 * @type {object}
	 */
	Q.Socket = require('./Q/Socket');
	/**
	 * Reference to Q.Dispatcher class
	 * @property Dispatcher
	 * @type {object}
	 */
	Q.Dispatcher = require('./Q/Dispatcher');
    /**
     * Reference to Q.Handlebars class
     * @property Handlebars
     * @type {object}
     */
    Q.Handlebars = require('./Q/Handlebars');
   /**
    * Reference to Q.Crypto class
    * @property Crypto
    * @type {object}
    */
	Q.Crypto = require('./Q/Crypto');
	//
	// set things up
	//
	//Q.Bootstrap.registerExceptionHandler();
	Q.Bootstrap.setIncludePath();
	/**
	 * Reference to Q.Utils class
	 * @property Utils
	 * @type {object}
	 */
	Q.Utils = require('./Q/Utils');
	Q.Bootstrap.configure(function (err) {
		if (err) {
			// if run as child Q.Bootstrap.configure returns errors in callback
			process.exit(2);
		}
		Q.app.name = Q.Config.expect(["Q", "app"]);
		Q.Bootstrap.loadPlugins(function () {
			Q.Bootstrap.loadHandlers(function () {
				console.log(typeof notListen === "string" ? notListen : 'Q platform initialized!');
				/**
				 * Qbix platform initialized
				 * @event init
				 * @param {Q} Q Initialized Qbix instance
				 */
				Q.emit('init', Q);
			});
		});
	}, notListen);
};

/**
 * Renders a particular view
 * @method view
 * @param {string} viewName
 *  The full name of the view
 * @param {array} [params=array] Parameters to pass to the view
 * @param {array} [options=array] Some options
 * @param {string|null} [options.language=null] Preferred language
 * @param {string|null} [options.source=false]
 * @return {string} The rendered content of the view
 */
Q.view = function _Q_view(viewName, params, options) {
	params = params || [];
	options = options || [];

	var parts = viewName.split('/');
	var viewPath = parts.join(Q.DS);
	var fields = Q.Config.get(['Q', 'views', 'fields'], null);
	if (fields && typeof fields === 'object') {
		params = Q.extend(fields, params);
	}

	// set options
	options.language = options.language || null;
	params.language = options.language;

	var textParams = Q.Text.params(parts, {'language': options.language});
	params = Q.extend(textParams, params);


	if (options.source) {
		return Q.Handlebars.renderSource(viewName, params);
	}

	return Q.Handlebars.render(viewPath, params);
};
/**
 * Check if a file exists in the include path
 * And if it does, return the absolute path.
 * @method realPath
 * @param {String} filename Name of the file to look for
 * @param {boolean} [ignoreCache=false] If true, then this function ignores
 *  the cached value, if any, and always attempts to search
 *  for the file. It will cache the new value.
 * @return {String|false} The absolute path if file exists, false if it does not
 */
Q.realPath = function _Q_realPath(filename, ignoreCache) {
  if (!ignoreCache && (filename in realPath_results)) {
		return realPath_results[filename];
	}
	var result = false, paths = (process.env.NODE_PATH || '').split(Q.PS);
	for (var i = 0; i<=paths.length; ++i) {
		var p = (i == paths.length) ? '' : paths[i];
		var fullpath = path.normalize((p.substr(-1) === Q.DS) ? p + filename : p + Q.DS + filename);
		if (fs.existsSync(fullpath)) {
			result = fullpath;
			break;
		}
  }
	realPath_results[filename] = result;
	return result;
};
var realPath_results = {};

/**
 * Serialize a plain object, with possible sub-objects, into an http querystring.
 * @static
 * @method queryString
 * @param {Object|String|HTMLElement} fields
 *  The object to serialize into a querystring that can be sent to PHP or something.
 *  The algorithm will recursively walk the object, and skip undefined values.
 *  You can also pass a form element here. If you pass a string, it will simply be returned.
 * @param {Array} keys
 *  An optional array of keys into the object, in the order to serialize things
 * @param {boolean} returnAsObject
 *  Pass true here to get an object of {fieldname: value} instead of a string
 * @return {String}
 *  A querystring that can be used with HTTP requests
 */
Q.queryString = function _Q_queryString(fields, keys, returnAsObject) {
	if (Q.isEmpty(fields)) {
		return '';
	}
	if (typeof fields === 'string') {
		return fields;
	}
	if (fields instanceof Element) {
		if (fields.tagName.toUpperCase() !== 'FORM') {
			throw new Q.Error("Q.queryString: element must be a FORM");
		}
		var result = '';
		Q.each(fields.querySelectorAll('input, textarea, select'), function () {
			var value = (this.tagName.toUpperCase() === 'SELECT')
				? this.options[this.selectedIndex].text
				: this.value;
			result += (result ? '&' : '') + this.getAttribute('name')
				+ '=' + encodeURIComponent(value);
		});
		return result;
	}
	var parts = [];
	function _params(prefix, obj) {
		if (obj == undefined) {
			return;
		}
		if (Q.typeOf(obj) === "array") {
			// Serialize array item.
			Q.each(obj, function _Q_param_each(i, value) {
				if (/\[\]$/.test(prefix)) {
					// Treat each array item as a scalar.
					_add(prefix, value);
				} else {
					_params(prefix + "[" + (Q.typeOf(value) === "object" || Q.typeOf(value) === "array" ? i : "") + "]", value, _add);
				}
			});
		} else if (obj && Q.typeOf(obj) === "object") {
			// Serialize object item.
			for (var name in obj) {
				_params(prefix + "[" + name + "]", obj[name], _add);
			}
		} else {
			// Serialize scalar item.
			_add(prefix, obj);
		}
	}
	
	var result = {};
	
	function _add(key, value) {
		// If value is a function, invoke it and return its value
		value = Q.typeOf(value) === "function" ? value() : value;
		if (value == undefined) return;
		if (returnAsObject) {
			result[key] = value;
		} else {
			parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(value));
		}
	}

	if (keys) {
		Q.each(keys, function _Q_param_each(i, field) {
			_params(field, fields[field]);
		});
	} else {
		Q.each(fields, function _Q_param_each(field, value) {
			_params(field, value);
		});
	}

	// Return the resulting serialization
	return returnAsObject
		? result
		: parts.join("&").replace(/%20/g, "+");
};

/**
 * Require node module
 * @method require
 * @param {String} what
 * @return {mixed}
 */
Q.require = function _Q_require(what) {
	var ext = what.split('.').pop();
	var realPath = Q.realPath(what + (ext === 'js' ? '' : '.js'));
	if (!realPath && ext != 'js') {
		var path = Q.realPath(what);
		if (path && fs.lstatSync(path).isFile()) {
			realPath = path;
		}
	}
	if (!realPath) {
		throw new Error("Q.require: file '"+what+"' not found");
	}
	return require(realPath);
};

var getLogStream = Q.getter(function (name, callback) {
	var path = ((Q.app && Q.app.FILES_DIR)
		? Q.app.FILES_DIR
		: Q.FILES_DIR)+Q.DS+'Q'+Q.DS+Q.Config.get(['Q', 'internal', 'logDir'], 'logs');
	var filename = path+Q.DS+name+'_node.log';
	Q.Utils.preparePath(filename, function (err) {
		if (err) {
			console.error("Failed to create directory '"+path+"', Error:", err.message);
			return callback && callback(err);
		}
		try {
			var stats = fs.statSync(filename);
		} catch (err) {
			if (err && err.code !== 'ENOENT') {
				err.message = "Could not stat '"+filename+"', Error:", err.message;
				callback && callback(err);
				return;
			}
		}
		if (stats && !stats.isFile()) {
			callback && callback(new Error("'"+filename+"' exists but is not a file"));
			return;
		}
		var stream = fs.createWriteStream(
			filename, {flags: 'a', encoding: 'utf-8'}
		);
		callback(null, stream);
	});
});

/**
 * Returns date/time string formatted the same way as PHP date function does
 * @method date
 * @param {String} format The format string
 * @param {number} timestamp The date to format
 * @return {String}
 */
Q.date = function (format, timestamp) {
	// http://kevin.vanzonneveld.net
	var jsdate, f, formatChr = /[a-z]{1}/gi,
		// Keep this here (works, but for code commented-out
		// below for file size reasons)
		//, tal= [],
		_pad = function (n, c) {
			if ((n = n + '').length < c) {
				return new Array((++c) - n.length).join('0') + n;
			}
			return n;
		},
		txt_words = ["Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
	function formatChrCb(t, s) {
		return f[t] ? f[t]() : s;
	};
	f = {
		// Day
		d: function () { // Day of month w/leading 0; 01..31
			return _pad(f.j(), 2);
		},
		D: function () { // Shorthand day name; Mon...Sun
			return f.l().slice(0, 3);
		},
		j: function () { // Day of month; 1..31
			return jsdate.getDate();
		},
		l: function () { // Full day name; Monday...Sunday
			return txt_words[f.w()] + 'day';
		},
		N: function () { // ISO-8601 day of week; 1[Mon]..7[Sun]
			return f.w() || 7;
		},
		S: function () { // Ordinal suffix for day of month; st, nd, rd, th
			var j = f.j();
			return j < 4 | j > 20 && ['st', 'nd', 'rd'][j%10 - 1] || 'th'; 
		},
		w: function () { // Day of week; 0[Sun]..6[Sat]
			return jsdate.getDay();
		},
		z: function () { // Day of year; 0..365
			var a = new Date(f.Y(), f.n() - 1, f.j()),
				b = new Date(f.Y(), 0, 1);
			return Math.round((a - b) / 864e5) + 1;
		},

		// Week
		W: function () { // ISO-8601 week number
			var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3),
				b = new Date(a.getFullYear(), 0, 4);
			return _pad(1 + Math.round((a - b) / 864e5 / 7), 2);
		},

		// Month
		F: function () { // Full month name; January...December
			return txt_words[6 + f.n()];
		},
		m: function () { // Month w/leading 0; 01...12
			return _pad(f.n(), 2);
		},
		M: function () { // Shorthand month name; Jan...Dec
			return f.F().slice(0, 3);
		},
		n: function () { // Month; 1...12
			return jsdate.getMonth() + 1;
		},
		t: function () { // Days in month; 28...31
			return (new Date(f.Y(), f.n(), 0)).getDate();
		},

		// Year
		L: function () { // Is leap year?; 0 or 1
			var j = f.Y();
			return j%4===0 & j%100!==0 | j%400===0;
		},
		o: function () { // ISO-8601 year
			var n = f.n(),
				W = f.W(),
				Y = f.Y();
			return Y + (n === 12 && W < 9 ? -1 : n === 1 && W > 9);
		},
		Y: function () { // Full year; e.g. 1980...2010
			return jsdate.getFullYear();
		},
		y: function () { // Last two digits of year; 00...99
			return (f.Y() + "").slice(-2);
		},

		// Time
		a: function () { // am or pm
			return jsdate.getHours() > 11 ? "pm" : "am";
		},
		A: function () { // AM or PM
			return f.a().toUpperCase();
		},
		B: function () { // Swatch Internet time; 000..999
			var H = jsdate.getUTCHours() * 36e2,
				// Hours
				i = jsdate.getUTCMinutes() * 60,
				// Minutes
				s = jsdate.getUTCSeconds(); // Seconds
			return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3);
		},
		g: function () { // 12-Hours; 1..12
			return f.G() % 12 || 12;
		},
		G: function () { // 24-Hours; 0..23
			return jsdate.getHours();
		},
		h: function () { // 12-Hours w/leading 0; 01..12
			return _pad(f.g(), 2);
		},
		H: function () { // 24-Hours w/leading 0; 00..23
			return _pad(f.G(), 2);
		},
		i: function () { // Minutes w/leading 0; 00..59
			return _pad(jsdate.getMinutes(), 2);
		},
		s: function () { // Seconds w/leading 0; 00..59
			return _pad(jsdate.getSeconds(), 2);
		},
		u: function () { // milliseconds; 000000-999000
			return _pad(jsdate.getMilliseconds() * 1000, 6);
		},

		// Timezone
		e: function () { // Timezone identifier; e.g. Atlantic/Azores, ...
			// The following works, but requires inclusion of the very large
			// timezone_abbreviations_list() function.
/*              return this.date_default_timezone_get();
*/
			throw new Q.Error("Not supported (see source code of date() for timezone on how to add support)");
		},
		I: function () { // DST observed?; 0 or 1
			// Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
			// If they are not equal, then DST is observed.
			var a = new Date(f.Y(), 0),
				// Jan 1
				c = Date.UTC(f.Y(), 0),
				// Jan 1 UTC
				b = new Date(f.Y(), 6),
				// Jul 1
				d = Date.UTC(f.Y(), 6); // Jul 1 UTC
			return 0 + ((a - c) !== (b - d));
		},
		O: function () { // Difference to GMT in hour format; e.g. +0200
			var tzo = jsdate.getTimezoneOffset();
			var a = Math.abs(tzo);
			return (tzo > 0 ? "-" : "+") + _pad(Math.floor(a / 60) * 100 + a % 60, 4);
		},
		P: function () { // Difference to GMT w/colon; e.g. +02:00
			var O = f.O();
			return (O.substr(0, 3) + ":" + O.substr(3, 2));
		},
		T: function () { // Timezone abbreviation; e.g. EST, MDT, ...
			// The following works, but requires inclusion of the very
			// large timezone_abbreviations_list() function.
/*              var abbr = '', i = 0, os = 0, default = 0;
			if (!tal.length) {
				tal = that.timezone_abbreviations_list();
			}
			if (that.php_js && that.php_js.default_timezone) {
				default = that.php_js.default_timezone;
				for (abbr in tal) {
					for (i=0; i < tal[abbr].length; i++) {
						if (tal[abbr][i].timezone_id === default) {
							return abbr.toUpperCase();
						}
					}
				}
			}
			for (abbr in tal) {
				for (i = 0; i < tal[abbr].length; i++) {
					os = -jsdate.getTimezoneOffset() * 60;
					if (tal[abbr][i].offset === os) {
						return abbr.toUpperCase();
					}
				}
			}
*/
			return 'UTC';
		},
		Z: function () { // Timezone offset in seconds (-43200...50400)
			return -jsdate.getTimezoneOffset() * 60;
		},

		// Full Date/Time
		c: function () { // ISO-8601 date.
			return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb);
		},
		r: function () { // RFC 2822
			return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb);
		},
		U: function () { // Seconds since UNIX epoch
			return jsdate / 1000 | 0;
		}
	};
	jsdate = (!timestamp ? new Date() : (timestamp instanceof Date) ? new Date(timestamp) : new Date(timestamp * 1000));
	return format.replace(formatChr, formatChrCb);
};

var timeHandles = {};

/**
 * Start time counter
 * @method time
 * @param {String} handle A handle to refer to time counter. Shall be namespaced to avoid overlap with
 *	other possible counters - Q/PROCESS/NAME
 */
Q.time = function _Q_time(handle) {
	timeHandles[handle] = (new Date()).getTime();
};

/**
 * Retrieves time difference between start by Q.time() and current time.
 * Time is formatted string <code>"XX days XX hours XX minutes XX seconds"</code>
 * If time is less than a second returns <code>"XXX milliseconds"</code>
 * @method timeEnd
 * @param {String} handle The handle started with Q.time(). If not started returns null
 * @return {String|null}
 */
Q.timeEnd = function _Q_timeEnd(handle) {
	if (!timeHandles[handle]) {
		return null;
	}
	var diff = (new Date()).getTime() - timeHandles[handle];
	var days = Math.floor(diff / 1000 / 60 / 60 / 24);
	var hours = Math.floor(diff / 1000 / 60 / 60 - (24 * days));
	var minutes = Math.floor(diff / 1000 / 60 - (24 * 60 * days) - (60 * hours));
	var seconds = Math.floor(diff / 1000 - (24 * 60 * 60 * days) - (60 * 60 * hours) - (60 * minutes));
	return 	((days > 0) ? days+" days " : '') +
			((days+hours > 0) ? hours+" hours " : '') +
			((days+hours+minutes > 0) ? minutes+" minutes " : '') +
			((days+hours+minutes+seconds > 0) ? seconds+" seconds" : diff+" milliseconds");
};

/**
 * Try to find an error message assuming typical error data structures for the arguments
 * @static
 * @method firstErrorMessage
 * @param {Object} data An object where the errors may be found. You can pass as many of these as you want. If it contains "errors" property, then errors[0] is the first error. If it contains an "error" property, than that's the first error. Otherwise, for the first argument only, if it is nonempty, then it's considered an error.
 * @return {String|null} The first error message found, or null
 */
Q.firstErrorMessage = function _Q_firstErrorMessage(data /*, data2, ... */) {
	var error = null;
	for (var i=0; i<arguments.length; ++i) {
		var d = arguments[i];
		if (Q.isEmpty(d)) {
			continue;
		}
		if (d.errors && d.errors[0]) {
			error = d.errors[0];
		} else if (d.error) {
			error = d.error;
		} else if (Q.isArrayLike(d)) {
			error = d[0];
		} else if (!i) {
			error = d;
		}
		if (error) {
			break;
		}
	}
	if (!error) {
		return null;
	}
	return (typeof error === 'string')
		? error
		: (error.message ? error.message : JSON.stringify(error));
};

/**
 * Writes a string to application log. If run outside Qbix application writes to console.
 * @method log
 * @param {mixed} message The data to write to log file. If data is string it is written to log, if it has other type
 *	it is converted to string using util.format with depth defined by Q/var_dump_max_levels config key
 * @param {String} [name] If set log file will be named name+'_node.log', otherwise it would be named ('Q/app' config value) + '_node.log'
 * @param {boolean} [timestamp=true] Whether to prepend the current timestamp
 * @param {Function} [callback=null] The callback to call after log file is written
 * @return {boolean} false if failed to parse arguments
 */
Q.log = function _Q_log(message, name, timestamp, callback) {
	if (typeof timestamp === "undefined") timestamp = true;
	if (typeof name === "function") {
		callback = name;
		timestamp = true;
		name = Q.Config.get(['Q', 'app'], false);
	} else if (typeof timestamp === "function") {
		callback = timestamp;
		timestamp = true;
	}
	if (typeof name === "undefined" || name === true) {
		name = Q.Config.get(['Q', 'app'], false);
	}

	if (typeof message !== "string") {
		if (!message) {
			message = JSON.stringify(message);
		} else if (message instanceof Error
		|| (message.fileName && message.stack)) {
			var error = message;
			message = error.name + ": " + error.message
				+ "\n" + "in " + error.fileName
					+ " at (" + error.lineNumber + ":" + error.columnNumber + ")"
				+ "\n" + error.stack;
		} else {
			message = 'inspecting '+Q.typeOf(message)+':\n'+util.inspect(message, false, Q.Config.get('Q', 'var_dump_max_levels', 5));
		}
	}

	message = (timestamp ? '['+Q.date('Y-m-d H:i:s')+'] ' : '')+(name ? name : 'Q')+': ' + message + "\n";

	if (!name) {
		return console.log(message);
	}
	getLogStream(name, function (err, stream) {
		if (err) {
			console.log(err);
			return;
		}
		stream.write(message);
	});
};

/**
 * Obtain a URL
 * @method url
 * @param {Object} what
 *  Usually the stuff that comes after the base URL
 * @param {Object} fields
 *  Optional fields to append to the querystring.
 *  NOTE: only handles scalar values in the object.
 * @param {Object} [options] A hash of options, including:
 * @param {String} [options.baseUrl] A string to replace the default base url
 * @param {Number} [options.cacheBust] Number of milliseconds before a new cachebuster is appended
 */
Q.url = function _Q_url(what, fields, options) {
	var what2 = what;
	var parts = what2.split('?');
	if (fields) {
		for (var k in fields) {
			parts[1] = (parts[1] || "").queryField(k, fields[k]);
		}
		what2 = parts[0] + (parts[1] ? '?' + parts[1] : '');
	}
	if (options && options.cacheBust) {
		what2 += "?Q.cacheBust="+Math.floor(Date.now()/options.cacheBust);
	}
	parts = what2.split('?');
	if (parts.length > 2) {
		what2 = parts.slice(0, 2).join('?') + '&' + parts.slice(2).join('&');
	}
	what2 = Q.interpolateUrl(what2);
	var result = '';
	var baseUrl = (options && options.baseUrl);
	if (!baseUrl) {
		baseUrl = Q.Config.get(['Q', 'web', 'appRootUrl']);
	}
	if (!what) {
		result = baseUrl + (what === '' ? '/' : '');
	} else if (what2.isUrl()) {
		result = what2;
	} else {
		result = baseUrl + ((what2.substr(0, 1) == '/') ? '' : '/') + what;
	}
	return result;
};

/**
 * Interpolate some standard placeholders inside a url, such as 
 * {{AppName}} or {{PluginName}}
 * @static
 * @method interpolateUrl
 * @param {String} url
 * @param {Object} [additional={}] Any additional substitutions
 * @return {String} The url with substitutions applied
 */
Q.interpolateUrl = function (url, additional) {
	if (url.indexOf('{{') < 0) {
		return url;
	}
	var substitutions = {};
	var baseUrl = Q.Config.get(['Q', 'web', 'appRootUrl']);
	substitutions['baseUrl'] = substitutions[Q.app.name] = baseUrl;
	substitutions['Q'] = Q.pluginBaseUrl('Q');
	for (var plugin in Q.plugins) {
		substitutions[plugin] = Q.pluginBaseUrl(plugin);
	}
	url = url.interpolate(substitutions);
	if (additional) {
		url = url.interpolate(additional);
	}
	return url;
};

/**
 * You can override this function to do something special
 * @method pluginBaseUrl
 */
Q.pluginBaseUrl = function (plugin) {
	return 'Q/plugins/' + plugin;
};

/*
 * Extend some built-in prototypes
 */

if (!Object.getPrototypeOf) {
	Object.getPrototypeOf = function (obj) {
		if (obj.__proto__) return obj.__proto__;
		if (obj.constructor && obj.constructor.prototype) {
			return obj.constructor.prototype;
		}
		return null;
	};
}

Date.fromTimestamp = function (timestamp) {
	if (isNaN(timestamp)) {
		return null;
	}
	timestamp = parseFloat(timestamp);
	return new Date(timestamp < 10000000000 ? timestamp * 1000 : timestamp);
};

Date.from = function (input) {
	if (input instanceof Date) {
		return input;
	}
	return Date.fromTimestamp(input) || new Date(input);
};

/**
 * @class String
 * @description Q extended methods for Strings
 */

var Sp = String.prototype;

/**
 * Returns a copy of the string with Every Word Capitalized
 * @method toCapitalized
 * @return {String}
 */
Sp.toCapitalized = function _String_prototype_toCapitalized() {
	return this.replace(/^([a-z])|\s+([a-z])/g, function (found) {
		return found.toUpperCase();
	});
};

/**
 * Determins whether the string's contents are a URL
 * @method isUrl
 * @return {boolean}
 */
Sp.isUrl = function _String_prototype_isUrl () {
	return !!this.match(/^([A-Za-z]*:|)\/\//);
};

/**
 * Determins whether the string's contents are an IP address
 * @method isUrl
 * @return {boolean}
 */
Sp.isIPAddress = function _String_prototype_isIPAddress () {
	return !!this.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/);
};

/**
 * Returns a copy of the string with special HTML characters escaped
 * @method encodeHTML
 * @param {Array} [convert] Array of characters to convert. Can include
 *   '&', '<', '>', '"', "'", "\n"
 * @return {String}
 */
Sp.encodeHTML = function _String_prototype_encodeHTML(convert) {
	var conversions = {
		'&': '&amp;',
		'<': '&lt;',
		'>': '&gt;',
		'"': '&quot;',
		"'": '&apos;',
		"\n": '<br>'
	};
	if (convert) {
		conversions = Q.take(conversions, convert);
	}
	return this.replaceAll(conversions);
};

/**
 * Reverses what encodeHTML does
 * @method decodeHTML
 * @param {Array} [convert] Array of codes to unconvert. Can include
 *  '&amp;', '&lt;', '&gt;, '&quot;', '&apos;', "<br>", "<br />"
 * @return {String}
 */
Sp.decodeHTML = function _String_prototype_decodeHTML(unconvert) {
	var conversions = {
		'&amp;': '&',
		'&lt;': '<',
		'&gt;': '>',
		'&quot;': '"',
		'&apos;': "'",
		"<br>": "\n",
		"<br />": "\n"
	};
	if (unconvert) {
		conversions = Q.take(conversions, unconvert);
	}
	return this.replaceAll(conversions);
};

/**
 * Interpolates some fields into the string wherever "{{fieldName}}" appears
 * or {{index}} appears.
 * @method interpolate
 * @param {Object|Array} fields Can be an object with field names and values,
 *   or an array corresponding to {{0}}, {{1}}, etc. If the string is missing
 *   {{0}} then {{1}} is mapped to the first element of the array.
 * @return {String}
 */
Sp.interpolate = function _String_prototype_interpolate(fields) {
	if (Q.isArrayLike(fields)) {
		var result = this;
		var b = (this.indexOf('{{0}}') < 0) ? 1 : 0;
		for (var i=0, l=fields.length; i<l; ++i) {
			result = result.replace('{{'+(i+b)+'}}', fields[i]);
		}
		return result;
	}
	return this.replace(/\{\{([^{}]*)\}\}/g, function (a, b) {
		var r = fields[b];
		return (typeof r === 'string' || typeof r === 'number') ? r : a;
	});
};

/**
 * Similar to String.prototype.replace, but replaces globally
 * @method replaceAll
 * @return {String}
 */
Sp.replaceAll = function _String_prototype_replaceAll(pairs) {
	var result = this;
	for (var k in pairs) {
		result = result.replace(new RegExp(k, 'g'), pairs[k]);
	}
	return result;
};

/**
 * Get or set querystring fields from a string, usually from location.search or location.hash
 * @method queryField
 * @param {String|Array|Object} name The name of the field. If it's an array, returns an object of {name: value} pairs. If it's an object, then they are added onto the querystring and the result is returned. If it's a string, it's the name of the field to get. And if it's an empty string, then we get the array of field names with no value, e.g. ?123&456&a=b returns [123,456]
 * @param {String} [value] Optional, provide a value to set in the querystring, or null to delete any fields that match name as a RegExp
 * @return {String|Object} the value of the field in the string, or if value was not undefined, the resulting querystring. Finally, if 
 */
Sp.queryField = function Q_queryField(name, value) {
	var what = this;
	var prefixes = ['#!', '#', '?', '!'];
	var count = prefixes.length;
	var prefix = '';
	var i, k, l, p, keys, parsed, ret, result;
	for (i=0; i<count; ++i) {
		l = prefixes[i].length;
		p = this.substring(0, l);
		if (p == prefixes[i]) {
			prefix = p;
			what = this.substring(l);
			break;
		}
	}
	if (!name) {
		ret = [];
		parsed = Q.parseQueryString(what, keys);
		for (k in parsed) {
			if (parsed[k] == null || parsed[k] === '') {
				ret.push(k);
			}
		}
		return ret;
	} if (Q.isArrayLike(name)) {
		ret = {}, keys = [];
		parsed = Q.parseQueryString(what, keys);
		for (i=0, l=name.length; i<l; ++i) {
			if (name[i] in parsed) {
				ret[name[i]] = parsed[name[i]];
			}
		}
		return ret;
	} else if (Q.isPlainObject(name)) {
		result = what;
		Q.each(name, function (key, value) {
			result = result.queryField(key, value);
		});
	} else if (value === undefined) {
		return Q.parseQueryString(what) [ name ];
	} else if (value === null) {
		keys = [];
		parsed = Q.parseQueryString(what, keys);
		var reg = new RegExp(name);
		for (k in parsed) {
			if (reg.test(k)) {
				delete parsed[k];
			}
		}
		return prefix + Q.queryString(parsed, keys);
	} else {
		keys = [];
		parsed = Q.parseQueryString(what, keys);
		if (!(name in parsed)) {
			keys.push(name);
		}
		parsed[name] = value;
		return prefix + Q.queryString(parsed, keys);
	}
};

/**
 * Obtain some unique hash from a string, analogous to Q_Utils::hashCode
 * @method hashCode
 * @return {number}
 */
Sp.hashCode = function() {
	var hash = 0;
	if (!this.length) return hash;
	for (var i = 0; i < this.length; i++) {
		var c = this.charCodeAt(i);
		hash = hash % 16777216;
		hash = ((hash<<5)-hash)+c;
		hash = hash & 0xffffffff; // Convert to 32bit integer
	}
	return hash;
};

if (!Sp.trim) {
	Sp.trim = function _String_prototype_trim() {
		return this.replace(/^\s+|\s+$/g, "");
	};
}

/**
 * Analogous to PHP's parse_url function
 * @method parseUrl
 * @param {String} component Optional name of component to return
 * @return {Object}
 */
Sp.parseUrl = function _String_prototype_parseUrl (component) {
	// http://kevin.vanzonneveld.net
	// modified by N.I for 'php' parse mode
	var key = ['source', 'scheme', 'authority', 'userInfo', 'user', 'pass', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'],
		parser = /^(?:([^:\/?#]+):)?(?:\/\/()(?:(?:()(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?()(?:(()(?:(?:[^?#\/]*\/)*)()(?:[^?#]*))(?:\?([^#]*))?(?:#(.*))?)/;
	var m = parser.exec(this), uri = {}, i = 14;
	while (i--) {
		if (m[i]) uri[key[i]] = m[i];
	}
	if (component) {
		return uri[component.replace('PHP_URL_', '').toLowerCase()];
	}
	delete uri.source;
	return uri;
};

/**
 * @method sameDomain
 * @param {String} url2 The url to compare against
 * @param {Object} options can include the following:
 * @param {boolean} [options.compareScheme] boolean for whether the url scheme should be compared also
 * @return {boolean}
 * @private
 */
Sp.sameDomain = function _String_prototype_sameDomain (url2, options) {
	var parsed1 = this.parseUrl(),
		parsed2 = url2.parseUrl();
	var same = (parsed1.host === parsed2.host)
		&& (parsed1.user === parsed2.user)
		&& (parsed1.pass === parsed2.pass)
		&& (parsed1.port === parsed2.port);
	return options && options.compareScheme
		? same && (parsed1.scheme === parsed2.scheme)
		: same;
};

/**
 * @method startsWith
 * @param {String} prefix
 * @return {boolean}
 */
Sp.startsWith = function _String_prototype_startsWith(prefix) {
	if (this.length < prefix.length) {
		return false;
	}
	return this.substr(0, prefix.length) === prefix;
};

/**
 * Used to split ids into one or more segments, in order to store millions
 * of files under a directory, without running into limits of various filesystems
 * on the number of files in a directory.
 * Consider using Amazon S3 or another service for uploading files in production.
 * @param {string} id the id to split
 * @param {integer} [lengths=3] the lengths of each segment (the last one can be smaller)
 * @param {string} [delimiter='/'] the delimiter to put between segments
 * @return {string} the segments, delimited by the delimiter
 */
Sp.splitId = function(lengths, delimiter) {
	lengths = lengths || 3;
	delimiter = delimiter || '/';
	var segments = [], pos = 0, len = this.length;
	while (pos < len) {
		segments.push(this.slice(pos, pos += lengths));
	}
	return segments.join(delimiter);
};

Sp.quote = function _String_prototype_quote() {
	var c, i, l = this.length, o = '"';
	for (i = 0; i < l; i += 1) {
		c = this.charAt(i);
		if (c >= ' ') {
			if (c === '\\' || c === '"') {
				o += '\\';
			}
			o += c;
		} else {
			switch (c) {
			case '\b':
				o += '\\b';
				break;
			case '\f':
				o += '\\f';
				break;
			case '\n':
				o += '\\n';
				break;
			case '\r':
				o += '\\r';
				break;
			case '\t':
				o += '\\t';
				break;
			default:
				c = c.charCodeAt();
				o += '\\u00' + Math.floor(c / 16).toString(16) +
					(c % 16).toString(16);
			}
		}
	}
	return o + '"';
};

/**
 * Binds a method to an object, so "this" inside the method
 * refers to that object when it is called.
 * @method bind
 * @param {Function} method A reference to the function to call
 * @param {Object} obj The object to bind as the context for the function call
 * @param {Object} options If supplied, binds these options and pushes them as the last argument to the function call.
 */
if (!Function.prototype.bind)
Function.prototype.bind = function _Function_prototype_bind(obj, options) {
	var method = this;
	if (!obj) obj = root;
	if (!options) {
		return function _Q_bind_result() {
			return method.apply(obj, arguments);
		};
	}
	return function _Q_bind_result_withOptions() {
		var args = Array.prototype.slice.call(arguments);
		if (options) args.push(options);
		return method.apply(obj, args);
	};
};

// Backward compatibility with older versions of Node.js
fs.exists = fs.exists || function(uri, callback){return path.exists.call(path, uri, callback);};
fs.existsSync = fs.existsSync || function(uri){return path.existsSync.call(path, uri);};
if (!Buffer.from) {
	Buffer.from = function (x, y, z) {
	    if (typeof x === 'number') {
			throw new TypeError('Buffer.from: first argument must not be a number');
	    }
		return new Buffer(x, y, z);
	};
}

if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function _Array_prototype_indexOf(searchElement /*, fromIndex */ ) {
		if (this === 0 || this === null) {
			throw new TypeError();
		}
		var t = Object(this);
		var len = t.length >>> 0;
		if (len === 0) {
			return -1;
		}
		var n = 0;
		if (arguments.length > 0) {
			n = Number(arguments[1]);
			if (n !== n) { // shortcut for verifying if it's NaN
				n = 0;
			} else if (n !== 0 && n !== root.Infinity && n !== -root.Infinity) {
				n = (n > 0 || -1) * Math.floor(Math.abs(n));
			}
		}
		if (n >= len) {
			return -1;
		}
		var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
		for (; k < len; k++) {
			if (k in t && t[k] === searchElement) {
				return k;
			}
		}
		return -1;
	};
}

Q.globalNames = Object.keys(root); // to find stray globals

/**
 * This function is useful to make sure your code is not polluting the global namespace
 * @method globalNamesAdded
 * @static
 */
Q.globalNamesAdded = function () {
	return Q.diff(Object.keys(root), Q.globalNames);
};