<commit_before>/**
 * straps.js - Class inheritance library with support for bean-style accessors
 *
 * Copyright (c) 2006 - 2013 Juerg Lehni
 * http://lehni.org/
 *
 * Distributed under the MIT license.
 *
 * straps.js was created by extracting and simplifying the inheritance framework
 * from boostrap.js, a JavaScript DOM library, also created by Juerg Lehni:
 * https://github.com/lehni/bootstrap.js
 *
 * Inspirations:
 * http://dean.edwards.name/weblog/2006/03/base/
 * http://dev.helma.org/Wiki/JavaScript+Inheritance+Sugar/
 */

var Base = new function() {
	var hidden = /^(statics|preserve|enumerable|prototype|toString|valueOf)$/,

		forEach = [].forEach || function(iter, bind) {
			for (var i = 0, l = this.length; i < l; i++)
				iter.call(bind, this[i], i, this);
		},

		forIn = function(iter, bind) {
			// Do not use Object.keys for iteration as iterators might modify
			// the object we're iterating over, making the hasOwnProperty still
			// necessary.
			for (var i in this)
				if (this.hasOwnProperty(i))
					iter.call(bind, this[i], i, this);
		},

		// A short-cut to a simplified version of Object.create that only
		// supports the first parameter (in the emulation):
		create = Object.create || function(proto) {
			// From all browsers that do not offer Object.create(), we only
			// support Firefox 3.5 & 3.6, and this hack works there:
			return { __proto__: proto };
		},

		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
			// Emulate Object.getOwnPropertyDescriptor for outdated browsers
			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
			return get
					? { get: get, set: obj.__lookupSetter__(name),
						enumerable: true, configurable: true }
					: obj.hasOwnProperty(name)
						? { value: obj[name], enumerable: true,
							configurable: true, writable: true }
						: null;
		},

		_define = Object.defineProperty || function(obj, name, desc) {
			// Emulate Object.defineProperty for outdated browsers
			if ((desc.get || desc.set) && obj.__defineGetter__) {
				if (desc.get)
					obj.__defineGetter__(name, desc.get);
				if (desc.set)
					obj.__defineSetter__(name, desc.set);
			} else {
				obj[name] = desc.value;
			}
			return obj;
		},

		define = function(obj, name, desc) {
			// Both Safari and Chrome at one point ignored configurable = true
			// and did not allow overriding of existing properties:
			// https://code.google.com/p/chromium/issues/detail?id=72736
			// https://bugs.webkit.org/show_bug.cgi?id=54289
			// The workaround is to delete the property first.
			// TODO: Remove this fix in July 2014, and use _define directly.
			delete obj[name];
			return _define(obj, name, desc);
		};

	/**
	 * Private function that injects functions from src into dest, overriding
	 * (and inherinting from) base.
	 */
	function inject(dest, src, enumerable, base, preserve) {
		var beans;

		/**
		 * Private function that injects one field with given name and checks if
		 * the field is a function that needs to be wrapped for calls of base().
		 * This is only needed if the function in base is different from the one
		 * in src, and if the one in src is actually calling base through base.
		 * The string of the function is parsed for this.base to detect calls.
		 */
		function field(name, val, dontCheck) {
			// This does even work for prop: 0, as it will just be looked up
			// again through describe.
			var val = val || (val = describe(src, name))
					&& (val.get ? val : val.value);
			// Allow aliases to properties with different names, by having
			// string values starting with '#'
			if (typeof val === 'string' && val[0] === '#')
				val = dest[val.substring(1)] || val;
			var isFunc = typeof val === 'function',
				res = val,
				// Only lookup previous value if we preserve or define a
				// function that might need it for this.base(). If we're
				// defining a getter, don't lookup previous value, but look if
				// the property exists (name in dest) and store result in prev
				prev = preserve || isFunc
					? (val && val.get ? name in dest : dest[name]) : null,
				bean;
			if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
					&& (!preserve || !prev)) {
				// Expose the 'super' function (meaning the one this function is
				// overriding) through #base:
				if (isFunc && prev)
					val.base = prev;
				// Produce bean properties if getters or setters are specified.
				// Just collect beans for now, and look them up in dest at the
				// end of fields injection. This ensures base works for beans
				// too, and inherits setters for redefined getters in
				// subclasses. Only add getter beans if they do not expect
				// arguments.
				// Functions that should function both with optional arguments
				// and as beans should not declare the parameters and use the
				// arguments array internally instead.
				if (isFunc && beans
						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/))
						&& val.length === (bean[1] === 'set' ? 1 : 0))
					beans[bean[3].toLowerCase() + bean[4]] = bean[2];
				// No need to create accessor description if it is one already.
				// It is considered a description if it is an object with a get
				// function that has zero parameters.
				if (!res || isFunc || !res.get || typeof res.get !== 'function'
						|| res.get.length !== 0)
					res = { value: res, writable: true };
				// Only set/change configurable and enumerable if this field is
				// configurable
				if ((describe(dest, name)
						|| { configurable: true }).configurable) {
					res.configurable = true;
					res.enumerable = enumerable;
				}
				define(dest, name, res);
			}
		}
		// Iterate through all definitions in src now and call field() for each.
		if (src) {
			beans = {};
			for (var name in src)
				if (src.hasOwnProperty(name) && !hidden.test(name))
					field(name, null, true);
			// IE (and some other browsers?) never enumerate these, even  if
			// they are simply set on an object. Force their creation. Ccheck
			// them for not being defined (by passing undefined for dontCheck).
			field('toString');
			field('valueOf');
			// Now finally define beans as well. Look up methods on dest, for
			// support of this.base() (See above).
			for (var name in beans) {
				var part = beans[name],
					set = dest['set' + part],
					// Convention: 'isName' is only considered a bean if there
					// is also a setter for it!
					get = dest['get' + part] || set && dest['is' + part];
				if (get)
					field(name, { get: get, set: set }, true);
			}
		}
		return dest;
	}

	function each(obj, iter, bind) {
		// To support both array-like structures and plain objects, use a simple
		// hack to filter out Base objects with #length getters in the
		// #length-base array-like check for now, by assuming these getters are
		// produced by #getLength beans.
		// NOTE: The correct check would call describe(obj, 'length') and look
		// for typeof res.value === 'number', but it's twice as slow on Chrome
		// and Firefox (WebKit does well), so this should do for now.
		if (obj)
			('length' in obj && !obj.getLength
					&& typeof obj.length === 'number'
				? forEach
				: forIn).call(obj, iter, bind = bind || obj);
		return bind;
	}

	function copy(dest, source) {
		for (var i in source)
			if (source.hasOwnProperty(i))
				dest[i] = source[i];
		return dest;
	}

	// Inject into new ctor object that's passed to inject(), and then returned
	// as the Base class.
	return inject(function Base() {
		// Define a constructor that merges in all the fields of the passed
		// objects using copy()
		for (var i = 0, l = arguments.length; i < l; i++)
			copy(this, arguments[i]);
	}, {
		inject: function(src/*, ... */) {
			if (src) {
				var proto = this.prototype,
					base = Object.getPrototypeOf(proto).constructor,
					// Allow the whole scope to just define statics by defining
					// statics: true.
					statics = src.statics === true ? src : src.statics;
				if (statics != src)
					inject(proto, src, src.enumerable, base && base.prototype,
							src.preserve);
				// Define new static fields as enumerable, and inherit from
				// base. enumerable is necessary so they can be copied over from
				// base, and it does not harm to have enumerable properties in
				// the constructor. Use the preserve setting in src.preserve for
				// statics too, not their own.
				inject(this, statics, true, base, src.preserve);
			}
			// If there are more than one argument, loop through them and call
			// inject again. Do not simple inline the above code in one loop,
			// since each of the passed objects might override this.inject.
			for (var i = 1, l = arguments.length; i < l; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(/* src, ... */) {
			var base = this,
				ctor;
			// Look for an initialize function in all injection objects and use
			// it directly as the actual constructor.
			for (var i = 0, l = arguments.length; i < l; i++)
				if (ctor = arguments[i].initialize)
					break;
			// If no initialize function is provided, create a constructor that
			// simply calls the base constructor.
			ctor = ctor || function() {
				base.apply(this, arguments);
			};
			ctor.prototype = create(this.prototype);
			// Expose base property on constructor functions as well.
			ctor.base = base;
			// The new prototype extends the constructor on which extend is
			// called. Fix constructor.
			define(ctor.prototype, 'constructor',
					{ value: ctor, writable: true, configurable: true });
			// Copy over static fields, as prototype-like inheritance
			// is not possible for static fields. Mark them as enumerable
			// so they can be copied over again.
			inject(ctor, this, true);
			// Inject all the definitions in src. Use the new inject instead of
			// the one in ctor, in case it was overriden. this is needed when
			// overriding the static .inject(). But only inject if there's
			// something to actually inject.
			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
		}
		// Pass true for enumerable, so inject() and extend() can be passed on
		// to subclasses of Base through Base.inject() / extend().
	}, true).inject({
		/**
		 * Injects the fields from the given object, adding this.base()
		 * functionality
		 */
		inject: function(/* src, ... */) {
			for (var i = 0, l = arguments.length; i < l; i++)
				inject(this, arguments[i], arguments[i].enumerable);
			return this;
		},

		/**
		 * Returns a new object that inherits all properties from "this",
		 * through proper JS inheritance, not copying.
		 * Optionally, src and hide parameters can be passed to fill in the
		 * newly created object just like in inject(), to copy the behavior
		 * of Function.prototype.extend.
		 */
		extend: function(/* src, ... */) {
			var res = create(this);
			return res.inject.apply(res, arguments);
		},

		each: function(iter, bind) {
			return each(this, iter, bind);
		},

		/**
		 * General purpose clone function that delegates cloning to the
		 * constructor that receives the object to be cloned as the first
		 * argument.
		 * NOTE: #clone() needs to be overridden in any class that requires
		 * other cloning behavior.
		 */
		clone: function() {
			return new this.constructor(this);
		},

		statics: {
			// Expose some local privates as static functions on Base.
			each: each,
			create: create,
			define: define,
			describe: describe,
			copy: copy,

			clone: function(obj) {
				return copy(new obj.constructor(), obj);
			},

			/**
			 * Returns true if obj is a plain JavaScript object literal, or a
			 * plain Base object, as produced by Base.merge().
			 */
			isPlainObject: function(obj) {
				var ctor = obj != null && obj.constructor;
				// We also need to check for ctor.name === 'Object', in case
				// this is an object from another global scope (e.g. an iframe,
				// or another vm context in node.js).
				return ctor && (ctor === Object || ctor === Base
						|| ctor.name === 'Object');
			},

			/**
			 * Returns the first argument that is defined. null is counted as
			 * defined too, as !== undefined is used for comparisons.
			 */
			pick: function() {
				for (var i = 0, l = arguments.length; i < l; i++)
					if (arguments[i] !== undefined)
						return arguments[i];
				return null;
			}
		}
	});
};

// Export Base class for node
if (typeof module !== 'undefined')
	module.exports = Base;
<commit_msg>Improve convention for creation of beans:<commit_after>/**
 * straps.js - Class inheritance library with support for bean-style accessors
 *
 * Copyright (c) 2006 - 2013 Juerg Lehni
 * http://lehni.org/
 *
 * Distributed under the MIT license.
 *
 * straps.js was created by extracting and simplifying the inheritance framework
 * from boostrap.js, a JavaScript DOM library, also created by Juerg Lehni:
 * https://github.com/lehni/bootstrap.js
 *
 * Inspirations:
 * http://dean.edwards.name/weblog/2006/03/base/
 * http://dev.helma.org/Wiki/JavaScript+Inheritance+Sugar/
 */

var Base = new function() {
	var hidden = /^(statics|preserve|enumerable|prototype|toString|valueOf)$/,

		forEach = [].forEach || function(iter, bind) {
			for (var i = 0, l = this.length; i < l; i++)
				iter.call(bind, this[i], i, this);
		},

		forIn = function(iter, bind) {
			// Do not use Object.keys for iteration as iterators might modify
			// the object we're iterating over, making the hasOwnProperty still
			// necessary.
			for (var i in this)
				if (this.hasOwnProperty(i))
					iter.call(bind, this[i], i, this);
		},

		// A short-cut to a simplified version of Object.create that only
		// supports the first parameter (in the emulation):
		create = Object.create || function(proto) {
			// From all browsers that do not offer Object.create(), we only
			// support Firefox 3.5 & 3.6, and this hack works there:
			return { __proto__: proto };
		},

		describe = Object.getOwnPropertyDescriptor || function(obj, name) {
			// Emulate Object.getOwnPropertyDescriptor for outdated browsers
			var get = obj.__lookupGetter__ && obj.__lookupGetter__(name);
			return get
					? { get: get, set: obj.__lookupSetter__(name),
						enumerable: true, configurable: true }
					: obj.hasOwnProperty(name)
						? { value: obj[name], enumerable: true,
							configurable: true, writable: true }
						: null;
		},

		_define = Object.defineProperty || function(obj, name, desc) {
			// Emulate Object.defineProperty for outdated browsers
			if ((desc.get || desc.set) && obj.__defineGetter__) {
				if (desc.get)
					obj.__defineGetter__(name, desc.get);
				if (desc.set)
					obj.__defineSetter__(name, desc.set);
			} else {
				obj[name] = desc.value;
			}
			return obj;
		},

		define = function(obj, name, desc) {
			// Both Safari and Chrome at one point ignored configurable = true
			// and did not allow overriding of existing properties:
			// https://code.google.com/p/chromium/issues/detail?id=72736
			// https://bugs.webkit.org/show_bug.cgi?id=54289
			// The workaround is to delete the property first.
			// TODO: Remove this fix in July 2014, and use _define directly.
			delete obj[name];
			return _define(obj, name, desc);
		};

	/**
	 * Private function that injects functions from src into dest, overriding
	 * (and inherinting from) base.
	 */
	function inject(dest, src, enumerable, base, preserve) {
		var beans;

		/**
		 * Private function that injects one field with given name and checks if
		 * the field is a function that needs to be wrapped for calls of base().
		 * This is only needed if the function in base is different from the one
		 * in src, and if the one in src is actually calling base through base.
		 * The string of the function is parsed for this.base to detect calls.
		 */
		function field(name, val, dontCheck) {
			// This does even work for prop: 0, as it will just be looked up
			// again through describe.
			var val = val || (val = describe(src, name))
					&& (val.get ? val : val.value);
			// Allow aliases to properties with different names, by having
			// string values starting with '#'
			if (typeof val === 'string' && val[0] === '#')
				val = dest[val.substring(1)] || val;
			var isFunc = typeof val === 'function',
				res = val,
				// Only lookup previous value if we preserve or define a
				// function that might need it for this.base(). If we're
				// defining a getter, don't lookup previous value, but look if
				// the property exists (name in dest) and store result in prev
				prev = preserve || isFunc
					? (val && val.get ? name in dest : dest[name]) : null,
				bean;
			if ((dontCheck || val !== undefined && src.hasOwnProperty(name))
					&& (!preserve || !prev)) {
				// Expose the 'super' function (meaning the one this function is
				// overriding) through #base:
				if (isFunc && prev)
					val.base = prev;
				// Produce bean properties if getters or setters are specified.
				// Just collect potential beans for now, and look them up in
				// dest at the end of fields injection. This ensures base works
				// for beans too, and inherits setters for redefined getters in
				// subclasses.
				if (isFunc && beans
						&& (bean = name.match(/^([gs]et|is)(([A-Z])(.*))$/)))
					beans[bean[3].toLowerCase() + bean[4]] = bean[2];
				// No need to create accessor description if it is one already.
				// It is considered a description if it is an object with a get
				// function that has zero parameters.
				if (!res || isFunc || !res.get || typeof res.get !== 'function'
						|| res.get.length !== 0)
					res = { value: res, writable: true };
				// Only set/change configurable and enumerable if this field is
				// configurable
				if ((describe(dest, name)
						|| { configurable: true }).configurable) {
					res.configurable = true;
					res.enumerable = enumerable;
				}
				define(dest, name, res);
			}
		}
		// Iterate through all definitions in src now and call field() for each.
		if (src) {
			beans = {};
			for (var name in src)
				if (src.hasOwnProperty(name) && !hidden.test(name))
					field(name, null, true);
			// IE (and some other browsers?) never enumerate these, even  if
			// they are simply set on an object. Force their creation. Ccheck
			// them for not being defined (by passing undefined for dontCheck).
			field('toString');
			field('valueOf');
			// Now finally define beans as well. Look up methods on dest, for
			// support of this.base() (See above).
			for (var name in beans) {
				var part = beans[name],
					set = dest['set' + part],
					// Convention: 'isName' is only considered a bean if there
					// is also a setter for it!
					get = dest['get' + part] || set && dest['is' + part];
				// Convention: Assume that if a potential getter has no
				// arguments and there is no setter, it is a read-only bean.
				// If there is both a getter and a setter, do not look at
				// arguments count at all.
				if (get && (get.length === 0 || set))
					field(name, { get: get, set: set }, true);
			}
		}
		return dest;
	}

	function each(obj, iter, bind) {
		// To support both array-like structures and plain objects, use a simple
		// hack to filter out Base objects with #length getters in the
		// #length-base array-like check for now, by assuming these getters are
		// produced by #getLength beans.
		// NOTE: The correct check would call describe(obj, 'length') and look
		// for typeof res.value === 'number', but it's twice as slow on Chrome
		// and Firefox (WebKit does well), so this should do for now.
		if (obj)
			('length' in obj && !obj.getLength
					&& typeof obj.length === 'number'
				? forEach
				: forIn).call(obj, iter, bind = bind || obj);
		return bind;
	}

	function copy(dest, source) {
		for (var i in source)
			if (source.hasOwnProperty(i))
				dest[i] = source[i];
		return dest;
	}

	// Inject into new ctor object that's passed to inject(), and then returned
	// as the Base class.
	return inject(function Base() {
		// Define a constructor that merges in all the fields of the passed
		// objects using copy()
		for (var i = 0, l = arguments.length; i < l; i++)
			copy(this, arguments[i]);
	}, {
		inject: function(src/*, ... */) {
			if (src) {
				var proto = this.prototype,
					base = Object.getPrototypeOf(proto).constructor,
					// Allow the whole scope to just define statics by defining
					// statics: true.
					statics = src.statics === true ? src : src.statics;
				if (statics != src)
					inject(proto, src, src.enumerable, base && base.prototype,
							src.preserve);
				// Define new static fields as enumerable, and inherit from
				// base. enumerable is necessary so they can be copied over from
				// base, and it does not harm to have enumerable properties in
				// the constructor. Use the preserve setting in src.preserve for
				// statics too, not their own.
				inject(this, statics, true, base, src.preserve);
			}
			// If there are more than one argument, loop through them and call
			// inject again. Do not simple inline the above code in one loop,
			// since each of the passed objects might override this.inject.
			for (var i = 1, l = arguments.length; i < l; i++)
				this.inject(arguments[i]);
			return this;
		},

		extend: function(/* src, ... */) {
			var base = this,
				ctor;
			// Look for an initialize function in all injection objects and use
			// it directly as the actual constructor.
			for (var i = 0, l = arguments.length; i < l; i++)
				if (ctor = arguments[i].initialize)
					break;
			// If no initialize function is provided, create a constructor that
			// simply calls the base constructor.
			ctor = ctor || function() {
				base.apply(this, arguments);
			};
			ctor.prototype = create(this.prototype);
			// Expose base property on constructor functions as well.
			ctor.base = base;
			// The new prototype extends the constructor on which extend is
			// called. Fix constructor.
			define(ctor.prototype, 'constructor',
					{ value: ctor, writable: true, configurable: true });
			// Copy over static fields, as prototype-like inheritance
			// is not possible for static fields. Mark them as enumerable
			// so they can be copied over again.
			inject(ctor, this, true);
			// Inject all the definitions in src. Use the new inject instead of
			// the one in ctor, in case it was overriden. this is needed when
			// overriding the static .inject(). But only inject if there's
			// something to actually inject.
			return arguments.length ? this.inject.apply(ctor, arguments) : ctor;
		}
		// Pass true for enumerable, so inject() and extend() can be passed on
		// to subclasses of Base through Base.inject() / extend().
	}, true).inject({
		/**
		 * Injects the fields from the given object, adding this.base()
		 * functionality
		 */
		inject: function(/* src, ... */) {
			for (var i = 0, l = arguments.length; i < l; i++)
				inject(this, arguments[i], arguments[i].enumerable);
			return this;
		},

		/**
		 * Returns a new object that inherits all properties from "this",
		 * through proper JS inheritance, not copying.
		 * Optionally, src and hide parameters can be passed to fill in the
		 * newly created object just like in inject(), to copy the behavior
		 * of Function.prototype.extend.
		 */
		extend: function(/* src, ... */) {
			var res = create(this);
			return res.inject.apply(res, arguments);
		},

		each: function(iter, bind) {
			return each(this, iter, bind);
		},

		/**
		 * General purpose clone function that delegates cloning to the
		 * constructor that receives the object to be cloned as the first
		 * argument.
		 * NOTE: #clone() needs to be overridden in any class that requires
		 * other cloning behavior.
		 */
		clone: function() {
			return new this.constructor(this);
		},

		statics: {
			// Expose some local privates as static functions on Base.
			each: each,
			create: create,
			define: define,
			describe: describe,
			copy: copy,

			clone: function(obj) {
				return copy(new obj.constructor(), obj);
			},

			/**
			 * Returns true if obj is a plain JavaScript object literal, or a
			 * plain Base object, as produced by Base.merge().
			 */
			isPlainObject: function(obj) {
				var ctor = obj != null && obj.constructor;
				// We also need to check for ctor.name === 'Object', in case
				// this is an object from another global scope (e.g. an iframe,
				// or another vm context in node.js).
				return ctor && (ctor === Object || ctor === Base
						|| ctor.name === 'Object');
			},

			/**
			 * Returns the first argument that is defined. null is counted as
			 * defined too, as !== undefined is used for comparisons.
			 */
			pick: function() {
				for (var i = 0, l = arguments.length; i < l; i++)
					if (arguments[i] !== undefined)
						return arguments[i];
				return null;
			}
		}
	});
};

// Export Base class for node
if (typeof module !== 'undefined')
	module.exports = Base;
<|endoftext|>


<commit_before>function fail(errMessage) {
    throw new Error(errMessage);
}

/**
*   Creates the Stream object
*   @constructor
*   @name Stream
*   @param {*} first - First element of the stream
*   @param {Function} restGenerator - function to generate remaining parts of the stream
*   @returns {Stream} Returns the new Stream object instance
**/
function Stream(first, restGenerator) {
    this.streamFirst = first;
    this.streamRest = restGenerator || function () {
        return new Stream(null, null);
    };
}

/**
*   Checks if a stream is empty
*
*   @returns {boolean} Returns `true` if the stream is empty
*   @example
*
*   s.isEmpty();
*   // => true
**/
function isEmpty(stream) {
    return this.streamFirst == null;
}

/**
*   Maps a function to all the elements of a stream
*
*   @static
*   @param {Stream} stream - Stream to map function to
*   @param {Function} fn - Function to apply to stream elements
*   @returns {Stream} Returns a new Stream with the input function applied to entries
*   @example
*
*   Stream.map(integerStream, function (n) { return n*2; });
*   // => true
**/
function map(stream, fn) {
    if (Stream.isEmpty(stream) || fn == null)
        fail('Cannot map over empty stream or falsy function');

    return new Stream(
        fn(stream.streamFirst),
        function () {
            return Stream.map(stream.streamRest(), fn);
        }
    );
}

/**
*   Adds two streams
*
*   @static
*   @param {Stream} s1 - Augend stream
*   @param {Stream} s2 - Addend stream
*   @returns {Stream} Returns a new Stream that is the sum of the Augend and Addend streams
*   @example
*
*   Stream.add(s1 s2);
**/
function add(s1, s2) {
    if (Stream.isEmpty(s1) || Stream.isEmpty(s2))
        fail('Cannot add empty streams');

    return new Stream(
        s1.streamFirst + s2.streamFirst,
        function () {
            return s1.streamRest() + s2.streamRest();
        }
    );
}

/**
*   Returns an infinite stream of ones
*
*   @static
*   @returns {Stream} Value at nth index in stream
*   @example
*
*   var ones = Stream.Ones();
**/
function Ones() {
    return new Stream(1, Ones);
};

/**
*   Filters unwanted elements out of a stream
*
*   @param {Function} fn - Filter function
*   @returns {Stream} Filtered stream
*   @example
*
*   var evenNumbers = Stream.filter(function(val) {
*       return val % 2 === 0;
*   });
**/
function filter(fn) {
    if(this.isEmpty()) {
        return this;
    }
    var first = this.head();
    var rest = this.tail();
    if (fn(first)){
        return new Stream(
            first,
            function () {
                return rest.filter(fn);
            });
    }
    return rest.filter(fn);
};

/**
*   Zips all input streams together. Works like Python's
*   zip but returns arrays of arrays instead of a list of tuples
*   The nth entry of a zipped stream is an an array consisting of
*   all the nth elements of its constituent streams.
*
*   @static
*   @param {...Stream} [streams] - streams to be zipped (picked off arguments object)
*   @returns {Stream} A new stream containing the zipped elements
*   @example
*
*   var zipped = Stream.Zip(s1, s2, s3);
**/
function zip() {
    var args = [].slice(arguments);

    var zippedFirsts = [];
    for(var i=0, len = args.length; i < len; i++){
        var s = args[i];
        zippedFirsts.push(s.streamFirst());
        s = s.streamRest(); //overwrite the stream so it stores only its rest
        //check for mutation bugs.
    }

    return new Stream(
        zippedFirsts,
        function () {
            return Stream.zip(args);
        }
    );
}

/**
*   Creates an input stream using a list of arguments
*
*   @static
*   @param {...*} [values] - values to be used in the stream
*   @returns {Stream} A new stream containing the elements
*   @example
*
*   var s = Stream.create(1,2,3,4);
**/
function create( /* arguments */) {
    if(arguments.length === 0) {
        return new Stream(null, null);
    }

    var tailArgs = [].slice.call(arguments,1);
    return new Stream(
        arguments[0],
        function () {
            return Stream.create.apply(null, tailArgs);
        }
    );
}

/**
*   Creates an input stream from an array
*
*   @static
*   @param {Array} [values] - array to use in creating the stream
*   @returns {Stream} A new stream containing the array elements
*   @example
*
*   var s = Stream.fromArray([1,2,3,4]);
**/
function fromArray(values) {
    return Stream.create(null, values);
}

/**
*   Walks a stream and applies the input function to the stream.
*   Stream has to be finite for this to work.
*
*   @param {Function} fn - The function to apply to every element of the stream
*   @example
*
*   s.walk(function(element) {
*        alert(element);
*   });
**/
function walk(fn) {
    var s = this;
    while (!s.isEmpty()){
        fn(s.head());
        s = s.tail();
    }
}

/**
*   Prints out the first n elements of a stream, will stop if stream length is less
*   than n
*
*   @param {Number} n - Number of elements to print
*   @example
*
*   s.print(5);
**/
function print(n) {
    var streamToPrint = this.pick(n);

    streamToPrint.walk(function (element){
        console.log(element);
    });
}

/**
*   Reduces the stream to a value which is the accumulated output obtained over
*   each stream element. Only valid for finite streams
*
*   @param {Function} fn - Function to execute on each argument,
*   taking the accumlatedValue so far and the current stream value
*   @param {*} initialValue - The initialValue to seed the accumulator with
*   @returns {*} The accumulated output
*   @example
*
*   var sum = stream.reduce(function (sum, n) {
*       return sum + n;
*   });
**/
function reduce(fn, initialValue) {
    var s = this;
    if(Stream.isEmpty(s)) {
        return initialValue;
    }

    if(initialValue == null){
        initialValue = s.streamFirst();
        s = s.streamRest();
    }

    return this.streamRest().reduce(fn, fn(initialValue, s.streamFirst()));
}

/**
*   Picks the first n elements out of a stream, terminates when it gets to the nth item or reaches the end of the stream
*
*   @param {Number} n - The number of elements to be picked
*   @returns {Stream} Returns a stream containing the picked items
*   @example
*
*   integerStream.pick(3);
**/
function pick(n) {
    if (!n || Stream.isEmpty(this)) {
        return new Stream(null, null);
    }

    var that = this;
    return new Stream(
        this.head(),
        function () {
            return that.tail().pick(n - 1);
        }
    );
}

/**
*   Picks the element at the nth index in a stream. Returns undefined
*   if stream size is less than the index
*
*   @param {Number} index - The index of the stream element to be picked
*   @returns {*} Value at nth index in stream
*   @example
*
*   integerStream.valueAt(3);
*   // => 3
**/
function valueAt(index) {
    var items = this.pick(index);

    return items[items.length - 1];
}

/**
*   Gets the length of a stream - only defined for finite streams
*
*   @returns {Number} length - The length of the stream
*   @example
*
*   stream.length()
*   // => 31
**/
function length() {
    var len = 0;
    var s = this;

    while(!Stream.isEmpty(s)) {
        len++;
        s = s.streamRest();
    }

    return len;
}

/**
*   Calculates the sum of the elements of a stream
*
*   @returns {Number} The sum of the elements of the stream
*   @example
*
*   stream.sum()
*   // => 31
**/
function sum() {
    return this.reduce(function (previousValue, currentValue){
        return previousValue + currentValue;
    }, 0);
}

/**
*   Check if the stream contains an element. Only defined for a finite stream
*
*   @param {*} index - The index of the stream element to be picked
*   @returns {Boolean} Returns `true` if the stream contains the element
*   @example
*
*   ones.contains(1)
*   // => true
**/
function contains(element) {
    var s = this;
    while(!Stream.isEmpty(s)) {
        if(s.streamFirst() === element){
            return true;
        }
        s = s.streamRest();
    }

    return false;
}

/**
*   Removes the first n elements of a stream.
*
*   @param {Number} n - The number of elements to be removed
*   @returns {Stream} The new stream with elements starting an
*   index n + 1 or an empty stream if n > stream.length
*   @example
*
*   integerStream.remove(3);
*   // => [4,5,6]
**/
function remove(n) {
    var s = this;
    while(n > 0) {
        if (Stream.isEmpty(s)) {
            return new Stream(null, null);
        }
        s = s.streamRest();
    }

    return new Stream(
        s.streamFirst(),
        function () {
            return s.streamRest()
        }
    );
}

/**
*   Returns the 'head' - first value - of a stream
*
*   @returns {*} The head of the stream
**/
function head() {
    if(this.isEmpty()){
        fail('Stream is empty!');
    }
    return this.streamFirst;
}

/**
 *  Constructs a stream made up of consecutive numbers in the
 *  range [low high]
 *
 *  @static
 *  @param {*} [low=0] - The lower limit value of the stream
 *  @param {*} high - The upper limit value of the stream
 *
 *  returns {Stream} A finite stream with elements in the range [low, high]
 */
function fromInterval(low, high){
    if(!low){
        low = 0;
    }
    if (!high || low === high){
        return new Stream(low, null);
    }

    return new Stream(
        low,
        Stream.fromInterval(low + 1, high)
    );
}

/**
 *  Constructs an infinite stream of consecutive numbers starting
 *  from integer `start`
 *
 *  @static
 *  @param {Number} [start=0] - The integer to start the stream from
 *
 *  returns {Stream} A infinite stream with elements starting from `start`
 */
function from(start) {
    if(!start){
        start = 0;
    }

    return new Stream(
        start,
        Stream.from(start + 1)
    );
}

/**
 *  Constructs a stream made up of consecutive numbers up to `stop`
 *
 *  @static
 *  @param {Number} [stop] - The maximum and last value of the stream
 *
 *  returns {Stream} A finite stream with elements in the range [0, stop]
 */
function upTo(stop){
    return Stream.fromInterval(0, stop);
}

/**
 *  Constructs an array containing the elements of a finite stream
 *   *
 *  returns {Array} A array containing the elements of the stream
 */
function toArray(){
    var items = [];

    var storer = function (val) {
        items.push(val);
    }

    this.walk(storer);
    return items;
}

/**
*   Returns the tail of a stream
*
*   @returns {Stream} The tail of the stream
**/
function tail() {
    if(this.isEmpty()){
        fail('Stream is empty!');
    }

    return this.streamRest();
}

//Static methods
Stream.map = map;
Stream.Ones = Ones;
Stream.add = add;
Stream.zip = zip;
Stream.create = create;
Stream.fromArray = fromArray;
Stream.fromInterval = fromInterval;
Stream.from = from;
Stream.upTo = upTo;

//Instance methods
Stream.prototype.head = head;
Stream.prototype.tail = tail;
Stream.prototype.isEmpty = isEmpty;
Stream.prototype.pick = pick;
Stream.prototype.valueAt = valueAt;
Stream.prototype.length = length;
Stream.prototype.reduce = reduce;
Stream.prototype.sum = sum;
Stream.prototype.filter = filter;
Stream.prototype.contains = contains;
Stream.prototype.print = print;
Stream.prototype.remove = remove;
Stream.prototype.toArray = toArray;<commit_msg>Implemented the append method<commit_after>function fail(errMessage) {
    throw new Error(errMessage);
}

/**
*   Creates the Stream object
*   @constructor
*   @name Stream
*   @param {*} first - First element of the stream
*   @param {Function} restGenerator - function to generate remaining parts of the stream
*   @returns {Stream} Returns the new Stream object instance
**/
function Stream(first, restGenerator) {
    this.streamFirst = first;
    this.streamRest = restGenerator || function () {
        return new Stream(null, null);
    };
}

/**
*   Checks if a stream is empty
*
*   @returns {boolean} Returns `true` if the stream is empty
*   @example
*
*   s.isEmpty();
*   // => true
**/
function isEmpty(stream) {
    return this.streamFirst == null;
}

/**
*   Maps a function to all the elements of a stream
*
*   @static
*   @param {Stream} stream - Stream to map function to
*   @param {Function} fn - Function to apply to stream elements
*   @returns {Stream} Returns a new Stream with the input function applied to entries
*   @example
*
*   Stream.map(integerStream, function (n) { return n*2; });
*   // => true
**/
function map(stream, fn) {
    if (Stream.isEmpty(stream) || fn == null)
        fail('Cannot map over empty stream or falsy function');

    return new Stream(
        fn(stream.streamFirst),
        function () {
            return Stream.map(stream.streamRest(), fn);
        }
    );
}

/**
*   Adds two streams
*
*   @static
*   @param {Stream} s1 - Augend stream
*   @param {Stream} s2 - Addend stream
*   @returns {Stream} Returns a new Stream that is the sum of the Augend and Addend streams
*   @example
*
*   Stream.add(s1 s2);
**/
function add(s1, s2) {
    if (Stream.isEmpty(s1) || Stream.isEmpty(s2))
        fail('Cannot add empty streams');

    return new Stream(
        s1.streamFirst + s2.streamFirst,
        function () {
            return s1.streamRest() + s2.streamRest();
        }
    );
}

/**
*   Returns an infinite stream of ones
*
*   @static
*   @returns {Stream} Value at nth index in stream
*   @example
*
*   var ones = Stream.Ones();
**/
function Ones() {
    return new Stream(1, Ones);
};

/**
*   Filters unwanted elements out of a stream
*
*   @param {Function} fn - Filter function
*   @returns {Stream} Filtered stream
*   @example
*
*   var evenNumbers = Stream.filter(function(val) {
*       return val % 2 === 0;
*   });
**/
function filter(fn) {
    if(this.isEmpty()) {
        return this;
    }
    var first = this.head();
    var rest = this.tail();
    if (fn(first)){
        return new Stream(
            first,
            function () {
                return rest.filter(fn);
            });
    }
    return rest.filter(fn);
};

/**
*   Zips all input streams together. Works like Python's
*   zip but returns arrays of arrays instead of a list of tuples
*   The nth entry of a zipped stream is an an array consisting of
*   all the nth elements of its constituent streams.
*
*   @static
*   @param {...Stream} [streams] - streams to be zipped (picked off arguments object)
*   @returns {Stream} A new stream containing the zipped elements
*   @example
*
*   var zipped = Stream.Zip(s1, s2, s3);
**/
function zip() {
    var args = [].slice(arguments);

    var zippedFirsts = [];
    for(var i=0, len = args.length; i < len; i++){
        var s = args[i];
        zippedFirsts.push(s.streamFirst());
        s = s.streamRest(); //overwrite the stream so it stores only its rest
        //check for mutation bugs.
    }

    return new Stream(
        zippedFirsts,
        function () {
            return Stream.zip(args);
        }
    );
}

/**
*   Creates an input stream using a list of arguments
*
*   @static
*   @param {...*} [values] - values to be used in the stream
*   @returns {Stream} A new stream containing the elements
*   @example
*
*   var s = Stream.create(1,2,3,4);
**/
function create( /* arguments */) {
    if(arguments.length === 0) {
        return new Stream(null, null);
    }

    var tailArgs = [].slice.call(arguments,1);
    return new Stream(
        arguments[0],
        function () {
            return Stream.create.apply(null, tailArgs);
        }
    );
}

/**
*   Creates an input stream from an array
*
*   @static
*   @param {Array} [values] - array to use in creating the stream
*   @returns {Stream} A new stream containing the array elements
*   @example
*
*   var s = Stream.fromArray([1,2,3,4]);
**/
function fromArray(values) {
    return Stream.create(null, values);
}

/**
*   Walks a stream and applies the input function to the stream.
*   Stream has to be finite for this to work.
*
*   @param {Function} fn - The function to apply to every element of the stream
*   @example
*
*   s.walk(function(element) {
*        alert(element);
*   });
**/
function walk(fn) {
    var s = this;
    while (!s.isEmpty()){
        fn(s.head());
        s = s.tail();
    }
}

/**
*   Prints out the first n elements of a stream, will stop if stream length is less
*   than n
*
*   @param {Number} n - Number of elements to print
*   @example
*
*   s.print(5);
**/
function print(n) {
    var streamToPrint = this.pick(n);

    streamToPrint.walk(function (element){
        console.log(element);
    });
}

/**
*   Reduces the stream to a value which is the accumulated output obtained over
*   each stream element. Only valid for finite streams
*
*   @param {Function} fn - Function to execute on each argument,
*   taking the accumlatedValue so far and the current stream value
*   @param {*} initialValue - The initialValue to seed the accumulator with
*   @returns {*} The accumulated output
*   @example
*
*   var sum = stream.reduce(function (sum, n) {
*       return sum + n;
*   });
**/
function reduce(fn, initialValue) {
    var s = this;
    if(Stream.isEmpty(s)) {
        return initialValue;
    }

    if(initialValue == null){
        initialValue = s.streamFirst();
        s = s.streamRest();
    }

    return this.streamRest().reduce(fn, fn(initialValue, s.streamFirst()));
}

/**
*   Picks the first n elements out of a stream, terminates when it gets to the nth item or reaches the end of the stream
*
*   @param {Number} n - The number of elements to be picked
*   @returns {Stream} Returns a stream containing the picked items
*   @example
*
*   integerStream.pick(3);
**/
function pick(n) {
    if (!n || Stream.isEmpty(this)) {
        return new Stream(null, null);
    }

    var that = this;
    return new Stream(
        this.head(),
        function () {
            return that.tail().pick(n - 1);
        }
    );
}

/**
*   Picks the element at the nth index in a stream. Returns undefined
*   if stream size is less than the index
*
*   @param {Number} index - The index of the stream element to be picked
*   @returns {*} Value at nth index in stream
*   @example
*
*   integerStream.valueAt(3);
*   // => 3
**/
function valueAt(index) {
    var items = this.pick(index);

    return items[items.length - 1];
}

/**
*   Gets the length of a stream - only defined for finite streams
*
*   @returns {Number} length - The length of the stream
*   @example
*
*   stream.length()
*   // => 31
**/
function length() {
    var len = 0;
    var s = this;

    while(!Stream.isEmpty(s)) {
        len++;
        s = s.streamRest();
    }

    return len;
}

/**
*   Calculates the sum of the elements of a stream
*
*   @returns {Number} The sum of the elements of the stream
*   @example
*
*   stream.sum()
*   // => 31
**/
function sum() {
    return this.reduce(function (previousValue, currentValue){
        return previousValue + currentValue;
    }, 0);
}

/**
*   Check if the stream contains an element. Only defined for a finite stream
*
*   @param {*} index - The index of the stream element to be picked
*   @returns {Boolean} Returns `true` if the stream contains the element
*   @example
*
*   ones.contains(1)
*   // => true
**/
function contains(element) {
    var s = this;
    while(!Stream.isEmpty(s)) {
        if(s.streamFirst() === element){
            return true;
        }
        s = s.streamRest();
    }

    return false;
}

/**
*   Removes the first n elements of a stream.
*
*   @param {Number} n - The number of elements to be removed
*   @returns {Stream} The new stream with elements starting an
*   index n + 1 or an empty stream if n > stream.length
*   @example
*
*   integerStream.remove(3);
*   // => [4,5,6]
**/
function remove(n) {
    var s = this;
    while(n > 0) {
        if (Stream.isEmpty(s)) {
            return new Stream(null, null);
        }
        s = s.streamRest();
    }

    return new Stream(
        s.streamFirst(),
        function () {
            return s.streamRest()
        }
    );
}

/**
*   Returns the 'head' - first value - of a stream
*
*   @returns {*} The head of the stream
**/
function head() {
    if(this.isEmpty()){
        fail('Stream is empty!');
    }
    return this.streamFirst;
}

/**
 *  Constructs a stream made up of consecutive numbers in the
 *  range [low high]
 *
 *  @static
 *  @param {*} [low=0] - The lower limit value of the stream
 *  @param {*} high - The upper limit value of the stream
 *
 *  returns {Stream} A finite stream with elements in the range [low, high]
 */
function fromInterval(low, high){
    if(!low){
        low = 0;
    }
    if (!high || low === high){
        return new Stream(low, null);
    }

    return new Stream(
        low,
        Stream.fromInterval(low + 1, high)
    );
}

/**
 *  Constructs an infinite stream of consecutive numbers starting
 *  from integer `start`
 *
 *  @static
 *  @param {Number} [start=0] - The integer to start the stream from
 *
 *  returns {Stream} A infinite stream with elements starting from `start`
 */
function from(start) {
    if(!start){
        start = 0;
    }

    return new Stream(
        start,
        Stream.from(start + 1)
    );
}

/**
 *  Constructs a stream made up of consecutive numbers up to `stop`
 *
 *  @static
 *  @param {Number} [stop] - The maximum and last value of the stream
 *
 *  returns {Stream} A finite stream with elements in the range [0, stop]
 */
function upTo(stop){
    return Stream.fromInterval(0, stop);
}

/**
 *  Constructs an array containing the elements of a finite stream
 *
 *  returns {Array} A array containing the elements of the stream
 */
function toArray(){
    var items = [];

    var storer = function (val) {
        items.push(val);
    }

    this.walk(storer);
    return items;
}

/**
*   Returns the tail of a stream
*
*   @returns {Stream} The tail of the stream
**/
function tail() {
    if(this.isEmpty()){
        fail('Stream is empty!');
    }

    return this.streamRest();
}

/**
 *  Appends a new stream to the end of a current stream
 *
  *  @param {Stream} [s] - The stream to append to the end of this stream
 */
function append(s) {
    if(this.isEmpty()){
        return s;
    }

    var that = this;
    return new Stream(
        this.head(),
        function () {
            return that.tail().append(s);
        }
    );
}

//Static methods
Stream.map = map;
Stream.Ones = Ones;
Stream.add = add;
Stream.zip = zip;
Stream.create = create;
Stream.fromArray = fromArray;
Stream.fromInterval = fromInterval;
Stream.from = from;
Stream.upTo = upTo;

//Instance methods
Stream.prototype.head = head;
Stream.prototype.tail = tail;
Stream.prototype.isEmpty = isEmpty;
Stream.prototype.append = append;
Stream.prototype.pick = pick;
Stream.prototype.valueAt = valueAt;
Stream.prototype.length = length;
Stream.prototype.reduce = reduce;
Stream.prototype.sum = sum;
Stream.prototype.filter = filter;
Stream.prototype.contains = contains;
Stream.prototype.print = print;
Stream.prototype.remove = remove;
Stream.prototype.toArray = toArray;<|endoftext|>


<commit_before>const fs = require('fs-extra');

module.exports = function zipFolder(folderToZip, destination, callback) {
    var archiver = require('archiver'),
        output = fs.createWriteStream(destination),
        archive = archiver.create('zip', {});

    // If folder to zip is a symlink, we want to get the target
    // of the link and zip that instead of zipping the symlink
    if (fs.lstatSync(folderToZip).isSymbolicLink()) {
        folderToZip = fs.realpathSync(folderToZip);
    }

    output.on('close', function () {
        callback(null, archive.pointer());
    });

    archive.on('error', function (err) {
        callback(err, null);
    });

    archive.directory(folderToZip, '/');
    archive.pipe(output);
    archive.finalize();
};
<commit_msg>🐛 Ignored node_modules dir when archiving (#10969)<commit_after>const fs = require('fs-extra');

module.exports = function zipFolder(folderToZip, destination, callback) {
    var archiver = require('archiver'),
        output = fs.createWriteStream(destination),
        archive = archiver.create('zip', {});

    // If folder to zip is a symlink, we want to get the target
    // of the link and zip that instead of zipping the symlink
    if (fs.lstatSync(folderToZip).isSymbolicLink()) {
        folderToZip = fs.realpathSync(folderToZip);
    }

    output.on('close', function () {
        callback(null, archive.pointer());
    });

    archive.on('error', function (err) {
        callback(err, null);
    });
    archive.glob(`**/*`, {
        cwd: folderToZip,
        ignore: ['node_modules/**']
    });
    archive.pipe(output);
    archive.finalize();
};
<|endoftext|>


<commit_before>var driver = require('node-phantom-simple')
var argv = require('yargs').argv
import Element from './element';

class Ghost {
  constructor () {
    this.testRunner = argv['ghost-runner'] || 'phantomjs-prebuilt'
    this.driverOpts = null
    this.setDriverOpts({})
    this.browser = null
    this.currentContext = null
    this.page = null
    this.childPages = []
    this.clientScripts = []

    // Open the console if we're running slimer, and the GHOST_CONSOLE env var is set.
    if (this.testRunner.match(/slimerjs/) && process.env.GHOST_CONSOLE) {
      this.setDriverOpts({parameters: ['-jsconsole']})
    }
  }

  /**
   * Sets options object that is used in driver creation.
   */
  setDriverOpts (opts) {
    this.driverOpts = this.testRunner.match(/phantom/)
        ? opts
        : {}

    if (opts.parameters) {
        this.driverOpts.parameters = opts.parameters
    }

    this.driverOpts.path = require(this.testRunner).path

    // The dnode `weak` dependency is failing to install on travis.
    // Disable this for now until someone needs it.
    this.driverOpts.dnodeOpts = { weak: false }
  }

  /**
   * Adds scripts to be injected to for each page load.
   * Should be called before ghost#open.
   */
  injectScripts () {
    Array.slice(arguments).forEach(script => {
      this.clientScripts.push(script)
    })
  }

  /**
   * Callback when a page loads.
   * Injects javascript and other things we need.
   */
  onOpen () {
    // Inject any client scripts
    this.clientScripts.forEach(script => {
      this.page.injectJs(script)
    })
  }

  /**
   * Opens a page.
   * @param {String} url Url of the page to open.
   * @param {Object} options Keys supported:
   *  settings -  Key: Value map of all settings to set.
   *  headers -  Key: Value map of custom headers.
   *  viewportSize -  E.g., {height: 600, width: 800}
   */
  async open (url, options={}) {
    // If we already have a page object, just navigate it.
    if (this.page) {
      return new Promise(resolve => {
        this.page.open(url, (err, status) => {
          this.onOpen()
          resolve(status)
        })
      })
    }

    return new Promise(resolve => {
      driver.create(this.driverOpts, (err, browser) => {
        this.browser = browser
        browser.createPage((err, page) => {
          this.page = page;

          options.settings = options.settings || {}
          for (var i in options.settings) {
            page.set('settings.' + i, options.settings[i])
          }

          if (options.headers) {
            page.set('customHeaders', options.headers)
          }

          if (options.viewportSize) {
            page.set('viewportSize', options.viewportSize)
          }

          page.onResourceTimeout = (url) => {
            console.log('page timeout when trying to load ', url)
          }

          page.onPageCreated = (page) => {
            var pageObj = {
              page: page,
              url: null
            }

            this.childPages.push(pageObj)

            page.onUrlChanged = (url) => {
              pageObj.url = url;
            }

            page.onClosing = (closingPage) => {
              this.childPages = this.childPages.filter(eachPage => eachPage === closingPage)
            }
          }

          page.onConsoleMessage = (msg) => {
            if (argv['verbose']) {
              console.log('[Console]', msg)
            }
          }

          page.open(url, (err, status) => {
            this.onOpen()
            resolve(status)
          })
        })
      })
    })
  }

  close () {
    if (this.page) {
      this.page.close()
    }
    this.page = null
    this.currentContext = null
  }

  async exit () {
    this.close()
    this.browser.exit()
    this.browser = null
  }

  /**
   * Sets the current page context to run test methods on.
   * This is useful for running tests in popups for example.
   * To use the root page, pass an empty value.
   */
  async usePage (pagePattern) {
    if (!pagePattern) {
      this.currentContext = null;
    } else {
      this.currentContext = await this.waitForPage(pagePattern)
    }
  }

  /**
   * Gets the current page context that we're using.
   */
  get pageContext() {
    return (this.currentContext && this.currentContext.page) || this.page;
  }

  goBack () {
    this.pageContext.goBack()
  }

  goForward () {
    this.pageContext.goForward()
  }

  screenshot (filename, folder='screenshots') {
    filename = filename || 'screenshot-' + Date.now()
    this.pageContext.render(`${folder}/${filename}.png`)
  }

  /**
   * Returns the title of the current page.
   */
  async pageTitle () {
    return new Promise(resolve => {
      this.pageContext.evaluate(() => { return document.title },
        (err, result) => {
          resolve(result)
        })
    })
  }

  /**
   * Waits for the page title to match a given state.
   */
  async waitForPageTitle (expected) {
    var waitFor = this.waitFor.bind(this)
    var pageTitle = this.pageTitle.bind(this)
    return new Promise(async resolve => {
      var result = await waitFor(async () => {
        var title = await pageTitle()
        if (expected instanceof RegExp) {
          return expected.test(title)
        } else {
          return title === expected
        }
      })
      resolve(result)
    })
  }

  /**
   * Returns an element if it finds it in the page, otherwise returns null.
   * @param {string} selector
   */
  async findElement (selector) {
    return new Promise(resolve => {
      this.pageContext.evaluate((selector) => {
        return !!document.querySelector(selector)
      },
      selector,
      (err, result) => {
        if (err) {
          console.warn('findElement error', err)
        }

        if (!result) {
          return resolve(null)
        }
        resolve(new Element(this.pageContext, selector))
      })
    })
  }

  /**
   * Returns an array of {Element} instances that match a selector.
   * @param {string} selector
   */
  async findElements (selector) {
    return new Promise(resolve => {
      this.pageContext.evaluate((selector) => {
        return document.querySelectorAll(selector).length
      },
      selector,
      (err, numElements) => {
        if (err) {
          console.warn('findElements error', err)
        }

        if (!numElements) {
          return resolve(null)
        }

        var elementCollection = [];
        for (var i = 0; i < numElements; i++) {
          elementCollection.push(new Element(this.pageContext, selector, i))
        }
        resolve(elementCollection)
      })
    })
  }

  /**
   * Returns all elements that match the current selector in the page.
   * @Deprecated
   */
  async countElements (selector) {
    console.log('countElements is deprecated, use findElements().length instead.')
    var collection = await this.findElements(selector);
    return collection.length;
  }

  /**
   * Resizes the page to a desired width and height.
   */
  async resize (width, height) {
    this.pageContext.set('viewportSize', {width, height})
  }

  /**
   * Executes a script within the page.
   */
  async script (func, args) {
    if (!Array.isArray(args)) {
      args = [args]
    }

    return new Promise(resolve => {
      this.pageContext.evaluate((stringyFunc, args) => {
        var invoke = new Function(
          "return " + stringyFunc
        )();
        return invoke.apply(null, args)
      },
      func.toString(),
      args,
      (err, result) => {
          resolve(result)
        })
    })
  }

  /**
   * Waits for an arbitrary amount of time, or an async function to resolve.
   * @param (Number|Function)
   */
  async wait (waitFor=1000, pollMs=100) {
    if (!(waitFor instanceof Function)) {
      return new Promise(resolve => {
        setTimeout(resolve, waitFor)
      })
    } else {
      return new Promise(resolve => {
        var poll = async () => {
          var result = await waitFor()
          if (result) {
            resolve(result)
          } else {
            setTimeout(poll, pollMs)
          }
        }
        poll()
      })
    }
  }

  /**
   * Waits for an element to exist in the page.
   */
  async waitForElement (selector) {
    // Scoping gets broken within async promises, so bind these locally.
    var waitFor = this.waitFor.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var element = await waitFor(async () => {
        var el = await findElement(selector)
        if (el) {
          return el
        }
        return false
      })
      resolve(element)
    })
  }

  /**
   * Waits for an element to be hidden, or removed from the dom.
   */
  async waitForElementNotVisible (selector) {
    var waitFor = this.waitFor.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var isHidden = await waitFor(async () => {
        var el = await findElement(selector)
        return !el || !await el.isVisible()
      })
      resolve(isHidden)
    })
  }

  /**
   * Waits for an element to exist, and be visible.
   */
  async waitForElementVisible (selector) {
    var waitFor = this.waitFor.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var isVisible = await waitFor(async () => {
        var el = await findElement(selector)
        return el && await el.isVisible()
      })
      resolve(isVisible)
    })
  }

  /**
   * Waits for a child page to be loaded.
   */
  waitForPage (url) {
    var waitFor = this.waitFor.bind(this)
    var childPages = this.childPages
    return new Promise(async resolve => {
      var page = await waitFor(async () => {
        return childPages.filter(val => {
          return val.url.includes(url)
        })
      })
      resolve(page[0])
    })
  }

  /**
   * Waits for a condition to be met
   * @deprecated.
   */
  async waitFor (func, pollMs=100) {
    console.log('waitFor is deprecated, use wait(fn) instead.')
    return this.wait(func, pollMs)
  }
}

var ghost = new Ghost()
export default ghost
<commit_msg>Use wait() instead of waitFor().<commit_after>var driver = require('node-phantom-simple')
var argv = require('yargs').argv
import Element from './element';

class Ghost {
  constructor () {
    this.testRunner = argv['ghost-runner'] || 'phantomjs-prebuilt'
    this.driverOpts = null
    this.setDriverOpts({})
    this.browser = null
    this.currentContext = null
    this.page = null
    this.childPages = []
    this.clientScripts = []

    // Open the console if we're running slimer, and the GHOST_CONSOLE env var is set.
    if (this.testRunner.match(/slimerjs/) && process.env.GHOST_CONSOLE) {
      this.setDriverOpts({parameters: ['-jsconsole']})
    }
  }

  /**
   * Sets options object that is used in driver creation.
   */
  setDriverOpts (opts) {
    this.driverOpts = this.testRunner.match(/phantom/)
        ? opts
        : {}

    if (opts.parameters) {
        this.driverOpts.parameters = opts.parameters
    }

    this.driverOpts.path = require(this.testRunner).path

    // The dnode `weak` dependency is failing to install on travis.
    // Disable this for now until someone needs it.
    this.driverOpts.dnodeOpts = { weak: false }
  }

  /**
   * Adds scripts to be injected to for each page load.
   * Should be called before ghost#open.
   */
  injectScripts () {
    Array.slice(arguments).forEach(script => {
      this.clientScripts.push(script)
    })
  }

  /**
   * Callback when a page loads.
   * Injects javascript and other things we need.
   */
  onOpen () {
    // Inject any client scripts
    this.clientScripts.forEach(script => {
      this.page.injectJs(script)
    })
  }

  /**
   * Opens a page.
   * @param {String} url Url of the page to open.
   * @param {Object} options Keys supported:
   *  settings -  Key: Value map of all settings to set.
   *  headers -  Key: Value map of custom headers.
   *  viewportSize -  E.g., {height: 600, width: 800}
   */
  async open (url, options={}) {
    // If we already have a page object, just navigate it.
    if (this.page) {
      return new Promise(resolve => {
        this.page.open(url, (err, status) => {
          this.onOpen()
          resolve(status)
        })
      })
    }

    return new Promise(resolve => {
      driver.create(this.driverOpts, (err, browser) => {
        this.browser = browser
        browser.createPage((err, page) => {
          this.page = page;

          options.settings = options.settings || {}
          for (var i in options.settings) {
            page.set('settings.' + i, options.settings[i])
          }

          if (options.headers) {
            page.set('customHeaders', options.headers)
          }

          if (options.viewportSize) {
            page.set('viewportSize', options.viewportSize)
          }

          page.onResourceTimeout = (url) => {
            console.log('page timeout when trying to load ', url)
          }

          page.onPageCreated = (page) => {
            var pageObj = {
              page: page,
              url: null
            }

            this.childPages.push(pageObj)

            page.onUrlChanged = (url) => {
              pageObj.url = url;
            }

            page.onClosing = (closingPage) => {
              this.childPages = this.childPages.filter(eachPage => eachPage === closingPage)
            }
          }

          page.onConsoleMessage = (msg) => {
            if (argv['verbose']) {
              console.log('[Console]', msg)
            }
          }

          page.open(url, (err, status) => {
            this.onOpen()
            resolve(status)
          })
        })
      })
    })
  }

  close () {
    if (this.page) {
      this.page.close()
    }
    this.page = null
    this.currentContext = null
  }

  async exit () {
    this.close()
    this.browser.exit()
    this.browser = null
  }

  /**
   * Sets the current page context to run test methods on.
   * This is useful for running tests in popups for example.
   * To use the root page, pass an empty value.
   */
  async usePage (pagePattern) {
    if (!pagePattern) {
      this.currentContext = null;
    } else {
      this.currentContext = await this.waitForPage(pagePattern)
    }
  }

  /**
   * Gets the current page context that we're using.
   */
  get pageContext() {
    return (this.currentContext && this.currentContext.page) || this.page;
  }

  goBack () {
    this.pageContext.goBack()
  }

  goForward () {
    this.pageContext.goForward()
  }

  screenshot (filename, folder='screenshots') {
    filename = filename || 'screenshot-' + Date.now()
    this.pageContext.render(`${folder}/${filename}.png`)
  }

  /**
   * Returns the title of the current page.
   */
  async pageTitle () {
    return new Promise(resolve => {
      this.pageContext.evaluate(() => { return document.title },
        (err, result) => {
          resolve(result)
        })
    })
  }

  /**
   * Waits for the page title to match a given state.
   */
  async waitForPageTitle (expected) {
    var waitFor = this.wait.bind(this)
    var pageTitle = this.pageTitle.bind(this)
    return new Promise(async resolve => {
      var result = await waitFor(async () => {
        var title = await pageTitle()
        if (expected instanceof RegExp) {
          return expected.test(title)
        } else {
          return title === expected
        }
      })
      resolve(result)
    })
  }

  /**
   * Returns an element if it finds it in the page, otherwise returns null.
   * @param {string} selector
   */
  async findElement (selector) {
    return new Promise(resolve => {
      this.pageContext.evaluate((selector) => {
        return !!document.querySelector(selector)
      },
      selector,
      (err, result) => {
        if (err) {
          console.warn('findElement error', err)
        }

        if (!result) {
          return resolve(null)
        }
        resolve(new Element(this.pageContext, selector))
      })
    })
  }

  /**
   * Returns an array of {Element} instances that match a selector.
   * @param {string} selector
   */
  async findElements (selector) {
    return new Promise(resolve => {
      this.pageContext.evaluate((selector) => {
        return document.querySelectorAll(selector).length
      },
      selector,
      (err, numElements) => {
        if (err) {
          console.warn('findElements error', err)
        }

        if (!numElements) {
          return resolve(null)
        }

        var elementCollection = [];
        for (var i = 0; i < numElements; i++) {
          elementCollection.push(new Element(this.pageContext, selector, i))
        }
        resolve(elementCollection)
      })
    })
  }

  /**
   * Returns all elements that match the current selector in the page.
   * @Deprecated
   */
  async countElements (selector) {
    console.log('countElements is deprecated, use findElements().length instead.')
    var collection = await this.findElements(selector);
    return collection.length;
  }

  /**
   * Resizes the page to a desired width and height.
   */
  async resize (width, height) {
    this.pageContext.set('viewportSize', {width, height})
  }

  /**
   * Executes a script within the page.
   */
  async script (func, args) {
    if (!Array.isArray(args)) {
      args = [args]
    }

    return new Promise(resolve => {
      this.pageContext.evaluate((stringyFunc, args) => {
        var invoke = new Function(
          "return " + stringyFunc
        )();
        return invoke.apply(null, args)
      },
      func.toString(),
      args,
      (err, result) => {
          resolve(result)
        })
    })
  }

  /**
   * Waits for an arbitrary amount of time, or an async function to resolve.
   * @param (Number|Function)
   */
  async wait (waitFor=1000, pollMs=100) {
    if (!(waitFor instanceof Function)) {
      return new Promise(resolve => {
        setTimeout(resolve, waitFor)
      })
    } else {
      return new Promise(resolve => {
        var poll = async () => {
          var result = await waitFor()
          if (result) {
            resolve(result)
          } else {
            setTimeout(poll, pollMs)
          }
        }
        poll()
      })
    }
  }

  /**
   * Waits for an element to exist in the page.
   */
  async waitForElement (selector) {
    // Scoping gets broken within async promises, so bind these locally.
    var waitFor = this.wait.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var element = await waitFor(async () => {
        var el = await findElement(selector)
        if (el) {
          return el
        }
        return false
      })
      resolve(element)
    })
  }

  /**
   * Waits for an element to be hidden, or removed from the dom.
   */
  async waitForElementNotVisible (selector) {
    var waitFor = this.wait.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var isHidden = await waitFor(async () => {
        var el = await findElement(selector)
        return !el || !await el.isVisible()
      })
      resolve(isHidden)
    })
  }

  /**
   * Waits for an element to exist, and be visible.
   */
  async waitForElementVisible (selector) {
    var waitFor = this.wait.bind(this)
    var findElement = this.findElement.bind(this)
    return new Promise(async resolve => {
      var isVisible = await waitFor(async () => {
        var el = await findElement(selector)
        return el && await el.isVisible()
      })
      resolve(isVisible)
    })
  }

  /**
   * Waits for a child page to be loaded.
   */
  waitForPage (url) {
    var waitFor = this.wait.bind(this)
    var childPages = this.childPages
    return new Promise(async resolve => {
      var page = await waitFor(async () => {
        return childPages.filter(val => {
          return val.url.includes(url)
        })
      })
      resolve(page[0])
    })
  }

  /**
   * Waits for a condition to be met
   * @deprecated.
   */
  async waitFor (func, pollMs=100) {
    console.log('waitFor is deprecated, use wait(fn) instead.')
    return this.wait(func, pollMs)
  }
}

var ghost = new Ghost()
export default ghost
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Content, Item, Input, Label, Text, Button } from 'native-base';
import renderIf from 'render-if';

class TextInputComponent extends Component {
  static propTypes = {
    defaultText: PropTypes.string.isRequired,
    value: PropTypes.string,
    headerLabel: PropTypes.string,
    label: PropTypes.string, //TODO -
    hiddenText: PropTypes.bool,
    footDescription: PropTypes.string,
    clearButton: PropTypes.bool,
    error: PropTypes.string, //TODO -
  }

  constructor(props) {
    super(props);
    this.state = {
      value: this.props.value,
    };
    this.clearInput = this.clearInput.bind(this);
  }

  clearInput() {
    this.textInput._root.clear();
  }

  render() {
    return (
      <Content>
        {renderIf(this.props.headerLabel !== '')(
           {this.props.headerLabel}
        )}
        <Item regular>
          <Input
            ref={component => this.textInput = component}
            placeholder={this.props.defaultText}
            value={this.state.value}
            secureTextEntry={this.props.hiddenText}
          />

          {renderIf(this.props.clearButton)(
            <Button transparent onPress={this.clearInput}>
              <Text> Clear </Text>
            </Button>
          )}
        </Item>
        {renderIf(this.props.footDescription !== '')(
           {this.props.footDescription}
         )}
      </Content>
    );
  }
}

export default TextInputComponent;
<commit_msg>Changes on TextInput component<commit_after>import React, { Component, PropTypes } from 'react';
import { Content, Item, Input, Label, Text, Button } from 'native-base';
import renderIf from 'render-if';

class TextInputComponent extends Component {
  static propTypes = {
    defaultText: PropTypes.string.isRequired,
    value: PropTypes.string,
    headerLabel: PropTypes.string,
    label: PropTypes.string,
    hiddenText: PropTypes.bool,
    footDescription: PropTypes.string,
    clearButton: PropTypes.bool,
    error: PropTypes.string, //TODO -
  }

  constructor(props) {
    super(props);
    this.state = {
      value: this.props.value,
    };
    this.clearInput = this.clearInput.bind(this);
  }

  clearInput() {
    this.textInput._root.clear();
  }

  render() {
    return (
      <Content>
        {renderIf(this.props.headerLabel !== '')(
          {this.props.headerLabel} //when on, will appear on top of text input
        )}

        <Item underline>
          {renderIf(this.props.label !== '')(
            {this.props.label} //when on, will appear left to text input
          )}
          <Input
          ref = {component => this.textInput = component}
          value = {this.state.value}
          placeholder= {this.props.defaultText}
          secureTextEntry = {this.props.hiddenText}/>

          {renderIf(this.props.clearButton)(
            <Icon active name='backspace' onPress={this.clearInput}/>
          )}
        </Item>

        {renderIf(this.props.footDescription !== '')(
           {this.props.footDescription}
        )}
      </Content>
    );
  }
}

export default TextInputComponent;
<|endoftext|>


<commit_before>/*
  struct.js v0.1

  This module use Proxy API provided by ECMAScript 6 (Harmony)
  @see http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies

  With ES6-shim(https://github.com/paulmillr/es6-shim) it
  could also work in Firefox4 or Chrome.

  Example:

    Struct.define('Position', {
      x: {type: 'number'},
      y: {type: 'number'}
    });

    Struct.define('Ninja', {
      name: {type: 'string', writable: false},
      life: {type: 'number'},
      pos:  {type: 'struct:Position'}
    });

    var sasuke = Struct.create('Ninja', {
      name: 'Sasuke'
    });

    sasuke.life = 50; // works
    sasuke.pos = Struct.create('Position', {x:0, y:10}); // works

    var bar = sasuke.undefProp;    // Throws error
    sasuke.life = '50';            // Throws error
    sasuke.pos = {x: 0, y:0, z:0}; // Throws error
    sasuke.newProp = 'foo';        // Throws error

    delete sasuke.life;  // works
    delete sasuke.life_; // Throws error

    Struct.getType(sasuke); // => Returns 'Ninja'

 *
 */
(function() {
  'use strict';

  if (typeof Struct !== 'undefined') {
    return;
  }

  var STRUCT_NAME_KEY = '__structName__';

  // Check Proxy API is enabled
  var hasProxyAPI_ = window.Proxy && (typeof(Proxy.create) === 'function');
  console.log('Proxy API?:', hasProxyAPI_);

  /**
   * @class Struct
   */
  var Struct = window.Struct = {
    structs: {}
  };

  /**
   * Define new struct.
   *
   * @param {String} name Struct name.
   * @param {Object} props Property configs.
   */
  Struct.define = function(name, props) {
    if (typeof(name) !== 'string') {
      throw 'First argument must be String type (Struct name)';
    }
    if (typeof(props) !== 'object') {
      throw 'Second argument must be Object type (Property settings)';
    }
    if (this.structs[name]) {
      throw name + ' is already defined';
    }

    Object.keys(props).forEach(function(k) {
      // Set default writable:true
      if (props[k].writable === undefined) {
        props[k].writable = true;
      }
    });

    // Add type name property
    props[STRUCT_NAME_KEY] = {
      value: name,
      wriatble: false,
      enumerable: false
    };

    // Struct definition will never change
    Object.freeze(props);

    this.structs[name] = props;
  }

  /**
   * Gets struct type name.
   *
   * @param {Object} obj Object.
   * @return {String} Type name. Returns undefined
   * if an argument is not a Struct object.
   */
  Struct.getType = function(obj) {
    if (typeof obj !== 'object') {
      throw 'First argument must be object type';
    }
    if (obj.hasOwnProperty(STRUCT_NAME_KEY)) {
      return obj[STRUCT_NAME_KEY];
    }
    return undefined;
  }

  /**
   * Check is struct object or not.
   *
   * @param {Object} obj Object.
   * @return {boolean} True if parameter is struct object.
   */
  Struct.isStruct = function(obj) {
    return !!obj &&
           typeof obj === 'object' &&
           typeof obj[STRUCT_NAME_KEY] === 'string';
  }

  /**
   * Create struct object.
   *
   * @param {String} name Struct name.
   * @param {Object} obj Base object (option).
   * @return {Object} Struct object.
   */
  Struct.create = function(name, obj) {
    if (!this.structs.hasOwnProperty(name)) {
      throw 'Struct named "' + name + '" is not defined';
    }
    obj = obj || {};

    var props = this.structs[name];
    Object.defineProperties(obj, props);

    var ret;
    if (hasProxyAPI_) {
      ret = Proxy.create(handlerMaker_(obj, props));
    } else {
      //fallback
      ret = Object.seal(obj);
    }
    return ret;
  }

  var REGEXP_STRUCT_TYPE = /^struct:(.+)/;

  /**
   * Check struct type (internal)
   */
  function isStructType(type, obj) {
    var mat = type.match(REGEXP_STRUCT_TYPE);
    console.log(mat);
    if (mat && Struct.isStruct(obj)) {
      console.log(mat[1]);
      console.log(Struct.getType(obj));
      return Struct.getType(obj) === mat[1];
    }
    return false;
  }

  /**
   * Create trap functions (internal)
   */
  function handlerMaker_(obj, props) {
    return {

      getOwnPropertyDescriptor: function(name) {
        var desc = Object.getOwnPropertyDescriptor(obj, name);
        // a trapping proxy's properties must always be configurable
        if (desc !== undefined) { desc.configurable = true; }
        return desc;
      },

      getPropertyDescriptor: function(name) {
        var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
        // a trapping proxy's properties must always be configurable
        if (desc !== undefined) { desc.configurable = true; }
        return desc;
      },

      getOwnPropertyNames: function() {
        return Object.getOwnPropertyNames(obj);
      },

      getPropertyNames: function() {
        return Object.getPropertyNames(obj);
      },

      defineProperty: function(name, desc) {
        throw 'Cannot fix this object';
      },

      /**
       * Delete specified property.
       * Check property name if defined in advance.
       */
      delete: function(name) {
        if (name in props) {
          return delete obj[name];
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      fix: function() {
        if (Object.isFrozen(obj)) {
          return Object.getOwnPropertyNames(obj).map(function(name) {
            return Object.getOwnPropertyDescriptor(obj, name);
          });
        }
        // As long as obj is not frozen,
        // the proxy won't allow itself to be fixed
        return undefined; // will cause a TypeError to be thrown
      },

      has: function(name) {
        return name in obj;
      },

      hasOwn: function(name) {
        return Object.prototype.hasOwnProperty.call(obj, name);
      },

      /**
       * Get value of specified property.
       * Check property name if defined in advance.
       */
      get: function(receiver, name) {
        if (name in props) {
          return obj[name];
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      /**
       * Set value.
       * Check property name if defined and type is matched in advance.
       */
      set: function(receiver, name, val) {
        if (name in props) {

          // Check property descriptor
          var desc = this.getOwnPropertyDescriptor(name);
          if (!desc || !desc.writable) {
            throw name + ' is not writable property';
          }

          // Check type match
          var type = props[name].type;
          if (val === null || val === undefined ||
              typeof val === type || isStructType(type, val)) {
            // OK
          } else {
            throw name + ' must be ' + props[name].type + ' type';
          }

          obj[name] = val;
          return true;
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      enumerate: function() {
        var result = [];
        for (var name in obj) { result.push(name); }
        return result;
      },

      keys: function() {
        return Object.keys(obj);
      }
    };
  }
})();

<commit_msg>Add type check functions<commit_after>/*
  struct.js v0.1

  This module use Proxy API provided by ECMAScript 6 (Harmony)
  @see http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies

  With ES6-shim(https://github.com/paulmillr/es6-shim) it
  could also work in Firefox4 or Chrome.

  Example:

    Struct.define('Position', {
      x: {type: 'number'},
      y: {type: 'number'}
    });

    Struct.define('Ninja', {
      name: {type: 'string', writable: false},
      life: {type: 'number'},
      pos:  {type: 'struct:Position'}
    });

    var sasuke = Struct.create('Ninja', {
      name: 'Sasuke'
    });

    sasuke.life = 50; // works
    sasuke.pos = Struct.create('Position', {x:0, y:10}); // works

    var bar = sasuke.undefProp;    // Throws error
    sasuke.life = '50';            // Throws error
    sasuke.pos = {x: 0, y:0, z:0}; // Throws error
    sasuke.newProp = 'foo';        // Throws error

    delete sasuke.life;  // works
    delete sasuke.life_; // Throws error

    Struct.getType(sasuke); // => Returns 'Ninja'

 *
 */
(function() {
  'use strict';

  if (typeof Struct !== 'undefined') {
    return;
  }

  var STRUCT_NAME_KEY = '__structName__';

  var REGEXP_STRUCT_TYPE = /^struct:(.+)/;

  // Check Proxy API is enabled
  var hasProxyAPI = window.Proxy && (typeof(Proxy.create) === 'function');

  // Support types and check methods
  var typeChecker = createChecker();

  /**
   * @class Struct
   */
  var Struct = window.Struct = {
    structs: {}
  };

  /**
   * Define new struct.
   *
   * @param {String} name Struct name.
   * @param {Object} props Property configs.
   */
  Struct.define = function(name, props) {
    if (typeof(name) !== 'string') {
      throw 'First argument must be String type (Struct name)';
    }
    if (typeof(props) !== 'object') {
      throw 'Second argument must be Object type (Property settings)';
    }
    if (this.structs[name]) {
      throw name + ' is already defined';
    }

    Object.keys(props).forEach(function(k) {
      // Set default writable:true
      if (props[k].writable === undefined) {
        props[k].writable = true;
      }
    });

    // Add type name property
    props[STRUCT_NAME_KEY] = {
      value: name,
      wriatble: false,
      enumerable: false
    };

    // Struct definition will never change
    Object.freeze(props);

    this.structs[name] = props;
  }

  /**
   * Gets struct type name.
   *
   * @param {Object} obj Object.
   * @return {String} Type name. Returns undefined
   * if an argument is not a Struct object.
   */
  Struct.getType = function(obj) {
    if (typeof obj !== 'object') {
      throw 'First argument must be object type';
    }
    if (obj.hasOwnProperty(STRUCT_NAME_KEY)) {
      return obj[STRUCT_NAME_KEY];
    }
    return undefined;
  }

  /**
   * Check is struct object or not.
   *
   * @param {Object} obj Object.
   * @return {boolean} True if parameter is struct object.
   */
  Struct.isStruct = function(obj) {
    return !!obj &&
           typeof obj === 'object' &&
           typeof obj[STRUCT_NAME_KEY] === 'string';
  }

  /**
   * Create struct object.
   *
   * @param {String} name Struct name.
   * @param {Object} obj Base object (option).
   * @return {Object} Struct object.
   */
  Struct.create = function(name, obj) {
    if (!this.structs.hasOwnProperty(name)) {
      throw 'Struct named "' + name + '" is not defined';
    }
    obj = obj || {};

    var props = this.structs[name];
    Object.defineProperties(obj, props);

    var ret;
    if (hasProxyAPI) {
      ret = Proxy.create(handlerMaker_(obj, props));
    } else {
      //fallback
      ret = Object.seal(obj);
    }
    return ret;
  }

  /**
   * Check struct type (internal)
   */
  function isStructType(type, obj) {
    var mat = type.match(REGEXP_STRUCT_TYPE);
    if (mat && Struct.isStruct(obj)) {
      console.log(mat[1]);
      console.log(Struct.getType(obj));
      return Struct.getType(obj) === mat[1];
    }
    return false;
  }

  /**
   * Check value type (internal)
   */
  function isType(type, val) {
    if (typeChecker[type]) {
      return typeChecker[type](val);
    }
    return false;
  }

  /**
   * Create trap functions (internal)
   */
  function handlerMaker_(obj, props) {
    return {

      getOwnPropertyDescriptor: function(name) {
        var desc = Object.getOwnPropertyDescriptor(obj, name);
        // a trapping proxy's properties must always be configurable
        if (desc !== undefined) { desc.configurable = true; }
        return desc;
      },

      getPropertyDescriptor: function(name) {
        var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
        // a trapping proxy's properties must always be configurable
        if (desc !== undefined) { desc.configurable = true; }
        return desc;
      },

      getOwnPropertyNames: function() {
        return Object.getOwnPropertyNames(obj);
      },

      getPropertyNames: function() {
        return Object.getPropertyNames(obj);
      },

      defineProperty: function(name, desc) {
        throw 'Cannot fix this object';
      },

      /**
       * Delete specified property.
       * Check property name if defined in advance.
       */
      delete: function(name) {
        if (name in props) {
          return delete obj[name];
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      fix: function() {
        if (Object.isFrozen(obj)) {
          return Object.getOwnPropertyNames(obj).map(function(name) {
            return Object.getOwnPropertyDescriptor(obj, name);
          });
        }
        // As long as obj is not frozen,
        // the proxy won't allow itself to be fixed
        return undefined; // will cause a TypeError to be thrown
      },

      has: function(name) {
        return name in obj;
      },

      hasOwn: function(name) {
        return Object.prototype.hasOwnProperty.call(obj, name);
      },

      /**
       * Get value of specified property.
       * Check property name if defined in advance.
       */
      get: function(receiver, name) {
        if (name in props) {
          return obj[name];
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      /**
       * Set value.
       * Check property name if defined and type is matched in advance.
       */
      set: function(receiver, name, val) {
        if (name in props) {

          // Check property descriptor
          var desc = this.getOwnPropertyDescriptor(name);
          if (!desc || !desc.writable) {
            throw name + ' is not writable property';
          }

          // Check type match
          var type = props[name].type;
          if (val === null || val === undefined ||
              isStructType(type, val) || isType(type, val)) {
            // OK
          } else {
            throw name + ' must be ' + props[name].type + ' type';
          }

          obj[name] = val;
          return true;
        } else {
          throw name + ' is not defined in this struct';
        }
      },

      enumerate: function() {
        var result = [];
        for (var name in obj) { result.push(name); }
        return result;
      },

      keys: function() {
        return Object.keys(obj);
      }
    };
  }

  /**
   * Create type checker functions (internal)
   */
  function createChecker() {

    function toString(val) {
      return Object.prototype.toString.call(val);
    }

    function isString(val) {
      return toString(val) === '[object String]';
    }

    function isNumber(val) {
      return toString(val) === '[object Number]' && !isNaN(val);
    }

    function isBoolean(val) {
      return toString(val) === '[object Boolean]';
    }

    function isFunction(val) {
      return toString(val) === '[object Function]';
    }

    function isArray(val) {
      return toString(val) === '[object Array]';
    }

    function isArrayLike(val) {
      return isArray(val) ||
             (val && typeof(val) === 'object' && isNumber(val.length));
    }

    function isObject(val) {
      return toString(val) === '[object Object]';
    }

    function isObjectLike(val) {
      return val !== null && typeof(val) === 'object';
    }

    function isRegExp(val) {
      return toString(val) === '[object RegExp]';
    }

    function isDate(val) {
      return toString(val) === '[object Date]';
    }

    function isDomNode(val) {
      return val && isString(val.nodeName) && isArrayLike(val.childNodes);
    }

    return {
      'string': isString,
      'number': isNumber,
      'boolean': isBoolean,
      'funtion': isFunction,
      'array': isArray,
      'arraylike': isArrayLike,
      'object': isObject,
      'objectlike': isObjectLike,
      'regexp': isRegExp,
      'date': isDate,
      'domnode': isDomNode,
    };
  }

})();

<|endoftext|>