function getJSONType(value) {
	if(!value) {
		return "null";
	}
	
	if(typeof value != "object") {
		return typeof value;
	}
	
	if(value.constructor == Array) {
		return "array";
	}
	
	if(value.constructor == Date) {
		return "date";
	}
	
	return "object";
}

function toJSONString(value) {
	if(value === 0) {
		return 0;
	}
	
	if(getJSONType(value) == "null") {
		return "null";
	}
	
	if(getJSONType(value) == "array") {
		return arrayToJSONString(value);
	}
	
	if(getJSONType(value) == "boolean") {
		return String(value);
	}
	
	if(getJSONType(value) == "date") {
		return dateToJSONString(value);
	}
	
	if(getJSONType(value) == "number") {
		return isFinite(value) ? String(value) : "null";
	}
	
	if(getJSONType(value) == "object") {
		return objectToJSONString(value);
	}
	
	if(getJSONType(value) == "string") {
		return stringToJSONString(value);
	}
}

function arrayToJSONString(value) {
	var output = ['['];
	var listSizeGreaterThanOne;
	var length = value.length;
	for(var i=0; i < length; i++) {
		var element = value[i];
		var type = typeof element;
		if(type == "undefined" || type == "function" || type == "unknown") {
			break;
		}
		
		if(listSizeGreaterThanOne) {
			output.push(",");
		}
		
		output.push(element === null ? "null" : toJSONString(element));
		listSizeGreaterThanOne = true;
	}
	
	output.push(']');
	return output.join('');
}

function dateToJSONString(value) {
	function fix(n) {
		return n < 10 ? '0' + n : n;
	}
	
	return '"' + value.getFullYear() + '-' +
		fix(value.getMonth() + 1) + '-' +
		fix(value.getDate()) + 'T' +
		fix(value.getHours()) + ':' +
		fix(value.getMinutes()) + ':' +
		fix(value.getSeconds()) + '"';
}

function objectToJSONString(value) {
	var output = ['{'];
	var arraySizeGreaterThanOne;
	
	for(key in value) {
		if(value.hasOwnProperty(key)) {
			var element = value[key];
			var type = typeof element;
			if(type == "undefined" || type == "function" || type == "unknown") {
				break;
			}
			
			if(arraySizeGreaterThanOne) {
				output.push(',');
			}
			
			output.push(toJSONString(key), ':', (element === null ? "null" : toJSONString(element)));
			arraySizeGreaterThanOne = true;
		}
	}
	
	output.push('}');
	return output.join('');
}

function stringToJSONString(value) {
	var m = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
	};

	if (/["\\\x00-\x1f]/.test(value)) {
        return '"' + value.replace(/([\x00-\x1f\\"])/g, function(a, b) {
            var c = m[b];
            if (c) {
                return c;
            }
            c = b.charCodeAt();
            return '\\u00' +
                Math.floor(c / 16).toString(16) +
                (c % 16).toString(16);
        }) + '"';
    }
    return '"' + value + '"';
}

String.prototype.parseJSON = function () {
    try {
		// If we're dealing with WebKit, don't run the regular expression.
		// TODO: The bug in question has been fixed in a recent version of
		// WebKit. Find out what version and test for it.
		if(navigator.userAgent.indexOf("AppleWebKit") != -1) {
			return eval("(" + this + ")");
		} else if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)) {
			return eval("(" + this + ")");
        }
    } catch (e) {
    }
	throw new SyntaxError("parseJSON");
};


