]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/json/json.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / json / json.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('json-parse', function(Y) {
9
10 /**
11  * <p>The JSON module adds support for serializing JavaScript objects into
12  * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
13  *
14  * <p>The JSON namespace is added to your YUI instance including static methods
15  * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
16  *
17  * <p>The functionality and method signatures follow the ECMAScript 5
18  * specification.  In browsers with native JSON support, the native
19  * implementation is used.</p>
20  *
21  * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
22  * <code>json-stringify</code>.</p>
23  * 
24  * <p>As their names suggest, <code>json-parse</code> adds support for parsing
25  * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
26  * JavaScript data into JSON strings (Y.JSON.stringify).  You may choose to
27  * include either of the submodules individually if you don't need the
28  * complementary functionality, or include the rollup for both.</p>
29  *
30  * @module json
31  * @class JSON
32  * @static
33  */
34
35 /**
36  * Provides Y.JSON.parse method to accept JSON strings and return native
37  * JavaScript objects.
38  *
39  * @module json
40  * @submodule json-parse
41  * @for JSON
42  * @static
43  */
44
45
46 // All internals kept private for security reasons
47 function fromGlobal(ref) {
48     return (Y.config.win || this || {})[ref];
49 }
50
51
52     /**
53      * Alias to native browser implementation of the JSON object if available.
54      *
55      * @property Native
56      * @type {Object}
57      * @private
58      */
59 var _JSON  = fromGlobal('JSON'),
60     // Create an indirect reference to eval to allow for minification
61     _eval  = fromGlobal('eval'),
62     Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
63     useNative = !!Native,
64
65     /**
66      * Replace certain Unicode characters that JavaScript may handle incorrectly
67      * during eval--either by deleting them or treating them as line
68      * endings--with escape sequences.
69      * IMPORTANT NOTE: This regex will be used to modify the input if a match is
70      * found.
71      *
72      * @property _UNICODE_EXCEPTIONS
73      * @type {RegExp}
74      * @private
75      */
76     _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
77
78
79     /**
80      * First step in the safety evaluation.  Regex used to replace all escape
81      * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
82      *
83      * @property _ESCAPES
84      * @type {RegExp}
85      * @private
86      */
87     _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
88
89     /**
90      * Second step in the safety evaluation.  Regex used to replace all simple
91      * values with ']' characters.
92      *
93      * @property _VALUES
94      * @type {RegExp}
95      * @private
96      */
97     _VALUES  = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
98
99     /**
100      * Third step in the safety evaluation.  Regex used to remove all open
101      * square brackets following a colon, comma, or at the beginning of the
102      * string.
103      *
104      * @property _BRACKETS
105      * @type {RegExp}
106      * @private
107      */
108     _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
109
110     /**
111      * Final step in the safety evaluation.  Regex used to test the string left
112      * after all previous replacements for invalid characters.
113      *
114      * @property _UNSAFE
115      * @type {RegExp}
116      * @private
117      */
118     _UNSAFE = /[^\],:{}\s]/,
119     
120     /**
121      * Replaces specific unicode characters with their appropriate \unnnn
122      * format. Some browsers ignore certain characters during eval.
123      *
124      * @method escapeException
125      * @param c {String} Unicode character
126      * @return {String} the \unnnn escapement of the character
127      * @private
128      */
129     _escapeException = function (c) {
130         return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
131     },
132
133     /**
134      * Traverses nested objects, applying a reviver function to each (key,value)
135      * from the scope if the key:value's containing object.  The value returned
136      * from the function will replace the original value in the key:value pair.
137      * If the value returned is undefined, the key will be omitted from the
138      * returned object.
139      *
140      * @method _revive
141      * @param data {MIXED} Any JavaScript data
142      * @param reviver {Function} filter or mutation function
143      * @return {MIXED} The results of the filtered data
144      * @private
145      */
146     _revive = function (data, reviver) {
147         var walk = function (o,key) {
148             var k,v,value = o[key];
149             if (value && typeof value === 'object') {
150                 for (k in value) {
151                     if (value.hasOwnProperty(k)) {
152                         v = walk(value, k);
153                         if (v === undefined) {
154                             delete value[k];
155                         } else {
156                             value[k] = v;
157                         }
158                     }
159                 }
160             }
161             return reviver.call(o,key,value);
162         };
163
164         return typeof reviver === 'function' ? walk({'':data},'') : data;
165     },
166
167     /**
168      * Parse a JSON string, returning the native JavaScript representation.
169      *
170      * @param s {string} JSON string data
171      * @param reviver {function} (optional) function(k,v) passed each key value
172      *          pair of object literals, allowing pruning or altering values
173      * @return {MIXED} the native JavaScript representation of the JSON string
174      * @throws SyntaxError
175      * @method parse
176      * @static
177      */
178     // JavaScript implementation in lieu of native browser support.  Based on
179     // the json2.js library from http://json.org
180     _parse = function (s,reviver) {
181         // Replace certain Unicode characters that are otherwise handled
182         // incorrectly by some browser implementations.
183         // NOTE: This modifies the input if such characters are found!
184         s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
185         
186         // Test for any remaining invalid characters
187         if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
188                             replace(_VALUES,']').
189                             replace(_BRACKETS,''))) {
190
191             // Eval the text into a JavaScript data structure, apply any
192             // reviver function, and return
193             return _revive( _eval('(' + s + ')'), reviver );
194         }
195
196         throw new SyntaxError('JSON.parse');
197     };
198     
199 Y.namespace('JSON').parse = function (s,reviver) {
200         if (typeof s !== 'string') {
201             s += '';
202         }
203
204         return Native && Y.JSON.useNativeParse ?
205             Native.parse(s,reviver) : _parse(s,reviver);
206 };
207
208 function workingNative( k, v ) {
209     return k === "ok" ? true : v;
210 }
211
212 // Double check basic functionality.  This is mainly to catch early broken
213 // implementations of the JSON API in Firefox 3.1 beta1 and beta2
214 if ( Native ) {
215     try {
216         useNative = ( Native.parse( '{"ok":false}', workingNative ) ).ok;
217     }
218     catch ( e ) {
219         useNative = false;
220     }
221 }
222
223 /**
224  * Leverage native JSON parse if the browser has a native implementation.
225  * In general, this is a good idea.  See the Known Issues section in the
226  * JSON user guide for caveats.  The default value is true for browsers with
227  * native JSON support.
228  *
229  * @property useNativeParse
230  * @type Boolean
231  * @default true
232  * @static
233  */
234 Y.JSON.useNativeParse = useNative;
235
236
237 }, '3.3.0' );
238 YUI.add('json-stringify', function(Y) {
239
240 /**
241  * Provides Y.JSON.stringify method for converting objects to JSON strings.
242  *
243  * @module json
244  * @submodule json-stringify
245  * @for JSON
246  * @static
247  */
248 var _JSON     = (Y.config.win || {}).JSON,
249     Lang      = Y.Lang,
250     isFunction= Lang.isFunction,
251     isObject  = Lang.isObject,
252     isArray   = Lang.isArray,
253     _toStr    = Object.prototype.toString,
254     Native    = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
255     useNative = !!Native,
256     UNDEFINED = 'undefined',
257     OBJECT    = 'object',
258     NULL      = 'null',
259     STRING    = 'string',
260     NUMBER    = 'number',
261     BOOLEAN   = 'boolean',
262     DATE      = 'date',
263     _allowable= {
264         'undefined'        : UNDEFINED,
265         'string'           : STRING,
266         '[object String]'  : STRING,
267         'number'           : NUMBER,
268         '[object Number]'  : NUMBER,
269         'boolean'          : BOOLEAN,
270         '[object Boolean]' : BOOLEAN,
271         '[object Date]'    : DATE,
272         '[object RegExp]'  : OBJECT
273     },
274     EMPTY     = '',
275     OPEN_O    = '{',
276     CLOSE_O   = '}',
277     OPEN_A    = '[',
278     CLOSE_A   = ']',
279     COMMA     = ',',
280     COMMA_CR  = ",\n",
281     CR        = "\n",
282     COLON     = ':',
283     COLON_SP  = ': ',
284     QUOTE     = '"',
285
286     // Regex used to capture characters that need escaping before enclosing
287     // their containing string in quotes.
288     _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
289
290     // Character substitution map for common escapes and special characters.
291     _CHARS = {
292         '\b': '\\b',
293         '\t': '\\t',
294         '\n': '\\n',
295         '\f': '\\f',
296         '\r': '\\r',
297         '"' : '\\"',
298         '\\': '\\\\'
299     };
300
301
302 // Utility function used to determine how to serialize a variable.
303 function _type(o) {
304     var t = typeof o;
305     return  _allowable[t] ||              // number, string, boolean, undefined
306             _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
307             (t === OBJECT ?
308                 (o ? OBJECT : NULL) :     // object, array, null, misc natives
309                 UNDEFINED);               // function, unknown
310 }
311
312 // Escapes a special character to a safe Unicode representation
313 function _char(c) {
314     if (!_CHARS[c]) {
315         _CHARS[c] =  '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
316     }
317     return _CHARS[c];
318 }
319
320 // Enclose escaped strings in quotes
321 function _string(s) {
322     return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
323 }
324
325 // Adds the provided space to the beginning of every line in the input string
326 function _indent(s,space) {
327     return s.replace(/^/gm, space);
328 }
329
330 // JavaScript implementation of stringify (see API declaration of stringify)
331 function _stringify(o,w,space) {
332     if (o === undefined) {
333         return undefined;
334     }
335
336     var replacer = isFunction(w) ? w : null,
337         format   = _toStr.call(space).match(/String|Number/) || [],
338         _date    = Y.JSON.dateToString,
339         stack    = [],
340         tmp,i,len;
341
342     if (replacer || !isArray(w)) {
343         w = undefined;
344     }
345
346     // Ensure whitelist keys are unique (bug 2110391)
347     if (w) {
348         tmp = {};
349         for (i = 0, len = w.length; i < len; ++i) {
350             tmp[w[i]] = true;
351         }
352         w = tmp;
353     }
354
355     // Per the spec, strings are truncated to 10 characters and numbers
356     // are converted to that number of spaces (max 10)
357     space = format[0] === 'Number' ?
358                 new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
359                 (space || EMPTY).slice(0,10);
360
361     function _serialize(h,key) {
362         var value = h[key],
363             t     = _type(value),
364             a     = [],
365             colon = space ? COLON_SP : COLON,
366             arr, i, keys, k, v;
367
368         // Per the ECMA 5 spec, toJSON is applied before the replacer is
369         // called.  Also per the spec, Date.prototype.toJSON has been added, so
370         // Date instances should be serialized prior to exposure to the
371         // replacer.  I disagree with this decision, but the spec is the spec.
372         if (isObject(value) && isFunction(value.toJSON)) {
373             value = value.toJSON(key);
374         } else if (t === DATE) {
375             value = _date(value);
376         }
377
378         if (isFunction(replacer)) {
379             value = replacer.call(h,key,value);
380         }
381
382         if (value !== h[key]) {
383             t = _type(value);
384         }
385
386         switch (t) {
387             case DATE    : // intentional fallthrough.  Pre-replacer Dates are
388                            // serialized in the toJSON stage.  Dates here would
389                            // have been produced by the replacer.
390             case OBJECT  : break;
391             case STRING  : return _string(value);
392             case NUMBER  : return isFinite(value) ? value+EMPTY : NULL;
393             case BOOLEAN : return value+EMPTY;
394             case NULL    : return NULL;
395             default      : return undefined;
396         }
397
398         // Check for cyclical references in nested objects
399         for (i = stack.length - 1; i >= 0; --i) {
400             if (stack[i] === value) {
401                 throw new Error("JSON.stringify. Cyclical reference");
402             }
403         }
404
405         arr = isArray(value);
406
407         // Add the object to the processing stack
408         stack.push(value);
409
410         if (arr) { // Array
411             for (i = value.length - 1; i >= 0; --i) {
412                 a[i] = _serialize(value, i) || NULL;
413             }
414         } else {   // Object
415             // If whitelist provided, take only those keys
416             keys = w || value;
417             i = 0;
418
419             for (k in keys) {
420                 if (keys.hasOwnProperty(k)) {
421                     v = _serialize(value, k);
422                     if (v) {
423                         a[i++] = _string(k) + colon + v;
424                     }
425                 }
426             }
427         }
428
429         // remove the array from the stack
430         stack.pop();
431
432         if (space && a.length) {
433             return arr ?
434                 OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
435                 OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
436         } else {
437             return arr ?
438                 OPEN_A + a.join(COMMA) + CLOSE_A :
439                 OPEN_O + a.join(COMMA) + CLOSE_O;
440         }
441     }
442
443     // process the input
444     return _serialize({'':o},'');
445 }
446
447 // Double check basic native functionality.  This is primarily to catch broken
448 // early JSON API implementations in Firefox 3.1 beta1 and beta2.
449 if ( Native ) {
450     try {
451         useNative = ( '0' === Native.stringify(0) );
452     } catch ( e ) {
453         useNative = false;
454     }
455 }
456
457 Y.mix(Y.namespace('JSON'),{
458     /**
459      * Leverage native JSON stringify if the browser has a native
460      * implementation.  In general, this is a good idea.  See the Known Issues
461      * section in the JSON user guide for caveats.  The default value is true
462      * for browsers with native JSON support.
463      *
464      * @property JSON.useNativeStringify
465      * @type Boolean
466      * @default true
467      * @static
468      */
469     useNativeStringify : useNative,
470
471     /**
472      * Serializes a Date instance as a UTC date string.  Used internally by
473      * stringify.  Override this method if you need Dates serialized in a
474      * different format.
475      *
476      * @method dateToString
477      * @param d {Date} The Date to serialize
478      * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
479      * @deprecated Use a replacer function
480      * @static
481      */
482     dateToString : function (d) {
483         function _zeroPad(v) {
484             return v < 10 ? '0' + v : v;
485         }
486
487         return d.getUTCFullYear()           + '-' +
488               _zeroPad(d.getUTCMonth() + 1) + '-' +
489               _zeroPad(d.getUTCDate())      + 'T' +
490               _zeroPad(d.getUTCHours())     + COLON +
491               _zeroPad(d.getUTCMinutes())   + COLON +
492               _zeroPad(d.getUTCSeconds())   + 'Z';
493     },
494
495     /**
496      * <p>Converts an arbitrary value to a JSON string representation.</p>
497      *
498      * <p>Objects with cyclical references will trigger an exception.</p>
499      *
500      * <p>If a whitelist is provided, only matching object keys will be
501      * included.  Alternately, a replacer function may be passed as the
502      * second parameter.  This function is executed on every value in the
503      * input, and its return value will be used in place of the original value.
504      * This is useful to serialize specialized objects or class instances.</p>
505      *
506      * <p>If a positive integer or non-empty string is passed as the third
507      * parameter, the output will be formatted with carriage returns and
508      * indentation for readability.  If a String is passed (such as "\t") it
509      * will be used once for each indentation level.  If a number is passed,
510      * that number of spaces will be used.</p>
511      *
512      * @method stringify
513      * @param o {MIXED} any arbitrary value to convert to JSON string
514      * @param w {Array|Function} (optional) whitelist of acceptable object
515      *                  keys to include, or a replacer function to modify the
516      *                  raw value before serialization
517      * @param ind {Number|String} (optional) indentation character or depth of
518      *                  spaces to format the output.
519      * @return {string} JSON string representation of the input
520      * @static
521      */
522     stringify : function (o,w,ind) {
523         return Native && Y.JSON.useNativeStringify ?
524             Native.stringify(o,w,ind) : _stringify(o,w,ind);
525     }
526 });
527
528
529 }, '3.3.0' );
530
531
532 YUI.add('json', function(Y){}, '3.3.0' ,{use:['json-parse', 'json-stringify']});
533