// Augment the basic prototypes if they have not already been augmented.

if(!Object.prototype.toJSONString)
{
	Array.prototype.toJSONString = function(w)
	{
		var a = [],     // The array holding the partial texts.
		i,          // Loop counter.
		l = this.length,
		v;          // The value to be stringified.

		// For each value in this array...

		for (i = 0; i < l; i += 1)
		{
			v = this[i];
			switch (typeof v)
			{
				case 'object':
					// Serialize a JavaScript object value. Treat objects thats lack the
					// toJSONString method as null. Due to a specification error in ECMAScript,
					// typeof null is 'object', so watch out for that case.
					if (v && typeof v.toJSONString === 'function')
						a.push(v.toJSONString(w));
					else
						a.push('null');
					break;
				case 'string':
				case 'number':
				case 'boolean':
					a.push(v.toJSONString());
					break;
				default:
					a.push('null');
					break;
			}
		}
		// Join all of the member texts together and wrap them in brackets.
		return '[' + a.join(',') + ']';
	};

	Boolean.prototype.toJSONString = function ()
	{
		return String(this);
	};

	Date.prototype.toJSONString = function ()
	{
		// Eventually, this method will be based on the date.toISOString method.
		function f(n)
		{
			// Format integers to have at least two digits.
			return n < 10 ? '0' + n : n;
		}

		return '"' + this.getUTCFullYear()   + '-' +
			f(this.getUTCMonth() + 1) + '-' +
			f(this.getUTCDate())      + 'T' +
			f(this.getUTCHours())     + ':' +
			f(this.getUTCMinutes())   + ':' +
			f(this.getUTCSeconds())   + 'Z"';
	};

	Number.prototype.toJSONString = function ()
	{
		// JSON numbers must be finite. Encode non-finite numbers as null.
		return isFinite(this) ? String(this) : 'null';
	};

	Object.prototype.toJSONString = function (w)
	{
		var a = [],     // The array holding the partial texts.
		k,          // The current key.
		i,          // The loop counter.
		v;          // The current value.
		// If a whitelist (array of keys) is provided, use it assemble the components
		// of the object.

		if(w)
		{
			for (i = 0; i < w.length; i += 1)
			{
				k = w[i];   
				if (typeof k !== 'string') continue;

				v = this[k];
				switch (typeof v)
				{
					case 'object':
						// Serialize a JavaScript object value. Ignore objects that lack the
						// toJSONString method. Due to a specification error in ECMAScript,
						// typeof null is 'object', so watch out for that case.
						if(v)
						{
							if (typeof v.toJSONString === 'function')
								a.push(k.toJSONString() + ':' +	v.toJSONString(w));
						}
						else
							a.push(k.toJSONString() + ':null');
						break;
					case 'string':
					case 'number':
					case 'boolean':
						a.push(k.toJSONString() + ':' + v.toJSONString());
						break;
						// Values without a JSON representation are ignored.
					default:
						break;
				}
			}
		}
		else
		{
			// Iterate through all of the keys in the object, ignoring the proto chain
			// and keys that are not strings.
			for (k in this)
			{
				if(typeof k !== 'string' || !Object.prototype.hasOwnProperty.apply(this,[k])) continue;

				v = this[k];
				switch (typeof v)
				{
					case 'object':
						// Serialize a JavaScript object value. Ignore objects that lack the
						// toJSONString method. Due to a specification error in ECMAScript,
						// typeof null is 'object', so watch out for that case.
						if(v)
						{
							if (typeof v.toJSONString === 'function')
								a.push(k.toJSONString() + ':' +	v.toJSONString());
						}
						else
							a.push(k.toJSONString() + ':null');
						break;
					case 'string':
					case 'number':
					case 'boolean':
						a.push(k.toJSONString() + ':' + v.toJSONString());
						break;
						// Values without a JSON representation are ignored.
					default:
						break;
				}
			}
		}
		// Join all of the member texts together and wrap them in braces.
		return '{' + a.join(',') + '}';
	};

	(function (s)
	{
		// Augment String.prototype. We do this in an immediate anonymous function to
		// avoid defining global variables.

		// m is a table of character substitutions.
		var m = {'\b': '\\b',
					'\t': '\\t',
					'\n': '\\n',
					'\f': '\\f',
					'\r': '\\r',
					'"' : '\\"',
					'\\': '\\\\'};

		s.parseJSON = function (filter)
		{
			var j;

			function walk(k, v)
			{
				var i, n;
				if (v && typeof v === 'object')
				{
					for (i in v)
					{
						if(!Object.prototype.hasOwnProperty.apply(v,[i])) continue;
						
						n = walk(i, v[i]);
						if (n !== undefined)
							v[i] = n;
					}
				}
				return filter(k, v);
			}

			// Parsing happens in three stages. In the first stage, we run the text against
			// a regular expression which looks for non-JSON characters. We are especially
			// concerned with '()' and 'new' because they can cause invocation, and '='
			// because it can cause mutation. But just to be safe, we will reject all
			// unexpected characters.

			// We split the first stage into 4 regexp operations in order to work around
			// crippling deficiencies in IE's and Safari's regexp engines. First we replace
			// all backslash pairs with '@' (a non-JSON character). Second, we replace all
			// simple value tokens with ']' characters. Third, we delete all open brackets
			// that follow a colon or comma or that begin the text. Finally, we look to see
			// that the remaining characters are only whitespace or ']' or ',' or ':' or '{'
			// or '}'. If that is so, then the text is safe for eval.

			if (/^[\],:{}\s]*$/.test(this.replace(/\\./g, '@').
				replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
					replace(/(?:^|:|,)(?:\s*\[)+/g, '')))
			{
				// In the second stage we use the eval function to compile the text into a
				// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
				// in JavaScript: it can begin a block or an object literal. We wrap the text
				// in parens to eliminate the ambiguity.
				j = eval('(' + this + ')');
				// In the optional third stage, we recursively walk the new structure, passing
				// each name/value pair to a filter function for possible transformation.
				return typeof filter === 'function' ? walk('', j) : j;
			}
			// If the text is not JSON parseable, then a SyntaxError is thrown.
			throw new SyntaxError('parseJSON');
		};

		s.toJSONString = function ()
		{
			// If the string contains no control characters, no quote characters, and no
			// backslash characters, then we can simply slap some quotes around it.
			// Otherwise we must also replace the offending characters with safe
			// sequences.
			if(/["\\\x00-\x1f]/.test(this))
			{
				return '"' + this.replace(/[\x00-\x1f\\"]/g, function (a)
				{
					var c = m[a];
					if(c) return c;
					c = a.charCodeAt();
					return '\\u00' + Math.floor(c / 16).toString(16) +	(c % 16).toString(16);}
				) + '"';
			}
			return '"' + this + '"';
		};
	})(String.prototype);
}