src/cm/media/js/lib/yui/yui3.0.0/build/json/json-stringify.js
changeset 0 40c8f766c9b8
equal deleted inserted replaced
-1:000000000000 0:40c8f766c9b8
       
     1 /*
       
     2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
       
     3 Code licensed under the BSD License:
       
     4 http://developer.yahoo.net/yui/license.txt
       
     5 version: 3.0.0
       
     6 build: 1549
       
     7 */
       
     8 YUI.add('json-stringify', function(Y) {
       
     9 
       
    10 /**
       
    11  * Provides Y.JSON.stringify method for converting objects to JSON strings.
       
    12  *
       
    13  * @module json
       
    14  * @submodule json-stringify
       
    15  * @for JSON
       
    16  * @static
       
    17  */
       
    18 var _JSON     = Y.config.win.JSON,
       
    19     Lang      = Y.Lang,
       
    20     isFunction= Lang.isFunction,
       
    21     isObject  = Lang.isObject,
       
    22     isArray   = Lang.isArray,
       
    23     _toStr    = Object.prototype.toString,
       
    24     Native    = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
       
    25     UNDEFINED = 'undefined',
       
    26     OBJECT    = 'object',
       
    27     NULL      = 'null',
       
    28     STRING    = 'string',
       
    29     NUMBER    = 'number',
       
    30     BOOLEAN   = 'boolean',
       
    31     DATE      = 'date',
       
    32     _allowable= {
       
    33         'undefined'        : UNDEFINED,
       
    34         'string'           : STRING,
       
    35         '[object String]'  : STRING,
       
    36         'number'           : NUMBER,
       
    37         '[object Number]'  : NUMBER,
       
    38         'boolean'          : BOOLEAN,
       
    39         '[object Boolean]' : BOOLEAN,
       
    40         '[object Date]'    : DATE,
       
    41         '[object RegExp]'  : OBJECT
       
    42     },
       
    43     EMPTY     = '',
       
    44     OPEN_O    = '{',
       
    45     CLOSE_O   = '}',
       
    46     OPEN_A    = '[',
       
    47     CLOSE_A   = ']',
       
    48     COMMA     = ',',
       
    49     COMMA_CR  = ",\n",
       
    50     CR        = "\n",
       
    51     COLON     = ':',
       
    52     COLON_SP  = ': ',
       
    53     QUOTE     = '"',
       
    54     // Regex used to capture characters that need escaping before enclosing
       
    55     // their containing string in quotes.
       
    56     _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
       
    57     // Character substitution map for common escapes and special characters.
       
    58     _CHARS = {
       
    59         '\b': '\\b',
       
    60         '\t': '\\t',
       
    61         '\n': '\\n',
       
    62         '\f': '\\f',
       
    63         '\r': '\\r',
       
    64         '"' : '\\"',
       
    65         '\\': '\\\\'
       
    66     };
       
    67 
       
    68 
       
    69 // Utility function used to determine how to serialize a variable.
       
    70 function _type(o) {
       
    71     var t = typeof o;
       
    72     return  _allowable[t] ||              // number, string, boolean, undefined
       
    73             _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
       
    74             (t === OBJECT ?
       
    75                 (o ? OBJECT : NULL) :     // object, array, null, misc natives
       
    76                 UNDEFINED);               // function, unknown
       
    77 }
       
    78 
       
    79 // Escapes a special character to a safe Unicode representation
       
    80 function _char(c) {
       
    81     if (!_CHARS[c]) {
       
    82         _CHARS[c] =  '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
       
    83     }
       
    84     return _CHARS[c];
       
    85 }
       
    86 
       
    87 // Enclose escaped strings in quotes
       
    88 function _string(s) {
       
    89     return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
       
    90 }
       
    91 
       
    92 // Adds the provided space to the beginning of every line in the input string
       
    93 function _indent(s,space) {
       
    94     return s.replace(/^/gm, space);
       
    95 }
       
    96 
       
    97 // JavaScript implementation of stringify (see API declaration of stringify)
       
    98 function _stringify(o,w,space) {
       
    99     if (o === undefined) {
       
   100         return undefined;
       
   101     }
       
   102 
       
   103     var replacer = isFunction(w) ? w : null,
       
   104         format   = _toStr.call(space).match(/String|Number/) || [],
       
   105         _date    = Y.JSON.dateToString,
       
   106         stack    = [],
       
   107         tmp,i,len;
       
   108 
       
   109     if (replacer || !isArray(w)) {
       
   110         w = undefined;
       
   111     }
       
   112 
       
   113     // Ensure whitelist keys are unique (bug 2110391)
       
   114     if (w) {
       
   115         tmp = {};
       
   116         for (i = 0, len = w.length; i < len; ++i) {
       
   117             tmp[w[i]] = true;
       
   118         }
       
   119         w = tmp;
       
   120     }
       
   121 
       
   122     // Per the spec, strings are truncated to 10 characters and numbers
       
   123     // are converted to that number of spaces (max 10)
       
   124     space = format[0] === 'Number' ?
       
   125                 new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
       
   126                 (space || EMPTY).slice(0,10);
       
   127 
       
   128     function _serialize(h,key) {
       
   129         var value = h[key],
       
   130             t     = _type(value),
       
   131             a     = [],
       
   132             colon = space ? COLON_SP : COLON,
       
   133             arr, i, keys, k, v;
       
   134 
       
   135         // Per the ECMA 5 spec, toJSON is applied before the replacer is
       
   136         // called.  Also per the spec, Date.prototype.toJSON has been added, so
       
   137         // Date instances should be serialized prior to exposure to the
       
   138         // replacer.  I disagree with this decision, but the spec is the spec.
       
   139         if (isObject(value) && isFunction(value.toJSON)) {
       
   140             value = value.toJSON(key);
       
   141         } else if (t === DATE) {
       
   142             value = _date(value);
       
   143         }
       
   144 
       
   145         if (isFunction(replacer)) {
       
   146             value = replacer.call(h,key,value);
       
   147         }
       
   148 
       
   149         if (value !== h[key]) {
       
   150             t = _type(value);
       
   151         }
       
   152 
       
   153         switch (t) {
       
   154             case DATE    : // intentional fallthrough.  Pre-replacer Dates are
       
   155                            // serialized in the toJSON stage.  Dates here would
       
   156                            // have been produced by the replacer.
       
   157             case OBJECT  : break;
       
   158             case STRING  : return _string(value);
       
   159             case NUMBER  : return isFinite(value) ? value+EMPTY : NULL;
       
   160             case BOOLEAN : return value+EMPTY;
       
   161             case NULL    : return NULL;
       
   162             default      : return undefined;
       
   163         }
       
   164 
       
   165         // Check for cyclical references in nested objects
       
   166         for (i = stack.length - 1; i >= 0; --i) {
       
   167             if (stack[i] === value) {
       
   168                 throw new Error("JSON.stringify. Cyclical reference");
       
   169             }
       
   170         }
       
   171 
       
   172         arr = isArray(value);
       
   173 
       
   174         // Add the object to the processing stack
       
   175         stack.push(value);
       
   176 
       
   177         if (arr) { // Array
       
   178             for (i = value.length - 1; i >= 0; --i) {
       
   179                 a[i] = _serialize(value, i) || NULL;
       
   180             }
       
   181         } else {   // Object
       
   182             // If whitelist provided, take only those keys
       
   183             keys = w || value;
       
   184             i = 0;
       
   185 
       
   186             for (k in keys) {
       
   187                 if (keys.hasOwnProperty(k)) {
       
   188                     v = _serialize(value, k);
       
   189                     if (v) {
       
   190                         a[i++] = _string(k) + colon + v;
       
   191                     }
       
   192                 }
       
   193             }
       
   194         }
       
   195 
       
   196         // remove the array from the stack
       
   197         stack.pop();
       
   198 
       
   199         if (space && a.length) {
       
   200             return arr ?
       
   201                 OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
       
   202                 OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
       
   203         } else {
       
   204             return arr ?
       
   205                 OPEN_A + a.join(COMMA) + CLOSE_A :
       
   206                 OPEN_O + a.join(COMMA) + CLOSE_O;
       
   207         }
       
   208     }
       
   209 
       
   210     // process the input
       
   211     return _serialize({'':o},'');
       
   212 }
       
   213 
       
   214 Y.mix(Y.namespace('JSON'),{
       
   215     /**
       
   216      * Leverage native JSON stringify if the browser has a native
       
   217      * implementation.  In general, this is a good idea.  See the Known Issues
       
   218      * section in the JSON user guide for caveats.  The default value is true
       
   219      * for browsers with native JSON support.
       
   220      *
       
   221      * @property JSON.useNativeStringify
       
   222      * @type Boolean
       
   223      * @default true
       
   224      * @static
       
   225      */
       
   226     useNativeStringify : !!Native,
       
   227 
       
   228     /**
       
   229      * Serializes a Date instance as a UTC date string.  Used internally by
       
   230      * stringify.  Override this method if you need Dates serialized in a
       
   231      * different format.
       
   232      *
       
   233      * @method dateToString
       
   234      * @param d {Date} The Date to serialize
       
   235      * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
       
   236      * @static
       
   237      */
       
   238     dateToString : function (d) {
       
   239         function _zeroPad(v) {
       
   240             return v < 10 ? '0' + v : v;
       
   241         }
       
   242 
       
   243         return d.getUTCFullYear()           + '-' +
       
   244               _zeroPad(d.getUTCMonth() + 1) + '-' +
       
   245               _zeroPad(d.getUTCDate())      + 'T' +
       
   246               _zeroPad(d.getUTCHours())     + COLON +
       
   247               _zeroPad(d.getUTCMinutes())   + COLON +
       
   248               _zeroPad(d.getUTCSeconds())   + 'Z';
       
   249     },
       
   250 
       
   251     /**
       
   252      * <p>Converts an arbitrary value to a JSON string representation.</p>
       
   253      *
       
   254      * <p>Objects with cyclical references will trigger an exception.</p>
       
   255      *
       
   256      * <p>If a whitelist is provided, only matching object keys will be
       
   257      * included.  Alternately, a replacer function may be passed as the
       
   258      * second parameter.  This function is executed on every value in the
       
   259      * input, and its return value will be used in place of the original value.
       
   260      * This is useful to serialize specialized objects or class instances.</p>
       
   261      *
       
   262      * <p>If a positive integer or non-empty string is passed as the third
       
   263      * parameter, the output will be formatted with carriage returns and
       
   264      * indentation for readability.  If a String is passed (such as "\t") it
       
   265      * will be used once for each indentation level.  If a number is passed,
       
   266      * that number of spaces will be used.</p>
       
   267      *
       
   268      * @method stringify
       
   269      * @param o {MIXED} any arbitrary value to convert to JSON string
       
   270      * @param w {Array|Function} (optional) whitelist of acceptable object
       
   271      *                  keys to include, or a replacer function to modify the
       
   272      *                  raw value before serialization
       
   273      * @param ind {Number|String} (optional) indentation character or depth of
       
   274      *                  spaces to format the output.
       
   275      * @return {string} JSON string representation of the input
       
   276      * @static
       
   277      */
       
   278     stringify : function (o,w,ind) {
       
   279         return Native && Y.JSON.useNativeStringify ?
       
   280             Native.stringify(o,w,ind) : _stringify(o,w,ind);
       
   281     }
       
   282 });
       
   283 
       
   284 
       
   285 }, '3.0.0' );