/**
 * jQuery JSON Plugin v2.3-edge (2011-09-25)
 *
 * @author Brantley Harris, 2009-2011
 * @author Timo Tijhof, 2011
 * @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
 *         copyrighted 2005 by Bob Ippolito.
 * @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
 *         website's http://www.json.org/json2.js, which proclaims:
 *         "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 *         I uphold.
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
 */

(function( $ ) {

        var     escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
                meta = {
                        '\b': '\\b',
                        '\t': '\\t',
                        '\n': '\\n',
                        '\f': '\\f',
                        '\r': '\\r',
                        '"' : '\\"',
                        '\\': '\\\\'
                },
                hasOwn = Object.prototype.hasOwnProperty;

        /**
         * jQuery.toJSON
         * Converts the given argument into a JSON respresentation.
         *
         * @param o {Mixed} The json-serializble *thing* to be converted
         *
         * If an object has a toJSON prototype, that will be used to get the representation.
         * Non-integer/string keys are skipped in the object, as are keys that point to a
         * function.
         *
         */
        $.toJSON = typeof JSON === 'object' && JSON.stringify
                ? JSON.stringify
                : function( o ) {

                if ( o === null ) {
                        return 'null';
                }

                var type = typeof o;

                if ( type === 'undefined' ) {
                        return undefined;
                }
                if ( type === 'number' || type === 'boolean' ) {
                        return '' + o;
                }
                if ( type === 'string') {
                        return $.quoteString( o );
                }
                if ( type === 'object' ) {
                        if ( typeof o.toJSON === 'function' ) {
                                return $.toJSON( o.toJSON() );
                        }
                        if ( o.constructor === Date ) {
                                var     month = o.getUTCMonth() + 1,
                                        day = o.getUTCDate(),
                                        year = o.getUTCFullYear(),
                                        hours = o.getUTCHours(),
                                        minutes = o.getUTCMinutes(),
                                        seconds = o.getUTCSeconds(),
                                        milli = o.getUTCMilliseconds();

                                if ( month < 10 ) {
                                        month = '0' + month;
                                }
                                if ( day < 10 ) {
                                        day = '0' + day;
                                }
                                if ( hours < 10 ) {
                                        hours = '0' + hours;
                                }
                                if ( minutes < 10 ) {
                                        minutes = '0' + minutes;
                                }
                                if ( seconds < 10 ) {
                                        seconds = '0' + seconds;
                                }
                                if ( milli < 100 ) {
                                        milli = '0' + milli;
                                }
                                if ( milli < 10 ) {
                                        milli = '0' + milli;
                                }
                                return '"' + year + '-' + month + '-' + day + 'T' +
                                        hours + ':' + minutes + ':' + seconds +
                                        '.' + milli + 'Z"';
                        }
                        if ( o.constructor === Array ) {
                                var ret = [];
                                for ( var i = 0; i < o.length; i++ ) {
                                        ret.push( $.toJSON( o[i] ) || 'null' );
                                }
                                return '[' + ret.join(',') + ']';
                        }
                        var     name,
                                val,
                                pairs = [];
                        for ( var k in o ) {
                                // Only include own properties,
                                // Filter out inherited prototypes
                                if ( !hasOwn.call( o, k ) ) {
                                        continue;
                                }

                                // Keys must be numerical or string. Skip others
                                type = typeof k;
                                if ( type === 'number' ) {
                                        name = '"' + k + '"';
                                } else if (type === 'string') {
                                        name = $.quoteString(k);
                                } else {
                                        continue;
                                }
                                type = typeof o[k];

                                // Invalid values like these return undefined
                                // from toJSON, however those object members
                                // shouldn't be included in the JSON string at all.
                                if ( type === 'function' || type === 'undefined' ) {
                                        continue;
                                }
                                val = $.toJSON( o[k] );
                                pairs.push( name + ':' + val );
                        }
                        return '{' + pairs.join( ',' ) + '}';
                }
        };

        /**
         * jQuery.evalJSON
         * Evaluates a given piece of json source.
         *
         * @param src {String}
         */
        $.evalJSON = typeof JSON === 'object' && JSON.parse
                ? JSON.parse
                : function( src ) {
                return eval('(' + src + ')');
        };

        /**
         * jQuery.secureEvalJSON
         * Evals JSON in a way that is *more* secure.
         *
         * @param src {String}
         */
        $.secureEvalJSON = typeof JSON === 'object' && JSON.parse
                ? JSON.parse
                : function( src ) {

                var filtered = 
                        src
                        .replace( /\\["\\\/bfnrtu]/g, '@' )
                        .replace( /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace( /(?:^|:|,)(?:\s*\[)+/g, '');

                if ( /^[\],:{}\s]*$/.test( filtered ) ) {
                        return eval( '(' + src + ')' );
                } else {
                        throw new SyntaxError( 'Error parsing JSON, source is not valid.' );
                }
        };

        /**
         * jQuery.quoteString
         * Returns a string-repr of a string, escaping quotes intelligently.
         * Mostly a support function for toJSON.
         * Examples:
         * >>> jQuery.quoteString('apple')
         * "apple"
         *
         * >>> jQuery.quoteString('"Where are we going?", she asked.')
         * "\"Where are we going?\", she asked."
         */
        $.quoteString = function( string ) {
                if ( string.match( escapeable ) ) {
                        return '"' + string.replace( escapeable, function( a ) {
                                var c = meta[a];
                                if ( typeof c === 'string' ) {
                                        return c;
                                }
                                c = a.charCodeAt();
                                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
                        }) + '"';
                }
                return '"' + string + '"';
        };

})( jQuery );
