// -*- coding: UTF-8 -*-
/*!
 * Wrench JavaScript 1.5 Environment v1.0.0
 * http://wrench.monkeyscript.org/
 *
 * Copyright © 2009 Daniel Friesen
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @fileOverview This file contains new methods for the Object class
 */
/**
 * @name Object
 * @class The built-in object class which all objects prototype which acts somewhat like a hash of key/value pairs.
 */

/**
 * Returns a new object with the passed object's keys as the new object's values
 * and the passed object's values as the keys to those values.
 * 
 * @param {Object} obj The object to create an inverted object from
 * @return {Object} The inverted object
 */
Object.invert = function invert(obj) {
	var o = {};
	for( var k in obj )
		if( obj.hasOwnProperty(k) )
			o[obj[k]] = k;
	return o;
};

/**
 * Merges multiple objects together into the first object with a right dominant pattern.
 * This will modify the first object and override methods with the rightmost value.
 * 
 * This method returns the object that was modified, so if you would like a new
 * object instead of one of your objects being overwritten use `Object.merge({}, ...);`
 * 
 * @param {Boolean} [deep=false] Use a deep object merge
 * @param {Object} obj Object to merge into
 * @param {Object} ...objs Objects to merge into obj
 * @return {Object} The first object passed to merge
 */
Object.merge = function merge() {
	var a = Array.slice(arguments), left, deep = false;
	if ( typeof a[0] === 'boolean' )
		deep = a.shift();
	left = a.shift();
	if ( left === null || typeof left !== 'object' )
		throw new TypeError();
	for ( var i = 0, l = a.length; i<l; i++ ) {
		var right = a[i];
		for ( var key in right ) {
			if ( right.hasOwnProperty(key) ) {
				if ( deep && isObject(right[key]) ) {
					if ( !isObject(left[key]) )
						left[key] = {};
					left[key] = merge( true, left[key], right[key] );
				} else {
					left[key] = right[key];
				}
			}
		}
	}
	return left;
};

/**
 * Return the number of key/value pairs within an object
 * 
 * @param {Object} obj The object to count
 * @return {Number} The number of pairs in the object
 */
(function(useCount) {
	Object.count = function count(obj) {
		if ( useCount ) return obj.__count__;
		return Object.keys(obj).length;
	};
})(({}).__count__ === 0);

/**
 * Return an array containing the list of keys within the object
 * 
 * @param {Object} obj The object to take keys from
 * @return {Array} The array of keys
 */
if ( !Object.keys )
	Object.keys = function keys(obj) {
		var keys = [];
		for ( var k in obj )
			if ( obj.hasOwnProperty( k ) )
				keys.push( k );
		return keys;
	};

/**
 * Return an array containing the list of values within the object
 * 
 * @param {Object} obj The object to take values from
 * @return {Array} The array of values
 */
if ( !Object.values )
	Object.values = function values(obj) {
		var values = [];
		for ( var k in obj )
			if ( obj.hasOwnProperty( k ) )
				values.push( obj[k] );
		return values;
	};

/**
 * @fileOverview This file contains new methods for the Number class
 */
/**
 * @name Number
 * @class A built-in class for numeric data which handles integers and floats within itself.
 */

/**
 * Return a padded string for the number.
 * 
 * @param {Number} len The minimum length of the string to return
 * @param {String} [chars="0"] Characters to use when padding the number
 * @param [radix=10] The radix to use when turning the number into a string
 */
Number.prototype.pad =
Number.prototype.padLeft = function pad(len, chars, radix) {
	return this.toString(radix || 10).padLeft(len || 0, chars || "0");
};

/**
 * @fileOverview This file contains new methods for the Math namespace
 */
/**
 * @name Math
 * @namespace A built in namespace containing mathmatical constants and functions.
 */

/**
 * Extension to Math.random which returns an integer between 2 values instead of
 * a random float.
 * Partly borrowed from {@link https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Math/random#Example.3a_Using_Math.random}
 * 
 * @param {Number} min The lowest number (inclusive) to return
 * @param {Number} max The highest number (inclusive) to return
 * @return {Number} A random integer between min and max
 */
Math.rand = function rand(min, max) {
	return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
};

/**
 * Calculate the sum of a list of numbers. Is also useful as an argument to .reduceNative
 * 
 * @param {Number} ... Arguments forming a list of numbers to sum up tp a total
 * @return {Number} The total sum of all the numbers passed
 */
if(!Math.sum)
	Math.sum = function sum(/*...*/) {
		if( !arguments.length )
			throw new TypeError();
		var args = Array.slice(arguments);
		var s = args.shift();
		while( args.length )
			s += args.shift();
		return s;
	};

/**
 * Calculate the average value from a list of numbers.
 * 
 * @param {Number} ... Arguments forming a list of numbers to create and average of
 * @return {Number} The average of all the numbers passed
 */
Math.avg = function avg(/*...*/) {
	return Math.sum.apply(Math, arguments) / arguments.length;
};

/**
 * @fileOverview This file contains generics for String methods
 */

(function fn(method) {
	if(method in String) return fn;
	String[method] = function(arr) {
		return String.prototype[method].apply(arr, Array.slice(arguments, 1));
	};
	return fn;
})('split')('explode')
('repeat')('expand');

/**
 * @fileOverview This file contains generics for Array methods
 */

Array.slice = function slice(arr) {
	return Array.prototype.slice.apply(arr, Array.prototype.slice.call(arguments, 1));
};

(function fn(method) {
	if(method in Array) return fn;
	Array[method] = function(arr) {
		return Array.prototype[method].apply(arr, Array.slice(arguments, 1));
	};
	return fn;
})('diff')('intersect')('unique')('shuffle')('item')('has')('indexOf')('lastIndexOf')
('every')('some')('filter')('forEach')('map')('reduce')('reduceRight')('join');

/**
 * @fileOverview This file contains new methods for the String class
 */
/**
 * @name String
 * @class A built-in class for sequences of UTF-16 characters forming a "string" of textual data.
 */

/**
 * Repeat a string a number of times.
 * 
 * @param {Number} num The number of times to repeat
 * @param {String} separator An optional separator to insert in between the strings
 * @return {String} the repeated string
 */
String.prototype.repeat = function repeat(num, separator) {
	return Array.fill(typeof num === 'number' ? num : 1, this).join(separator||'');
};

/**
 * Expand a string repeating it up to a certain length.
 * 
 * @param {Number} len The length of the string to expand to
 * @return {String} the expanded string
 */
String.prototype.expand = function expand(len) {
	return this.repeat(Math.ceil(len / this.length)).substr(0, len);
};

/**
 * Return a version of this string with the first character in upper case
 * 
 * @return {String} The string with the modified case
 */
String.prototype.toFirstUpperCase = function toFirstUpperCase() {
	return this.charAt(0).toUpperCase() + this.substr(1);
};

/**
 * Return a version of this string with the first character in lower case
 * 
 * @return {String} The string with the modified case
 */
String.prototype.toFirstLowerCase = function toFirstLowerCase() {
	return this.charAt(0).toLowerCase() + this.substr(1);
};

/**
 * Return a version fo this string with all words matched by \w given an upper
 * case first character.
 * 
 * @return {String} The string with the modified case
 */
String.prototype.toTitleCase = function toTitleCase() {
	return this.replace(/\w+/g, function(m) { return m.toFirstUpperCase(); });
};

/**
 * Check and see if this string starts with another
 * 
 * @return {Boolean} A boolean indicating if this string starts with another
 */
String.prototype.startsWith = function startsWith(other) {
	return this.substr(0, other.length) === other;
};

/**
 * Check and see if this string ends with another
 * 
 * @return {Boolean} A boolean indicating if this string ends with another
 */
String.prototype.endsWith = function endsWith(other) {
	return this.substr(-other.length) === other;
};

/**
 * Check and see if this string contains another
 * 
 * @return {Boolean} A boolean indicating if this string contains another
 */
String.prototype.contains = function contains(other) {
	return this.indexOf(other) > -1;
};

/**
 * Count the number of times a substring is found within this string
 * 
 * @param {String} other The substring to search for
 * @param {Number} [offset=0] The offset from the start of the string for the search
 * @return {Number} An integer indicating how many times the substring is found
 */
String.prototype.numberOf = function numberOf(other, offset) {
	offset = offset || 0;
	var i, c = 0;
	while( (i = this.indexOf(other, offset)) >= 0 ) {
		c++;
		offset = i + other.length;
	}
	return c;
};

/**
 * Reverse the order of characters in this string
 * 
 * @return {String} A new string with characters in the reverse order
 */
String.prototype.reverse = function reverse() {
	return this.split('').reverse().join('');
};
/**
 * Trim all whitespace from the left side of the string
 * 
 * @return {String} The new trimmed string
 */

/**
 * Trim all whitespace from the left side of the string
 * 
 * @return {String} The new trimmed string
 */
if ( !String.prototype.trimLeft )
	String.prototype.trimLeft = function trimLeft() {
		return this.replace(/^\s\s*/, '');
	};

/**
 * Trim all whitespace from the right side of the string
 * 
 * @return {String} The new trimmed string
 */
if ( !String.prototype.trimRight )
	String.prototype.trimRight = function trimRight() {
		return this.replace(/\s*\s*$/, '');
	};

/**
 * Trim all whitespace from both sides of the string
 * 
 * @return {String} The new trimmed string
 */
if ( !String.prototype.trim )
	String.prototype.trim = function trim() {
		return this.trimLeft().trimRight();
	};


/**
 * Strip characters from both sides of the string
 * 
 * @param {String} chars The characters to remove
 * @return {String} The new stripped string
 */
String.prototype.strip = function strip(chars, internal) {
	if(!chars) throw new TypeError("Stripping requires a list of characters to strip");
	internal = internal || 3;
	// This creates a table where chars[char] will be truthy/falsey for inclusion
	var chars = Object.invert(typeof chars === 'string' ? chars.split('') : chars);
	
	var start = 0, end = this.length;
	
	if ( internal & 1 ) { // Left
		while( this.charAt(start) in chars )
			start++;
	}
	if ( internal & 2 ) { // Right
		while( this.charAt(end-1) in chars && end > start )
			end--;
	}
	
	return this.substring(start, end);
};

/**
 * Strip characters from the left side of the string
 * 
 * @param {String} chars The characters to remove
 * @return {String} The new stripped string
 */
String.prototype.stripLeft = function stripLeft(chars) {
	return this.strip(chars, 1);
};

/**
 * Strip characters from the right side of the string
 * 
 * @param {String} chars The characters to remove
 * @return {String} The new stripped string
 */
String.prototype.stripRight = function stripRight(chars) {
	return this.strip(chars, 2);
};

/**
 * Pad a string on both sides to a certain length
 * If the string is equal to or larger than that length then it's size will be left alone
 * 
 * @param {Number} len The length to pad the string to
 * @param {String} [chars=" "] The characters to pad the string with
 */
String.prototype.pad = function pad(len, chars) {
	chars = chars || ' ';
	var expand = Math.max(0, len - this.length);
	return chars.expand(Math.floor(expand/2)) + this + chars.expand(Math.ceil(expand/2));
};

/**
 * Pad a string on the left side to a certain length
 * If the string is equal to or larger than that length then it's size will be left alone
 * 
 * @param {Number} len The length to pad the string to
 * @param {String} [chars=" "] The characters to pad the string with
 */
String.prototype.padLeft = function padLeft(len, chars) {
	chars = chars || ' ';
	return chars.expand(Math.max(0, len - this.length)) + this;
};

/**
 * Pad a string on the right side to a certain length
 * If the string is equal to or larger than that length then it's size will be left alone
 * 
 * @param {Number} len The length to pad the string to
 * @param {String} [chars=" "] The characters to pad the string with
 */
String.prototype.padRight = function padRight(len, chars) {
	chars = chars || ' ';
	return this + chars.expand(Math.max(0, len - this.length));
};

/**
 * Partition a string. This breaks up a string by the first occurence of a separator
 * and returns a 3 item array containing the part before the separator,
 * the separator itself, and the part after the separator.
 * If the separator is not found then the returned array will contain the string
 * followed by two empty strings.
 * @see http://docs.python.org/library/stdtypes.html#str.partition
 * 
 * @param {String|RegExp} sep The separator to split by
 * @return {Array} The three item partitioned array
 */
String.prototype.partition = function partition(sep) {
	if ( sep instanceof RegExp ) {
		var m = this.match(m);
		var i = m ? m.index : -1;
		sep = m[0];
	} else {
		var i = this.indexOf(sep);
	}
	var l = sep.length;
	
	return i > -1 ?
		[ this.substr(0, i), sep, this.substr(i+l) ] :
		[ this.toString(), '', '' ];
};

/**
 * Same as string.partition but from the rightmost occurence of the separator
 * @see String#partition
 * 
 * @param {String} sep The separator to split by
 * @return {Array} The three item partitioned array
 */
String.prototype.partitionRight = function partitionRight(sep) {
	var i = this.lastIndexOf(sep);
	return i > -1 ?
		[ this.substr(0, i), sep, this.substr(i+sep.length) ] :
		[ '', '', this.toString() ];
};

/**
 * Split up a string by a separator.
 * This behaves the same as .split when used without a limit. The difference
 * between .split and .explode is that when used with a limit .split discards
 * anything over the limit, and .explode leaves them intact within the last string
 * 
 * "foo,bar,baz".split(',', 2); // ["foo","bar"]
 * "foo,bar,baz".explode(',', 2); // ["foo", "bar,baz"]
 * @param {String} sep The separator to split by
 * @param {Number} [limit=Infinity] The split limit
 * @return {Array} The slitted array
 */
String.prototype.explode = function explode(sep, limit) {
	if ( !limit )
		return this.split(sep);
	// Next version we will support RegExp.
	// Note that .split handles regex as if you always passed a /g to .match
	//if ( sep instanceof RegExp ) {
	//	sep = RegExp.rebuildFrom(sep, { global: true });
	//} else {
		var s = this.split(sep);
		var ss = s.splice(0, limit);
		if ( s.length ) {
			var sss = ss.pop();
			ss.push([sss].concat(s).join(sep));
		}
		return ss;
	//}
};

/**
 * Search through a string starting at an offset and collect all the matches
 * to that regex into an array.
 * Using a normal regex this returns an array of strings
 * If the regex contains groups then instead it returns an array of arrays
 * containing the groups.
 * 
 * @param {RegExp} regex Regular expression to scan over the string with
 * @param {Number} [offset=0] Offset to start at.
 * @return {Array} List of matches
 */
String.prototype.scan = function scan(regex, offset) {
	var m, list = [];
	offset = offset || 0;
	if ( regex.global ) {
		var str = this.substr(offset);
		while( m = regex.exec(str) )
			list.push( m.length > 1 ? m.slice(1) : m[0] );
	} else {
		while( m = this.substr(offset).match(regex) ) {
			offset += m.index + m[0].length;
			list.push( m.length > 1 ? m.slice(1) : m[0] );
		}
	}
	
	return list;
};

/**
 * Converts a dash (foo-bar) or underscore (foo_bar) style name into a
 * cammel case (fooBar) name.
 * 
 * @return {String} The new cammel case style string
 */
String.prototype.toCamelCase = function toCamelCase() {
	return this.replace(/[-_][a-z]/g, function(i) { return i.charAt(1).toUpperCase(); });
};

/**
 * Converts a cammel case (fooBar) name into an underscore (foo_bar) style name.
 * 
 * @return {String} The new underscore style string
 */
String.prototype.toUnderscore = function toUnderscore() {
	return this.replace(/[A-Z]/g, function(i) { return '_' + i.toLowerCase(); });
};

/**
 * Converts a cammel case (fooBar) name into a dash (foo-bar) style name.
 * 
 * @return {String} The new dash style string
 */
String.prototype.toDash = function toDash() {
	return this.replace(/[A-Z]/g, function(i) { return '-' + i.toLowerCase(); });
};

/**
 * @fileOverview This file contains Array compatibility prototypes for those introduced in JavaScript 1.6
 */

/**#@+
 * @native 1.6
 */

/**
 * Returns the first index at which a given element can be found in the array, or -1 if it is not present. 
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/indexOf
 * 
 * @name indexOf
 * @methodOf Array.prototype
 * 
 * @param elt Element to locate in the array
 * @param {Number} [from=0] The index at which to begin the search
 * @return {Number} The index of the first occurence of the element or -1 if not found
 */
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(elt /*, from*/) {
		var len = this.length >>> 0;
		
		var from = Number(arguments[1]) || 0;
		from = (from < 0) ? Math.ceil(from) : Math.floor(from);
		if (from < 0)
			from += len;

		for (; from < len; from++) {
			if (from in this && this[from] === elt)
				return from;
		}
		return -1;
	};
}

/**
 * Returns the last index at which a given element can be found in the array,
 * or -1 if it is not present. The array is searched backwards, starting at fromIndex.
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/lastIndexOf
 * 
 * @name lastIndexOf
 * @methodOf Array.prototype
 * 
 * @param elt Element to locate in the array
 * @param {Number} [from=0] The index at which to begin the search
 * @return {Number} The index of the last occurence of the element or -1 if not found
 */
if (!Array.prototype.lastIndexOf) {
	Array.prototype.lastIndexOf = function(elt /*, from*/) {
		var len = this.length;
		
		var from = Number(arguments[1]);
		if (isNaN(from)) {
			from = len - 1;
		} else {
			from = (from < 0) ? Math.ceil(from) : Math.floor(from);
			if (from < 0)
				from += len;
			else if (from >= len)
				from = len - 1;
		}
		
		for (; from > -1; from--) {
			if (from in this && this[from] === elt)
				return from;
		}
		return -1;
	};
}

/**
 * Tests whether all elements in the array pass the test implemented by the provided function. 
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/every
 * 
 * @name every
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to test for each element
 * @param [thisp] Object to use as this when executing callback
 * @return {Boolean} Boolean indicating whether all elements in the array passed or not
 */
if (!Array.prototype.every) {
	Array.prototype.every = function(fun /*, thisp*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this && !fun.call(thisp, this[i], i, this))
				return false;
		}
		
		return true;
	};
}

/**
 * Creates a new array with all elements that pass the test implemented by the provided function
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
 * 
 * @name filter
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to test each element of the array
 * @param [thisp] Object to use as this when executing callback
 * @return {Array} New array with all elements that passed
 */
if (!Array.prototype.filter) {
	Array.prototype.filter = function(fun /*, thisp*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		var res = new Array();
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this) {
				var val = this[i]; // in case fun mutates this
				if (fun.call(thisp, val, i, this))
					res.push(val);
			}
		}
		
		return res;
	};
}

/**
 * Executes a provided function once per array element
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach
 * 
 * @name forEach
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to execute for each element
 * @param [thisp] Object to use as this when executing callback
 */
if (!Array.prototype.forEach) {
	Array.prototype.forEach = function(fun /*, thisp*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this)
				fun.call(thisp, this[i], i, this);
		}
	};
}

/**
 * Creates a new array with the results of calling a provided function on every element in this array
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
 * 
 * @name map
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function that produces an element of the new Array from an element of the current one
 * @param [thisp] Object to use as this when executing callback
 * @return {Array} New array with the collected results
 */
if (!Array.prototype.map) {
	Array.prototype.map = function(fun /*, thisp*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		var res = new Array(len);
		var thisp = arguments[1];
		for (var i = 0; i < len; i++) {
			if (i in this)
				res[i] = fun.call(thisp, this[i], i, this);
		}
		
		return res;
	};
}

/**
 * Tests whether some element in the array passes the test implemented by the provided function.
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/some
 * 
 * @name some
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to test for each element
 * @param [thisp] Object to use as this when executing callback
 * @return {Boolean} Boolean indicating whether some element in the array passed or not
 */
if (!Array.prototype.some) {
	Array.prototype.some = function(fun /*, thisp*/) {
		var i = 0, len = this.length >>> 0;
		
		if (typeof fun != "function")
			throw new TypeError();
		
		var thisp = arguments[1];
		for (; i < len; i++) {
			if (i in this && fun.call(thisp, this[i], i, this))
				return true;
		}
		
		return false;
	};
}
/**#@-*/

/**
 * @fileOverview This file contains Array compatibility prototypes for those introduced in JavaScript 1.8
 */

/**#@+
 * @native 1.8
 */

/**
 * Apply a function against an accumulator and each value of the array (from left-to-right) as to reduce it to a single value.
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
 * 
 * @name reduce
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to execute on each value in the array
 * @param [initial] Object to use as the first argument to the first call of the callback
 * @return The final value created by the reduce
 */
if (!Array.prototype.reduce) {
	Array.prototype.reduce = function(fun /*, initial*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		// no value to return if no initial value and an empty array
		if (len == 0 && arguments.length == 1)
			throw new TypeError();
		
		var i = 0;
		if (arguments.length >= 2)
		{
			var rv = arguments[1];
		}
		else
		{
			do {
				if (i in this) {
					rv = this[i++];
					break;
				}
				
				// if array contains no values, no initial value to return
				if (++i >= len)
					throw new TypeError();
			}
			while (true);
		}
		
		for (; i < len; i++) {
			if (i in this)
				rv = fun.call(null, rv, this[i], i, this);
		}
		
		return rv;
	};
}

/**
 * Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value.
 * 
 * @see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
 * 
 * @name reduceRight
 * @methodOf Array.prototype
 * 
 * @param {Function} fun Function to execute on each value in the array
 * @param [initial] Object to use as the first argument to the first call of the callback
 * @return The final value created by the reduce
 */
if (!Array.prototype.reduceRight) {
	Array.prototype.reduceRight = function(fun /*, initial*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		
		// no value to return if no initial value, empty array
		if (len == 0 && arguments.length == 1)
			throw new TypeError();
		
		var i = len - 1;
		if (arguments.length >= 2) {
			var rv = arguments[1];
		} else {
			do {
				if (i in this) {
					rv = this[i--];
					break;
				}
				
				// if array contains no values, no initial value to return
				if (--i < 0)
					throw new TypeError();
			}
			while (true);
		}
		
		for (; i >= 0; i--) {
			if (i in this)
				rv = fun.call(null, rv, this[i], i, this);
		}
		
		return rv;
	};
}
/**#@-*/

/**
 * @fileOverview This file contains new methods for the Array class
 */
/**
 * @name Array
 * @class A built-in class for expressing ordered lists of values.
 */

/**
 * Create a new array of a specified length filled with a certain value
 * 
 * @constructs
 * @param {Number} size The size of the array to create
 * @param [value=undefined] The value to populate all the items in the array with
 */
Array.fill = function fill(size, value) {
	var arr = new Array(Number(size||0));
	for ( var i = arr.length-1; i >= 0; --i )
		arr[i] = value;
	return arr;
};

/**
 * Return a new array with the contents of this array repeated over
 * `num` amount of times.
 * 
 * @param {Number} num The number of times to repeat the array
 * @return {Array} The new repeated array
 */
Array.prototype.repeat = function repeat(num) {
	return Array.prototype.concat.apply([], Array.fill(num, this));
};

/**
 * Check if the array contains an item
 * 
 * @param {Number} item The item to look for
 * @return {Boolean} Whether or not the item was found within the array
 */
Array.prototype.has = function has(item) {
	return this.indexOf(item) > -1;
};

/**
 * Return an array where all duplicate items have been removed
 * 
 * @return {Array} A new array with a unique list of all items in this array
 */
Array.prototype.unique = function unique() {
	return this.filter(function(item, i, arr) { return arr.indexOf(item) >= i; });
};

/**
 * Shuffle the array
 * 
 * @return {Array} The same array for convenience
 */
Array.prototype.shuffle = function shuffle() {
	return this.sort(function() { return Math.random() > 0.5 ? 1 : -1; });
};

/**
 * Return an item from an index in the array
 * This is provided for client-side convenience so you have the same technique
 * for getting an item on both an array and a list of html nodes.
 * 
 * @param {Number} i The index to return the item from
 */
Array.prototype.item = function item(i) {
	return this[i];
};

/**
 * Remove the first (or more) occurrence(s) of an item from the array
 * 
 * @param item The item to remove
 * @param {Number} [max=1] The max number of items to remove, use Infinity to remove them all
 */
Array.prototype.remove = function remove(item, max) {
	if ( max === 0 )
		return;
	if ( max === -1 )
		max = Infinity;
	max = max || 1;
	while(max) {
		var i = this.indexOf(item);
		if( i < 0 ) break;
		this.splice(i, 1);
		max--;
	}
	// ToDo: Determine a reasonable return value
};

/**
 * Append a list of items from an array onto the end of this array
 * 
 * @param items Array The array of items to append to this array
 */
Array.prototype.append = function append(items) {
	return Array.prototype.push.apply( this, items );
};

/**
 * Clear an array of all items
 */
Array.prototype.clear = function clear() {
	return this.splice(0, this.length);
};

/**
 * Return a new array clean of all undefined and null values inside of an array
 * if false is passed to empty then only undefined items are cleaned
 * if true is passed to empty then empty strings will also be cleaned
 * 
 * @param {Boolean} [empty=undefined] Whether to also clean out empty strings or to not clear out nulls
 * @return {Array} The new array
 */
Array.prototype.clean = function clean(empty) {
	return this.filter(function(item) {
		if ( item === undefined ) return false;
		if ( empty !== false && item === null ) return false;
		if ( empty === true && item === "" ) return false;
		return true;
	});
};

/**
 * Return a random item from this array.
 * 
 * @return A random item from the array
 */
Array.prototype.rand = function() {
	return this[Math.rand(0, this.length-1)];
};

/**
 * A version of array.reduce() which only passes the two values to reduce on
 * to the callback so you can use native methods like Math.max inside a reduce.
 * 
 * @param {Function} fn The callback function
 */
Array.prototype.reduceNative = function reduceNative(fn) {
	return this.reduce(function(a, b) { return fn(a, b); });
};

/**
 * Returns a new version of this array which has been flattened
 * Flattening turns an array like [[1,2,3], [4,5,6], [7,8,9]];
 * into one like [1,2,3,4,5,6,7,8,9];
 * 
 * @return {Array} The new flattened array
 */
Array.prototype.flat = function flat() {
	var arr = [];
	function reduce(item) {
		if( item instanceof Array )
			item.forEach(reduce);
		else arr.push(item);
	};
	this.forEach(reduce);
	return arr;
};

/**
 * @fileOverview This file contains array diff and intersect methods for JS 1.5 to 1.7 (note that JsDoc does not index Array.new.js)
 */

/**
 * Compare this array with another one and return an array with all the items
 * in this one that are not in the other.
 * 
 * @param {Array} otherArray The array to compare to
 * @return {Array} An array with all items in this array not in otherArray
 */
Array.prototype.diff = function diff(otherArray) {
	return this.filter(function(item) {
		return !otherArray.has(item);
	});
};

/**
 * Compare this array with another one and return an array with all the items
 * that are in both arrays.
 * 
 * @param {Array} otherArray The array to compare to
 * @return {Array} An array with all items in this array and otherArray
 */
Array.prototype.intersect = function intersect(otherArray) {
	return this.filter(function(item) {
		return otherArray.has(item);
	});
};


/**
 * ES5: Returns the prototype for this instance
 * 
 * @see // http://ejohn.org/blog/objectgetprototypeof/#js-4
 * 
 * @name getPrototypeOf
 * @methodOf Object
 * 
 * @param {Object} object The object to get the protoype of
 * @return {Objedct} The object's prototype
 */
if (!Object.getPrototypeOf) {
	if ( typeof "test".__proto__ === "object" ) {
		Object.getPrototypeOf = function(object) {
			return object.__proto__;
		};
	} else {
		Object.getPrototypeOf = function(object){
			// May break if the constructor has been tampered with
			return object.constructor.prototype;
		};
	}
}

(function(toString) {
	
	this.isString = function isString(str) {
		return typeof str === 'string' || toString.call(str) === '[object String]';
	};

	this.isNumber = function isNumber(num) {
		return typeof num === 'number' || toString.call(num) === '[object Number]';
	};

	this.isArray = function isArray(arr) {
		return toString.call(arr) === '[object Array]';
	};

	this.isObject = function isObject(obj) {
		return toString.call(obj) === '[object Object]';
	};

	this.isFunction = function isFunction(fn) {
		return toString.call(fn) === '[object Function]';
	};
	
}).call(this, Object.prototype.toString);


