/*
http://www.JSON.org/json2.js
2011-02-23
*/

//(function () {
if (!JSON) {
	"use strict";
	var JSON = {
		cx : /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		escapable : /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
		meta : { // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
		gap : null,
		indent : null,
		rep : null,
		
		f : function (n) {
	        // Format integers to have at least two digits.
	        return n < 10 ? '0' + n : n;
	    },
	    
	    quote : function (string) {
	    	this.escapable.lastIndex = 0;
	        return this.escapable.test(string) ? '"' + string.replace(this.escapable, function (a) {
	            var c = this.meta[a];
	            return typeof c === 'string' ? c :
	                '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
	        }) + '"' : '"' + string + '"';
	    },
	    
	    str : function (key, holder) {
	    	var i, // The loop counter.
	            k, // The member key.
	            v, // The member value.
	            length,
	            mind = this.gap,
	            partial,
	            value = holder[key];
	
	        if (value && typeof value === 'object' &&
	                typeof value.toJSON === 'function') {
	            value = value.toJSON(key);
	        }
	
	        if (typeof this.rep === 'function') {
	            value = this.rep.call(holder, key, value);
	        }
	
	        switch (typeof value) {
		        case 'string':
		            return this.quote(value);
		        case 'number':
		        	return isFinite(value) ? String(value) : 'null';
		        case 'boolean':
		        case 'null':
		        	return String(value);
				case 'object':
					if (!value) {
		                return 'null';
		            }
		            this.gap += this.indent;
		            partial = [];
		            if (Object.prototype.toString.apply(value) === '[object Array]') {
		            	length = value.length;
		                for (i = 0; i < length; i += 1) {
		                    partial[i] = this.str(i, value) || 'null';
		                }
		                v = partial.length === 0 ? '[]' : this.gap ?
		                    '[\n' + this.gap + partial.join(',\n' + this.gap) + '\n' + mind + ']' :
		                    '[' + partial.join(',') + ']';
		                this.gap = mind;
		                return v;
		            }
		            
		            if (this.rep && typeof this.rep === 'object') {
		                length = this.rep.length;
		                for (i = 0; i < length; i += 1) {
		                    if (typeof this.rep[i] === 'string') {
		                        k = rep[i];
		                        v = this.str(k, value);
		                        if (v) {
		                            partial.push(this.quote(k) + (this.gap ? ': ' : ':') + v);
		                        }
		                    }
		                }
		            } else {
		            	for (k in value) {
		                    if (Object.prototype.hasOwnProperty.call(value, k)) {
		                        v = this.str(k, value);
		                        if (v) {
		                            partial.push(this.quote(k) + (this.gap ? ': ' : ':') + v);
		                        }
		                    }
		                }
		            }
		            
		            v = partial.length === 0 ? '{}' : this.gap ?
		                '{\n' + this.gap + partial.join(',\n' + this.gap) + '\n' + mind + '}' :
		                '{' + partial.join(',') + '}';
		            this.gap = mind;
		            return v;
	        }
	    }
	};

    if (typeof Date.prototype.toJSON !== 'function') {
    	Date.prototype.toJSON = function (key) {
    		return isFinite(this.valueOf()) ?
                this.getUTCFullYear() + '-' +
                JSON.f(this.getUTCMonth() + 1) + '-' +
                JSON.f(this.getUTCDate()) + 'T' +
                JSON.f(this.getUTCHours()) + ':' +
                JSON.f(this.getUTCMinutes()) + ':' +
                JSON.f(this.getUTCSeconds()) + 'Z' : null;
        };

        String.prototype.toJSON =
            Number.prototype.toJSON =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
        	var i;
            JSON.gap = '';
            JSON.indent = '';

			if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    JSON.indent += ' ';
                }
			} else if (typeof space === 'string') {
                JSON.indent = space;
            }
            
            JSON.rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            
            return JSON.str('', {'': value});
        };
    }
    
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
        	var j;

            function walk(holder, key) {
            	var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }

			text = String(text);
            this.cx.lastIndex = 0;
            if (this.cx.test(text)) {
                text = text.replace(this.cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

			if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
				
				j = eval('(' + text + ')');
				
				return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

            throw new SyntaxError('JSON.parse');
        };
    }
}
//}());

String.prototype.parseJSON = function () {
    try {
        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
            eval('(' + this + ')');
    } catch (e) {
        return false;
    }
};

/*if(typeof Object.prototype.toJSONString == 'undefined') {
    Object.prototype.toJSONString = function () {
        //return JSON.stringify(this);
        return JSON.stringify(JSON.decycle(this));
    };
}

if(typeof Array.prototype.toJSONString == 'undefined') {
    Array.prototype.toJSONString = function () {
        return JSON.stringify(this);
    };
}*/

/*if(typeof Object.prototype.toSource == 'undefined') {
  Object.prototype.toSource=function() {
    return JSON.stringify(this);
  }
}

if(typeof Array.prototype.toSource == 'undefined') {
  Array.prototype.toSource=function() {
    return JSON.stringify(this);
  }
}*/

if(typeof Array.prototype.push == 'undefined') {
	Array.prototype.push = function() {
		for( var i = 0, b = this.length, a = arguments, l = a.length; i<l; i++ ) {
			this[b+i] = a[i];
		}
		return this.length;
	}
}

/////////////////////////////////////////////////
/////////////////////////////////////////////////

if (typeof JSON.decycle !== 'function') {
    JSON.decycle = function decycle(object) {
        "use strict";

// Make a deep copy of an object or array, assuring that there is at most
// one instance of each object or array in the resulting structure. The
// duplicate references (which might be forming cycles) are replaced with
// an object of the form
// {$ref: PATH}
// where the PATH is a JSONPath string that locates the first occurance.
// So,
// var a = [];
// a[0] = a;
// return JSON.stringify(JSON.decycle(a));
// produces the string '[{"$ref":"$"}]'.

// JSONPath is used to locate the unique object. $ indicates the top level of
// the object or array. [NUMBER] or [STRING] indicates a child member or
// property.

        var objects = [], // Keep a reference to each unique object or array
            paths = []; // Keep the path to each unique object or array

        return (function derez(value, path) {

// The derez recurses through the object, producing the deep copy.

            var i, // The loop counter
                name, // Property name
                nu; // The new object or array

            switch (typeof value) {
            case 'object':

// typeof null === 'object', so get out if this value is not really an object.

                if (!value) {
                    return null;
                }

// If the value is an object or array, look to see if we have already
// encountered it. If so, return a $ref/path object. This is a hard way,
// linear search that will get slower as the number of unique objects grows.

                for (i = 0; i < objects.length; i += 1) {
                    if (objects[i] === value) {
                        return {$ref: paths[i]};
                    }
                }

// Otherwise, accumulate the unique value and its path.

                objects.push(value);
                paths.push(path);

// If it is an array, replicate the array.

                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    nu = [];
                    for (i = 0; i < value.length; i += 1) {
                        nu[i] = derez(value[i], path + '[' + i + ']');
                    }
                } else {

// If it is an object, replicate the object.

                    nu = {};
                    for (name in value) {
                        if (Object.prototype.hasOwnProperty.call(value, name)) {
                            nu[name] = derez(value[name],
                                path + '[' + JSON.stringify(name) + ']');
                        }
                    }
                }
                return nu;
            case 'number':
            case 'string':
            case 'boolean':
                return value;
            }
        }(object, '$'));
    };
}


if (typeof JSON.retrocycle !== 'function') {
    JSON.retrocycle = function retrocycle($) {
        "use strict";

// Restore an object that was reduced by decycle. Members whose values are
// objects of the form
// {$ref: PATH}
// are replaced with references to the value found by the PATH. This will
// restore cycles. The object will be mutated.

// The eval function is used to locate the values described by a PATH. The
// root object is kept in a $ variable. A regular expression is used to
// assure that the PATH is extremely well formed. The regexp contains nested
// * quantifiers. That has been known to have extremely bad performance
// problems on some browsers for very long strings. A PATH is expected to be
// reasonably short. A PATH is allowed to belong to a very restricted subset of
// Goessner's JSONPath.

// So,
// var s = '[{"$ref":"$"}]';
// return JSON.retrocycle(JSON.parse(s));
// produces an array containing a single element which is the array itself.

        var px =
            /^\$(?:\[(?:\d?|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/;

        (function rez(value) {

// The rez function walks recursively through the object looking for $ref
// properties. When it finds one that has a value that is a path, then it
// replaces the $ref object with a reference to the value that is found by
// the path.

            var i, item, name, path;

            if (value && typeof value === 'object') {
                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    for (i = 0; i < value.length; i += 1) {
                        item = value[i];
                        if (item && typeof item === 'object') {
                            path = item.$ref;
                            if (typeof path === 'string' && px.test(path)) {
                                value[i] = eval(path);
                            } else {
                                rez(item);
                            }
                        }
                    }
                } else {
                    for (name in value) {
                        if (typeof value[name] === 'object') {
                            item = value[name];
                            if (item) {
                                path = item.$ref;
                                if (typeof path === 'string' && px.test(path)) {
                                    value[name] = eval(path);
                                } else {
                                    rez(item);
                                }
                            }
                        }
                    }
                }
            }
        }($));
        return $;
    };
}
