Date.prototype.toJSON = function () {
    function f(n) {
        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');
};


JSON = (function () {

    function defaultFilter(baseObj, key) {
        var result;

        if (typeof key === 'string') {
            if (!Object.prototype.hasOwnProperty.call(baseObj, key)) {
                return undefined;
            }
        } else if (typeof key === 'number') {
            if (!(baseObj instanceof Array)) {
                return undefined;
            }
        } else {
            return undefined;
        }
        result = baseObj[key];
        if (result && typeof result.toJSON === 'function') {
            return result.toJSON();
        } else {
            return result;
        }
    }
    
    /** m is a table of character substitutions. */
    var m = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
    };

    return {
        defaultFilter: defaultFilter,

        serialize: function(value, optFilter) {
            var out = []; // array holding partial texts
            // var stack = []; // for diagnosing cycles
            var filter = optFilter || defaultFilter;

            /**
             * The internal recursive serialization function.
             */
            function serialize(value) {
                var i,j; // loop counters
                var len; // array lengths;
                var needComma = false;
                var k,v; // property key and value
                
                // stack.push(value);
                
                switch (typeof value) {
                case 'object':
                    if (value === null) {
                        out.push('null');
                        
                    } else if (value instanceof Array) {
                        len = value.length;
                        out.push('[');
                        for (i = 0; i < len; i += 1) {
                            v = filter(value, i);
                            if (v !== undefined) {
                                if (needComma) {
                                    out.push(',');
                                } else {
                                    needComma = true;
                                }
                                serialize(v);
                            }
                        }
                        out.push(']');
                        
                    } else {
                        out.push('{');
                        for (k in value) {
                            v = filter(value, k);
                            if (v !== undefined) {
                                if (needComma) {
                                    out.push(',');
                                } else {
                                    needComma = true;
                                }
                                serialize(k);
                                out.push(':');
                                serialize(v);
                            }
                        }
                        out.push('}');
                    }
                    break;
                    
                case 'string':
                    // 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(value)) { //"])){
                        out.push('"' + 
                                 value.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);
                        }) + '"');
                    } else {
                        out.push('"' + value + '"');
                    }
                    break;

                case 'number':
                    // JSON numbers must be finite. Encode non-finite numbers
                    // as null. 
                    out.push(isFinite(value) ? String(value) : 'null');
                    break;

                case 'boolean':
                    out.push(String(value));
                    break;

                default:
                    out.push('null');
                }
                // stack.pop();
            }

            var fakeRoot = [value];
            serialize(filter(fakeRoot, 0));
            return out.join('');
        },

        unserialize: function(str, optFilter) {

            var result;
            
            function walk(value) {
                var i,len,k,v;

                if (value && typeof value === 'object') {
                    if (value instanceof Array) {
                        len = value.length;
                        for (i = 0; i < len; i += 1) {
                            walk(value[i]);
                            v = optFilter(value, i);
                            if (v === undefined) {
                                delete value[i];
                            } else {
                                value[i] = v;
                            }
                        }
                    } else {
                        for (k in value) {
                            walk(value[k]);
                            v = optFilter(value, k);
                            if (v === undefined) {
                                delete value[k];
                            } else {
                                value[k] = v;
                            }
                        }
                    }
                }
                
            }

            if ((/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).
                test(str.
                     replace((/\\./g), '@').
                     replace((/"[^"\\\n\r]*"/g), ''))) { //"))) {
                result = eval('(' + str + ')');
                if (optFilter) {
                    var fakeRoot = [result];
                    walk(fakeRoot);
                    return fakeRoot[0];
                } else {
                    return result;
                }
            }
            throw new SyntaxError('parseJSON');
        }
    };
})();

