PageRenderTime 67ms CodeModel.GetById 46ms app.highlight 15ms RepoModel.GetById 2ms app.codeStats 0ms

/src/main/resources/org/apache/struts2/static/json/json-debug.js

http://struts2yuiplugin.googlecode.com/
JavaScript | 396 lines | 328 code | 7 blank | 61 comment | 0 complexity | 0ebd899673df1d2177739ebc0094c831 MD5 | raw file
  1/*
  2Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3Code licensed under the BSD License:
  4http://developer.yahoo.net/yui/license.txt
  5version: 2.7.0
  6*/
  7/**
  8 * Provides methods to parse JSON strings and convert objects to JSON strings.
  9 * @module json
 10 * @class JSON
 11 * @static
 12 */
 13YAHOO.lang.JSON = (function () {
 14
 15var l = YAHOO.lang,
 16
 17    /**
 18     * Replace certain Unicode characters that JavaScript may handle incorrectly
 19     * during eval--either by deleting them or treating them as line
 20     * endings--with escape sequences.
 21     * IMPORTANT NOTE: This regex will be used to modify the input if a match is
 22     * found.
 23     * @property _UNICODE_EXCEPTIONS
 24     * @type {RegExp}
 25     * @private
 26     */
 27    _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
 28
 29    /**
 30     * First step in the validation.  Regex used to replace all escape
 31     * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
 32     * @property _ESCAPES
 33     * @type {RegExp}
 34     * @static
 35     * @private
 36     */
 37    _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
 38
 39    /**
 40     * Second step in the validation.  Regex used to replace all simple
 41     * values with ']' characters.
 42     * @property _VALUES
 43     * @type {RegExp}
 44     * @static
 45     * @private
 46     */
 47    _VALUES  = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
 48
 49    /**
 50     * Third step in the validation.  Regex used to remove all open square
 51     * brackets following a colon, comma, or at the beginning of the string.
 52     * @property _BRACKETS
 53     * @type {RegExp}
 54     * @static
 55     * @private
 56     */
 57    _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
 58
 59    /**
 60     * Final step in the validation.  Regex used to test the string left after
 61     * all previous replacements for invalid characters.
 62     * @property _INVALID
 63     * @type {RegExp}
 64     * @static
 65     * @private
 66     */
 67    _INVALID  = /^[\],:{}\s]*$/,
 68
 69    /**
 70     * Regex used to replace special characters in strings for JSON
 71     * stringification.
 72     * @property _SPECIAL_CHARS
 73     * @type {RegExp}
 74     * @static
 75     * @private
 76     */
 77    _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
 78
 79    /**
 80     * Character substitution map for common escapes and special characters.
 81     * @property _CHARS
 82     * @type {Object}
 83     * @static
 84     * @private
 85     */
 86    _CHARS = {
 87        '\b': '\\b',
 88        '\t': '\\t',
 89        '\n': '\\n',
 90        '\f': '\\f',
 91        '\r': '\\r',
 92        '"' : '\\"',
 93        '\\': '\\\\'
 94    };
 95
 96/**
 97 * Traverses nested objects, applying a filter or reviver function to
 98 * each value.  The value returned from the function will replace the
 99 * original value in the key:value pair.  If the value returned is
100 * undefined, the key will be omitted from the returned object.
101 * @method _revive
102 * @param data {MIXED} Any JavaScript data
103 * @param reviver {Function} filter or mutation function
104 * @return {MIXED} The results of the filtered/mutated data structure
105 * @private
106 */
107function _revive(data, reviver) {
108    var walk = function (o,key) {
109        var k,v,value = o[key];
110        if (value && typeof value === 'object') {
111            for (k in value) {
112                if (l.hasOwnProperty(value,k)) {
113                    v = walk(value, k);
114                    if (v === undefined) {
115                        delete value[k];
116                    } else {
117                        value[k] = v;
118                    }
119                }
120            }
121        }
122        return reviver.call(o,key,value);
123    };
124
125    return typeof reviver === 'function' ? walk({'':data},'') : data;
126}
127
128/**
129 * Escapes a special character to a safe Unicode representation
130 * @method _char
131 * @param c {String} single character to escape
132 * @return {String} safe Unicode escape
133 */
134function _char(c) {
135    if (!_CHARS[c]) {
136        _CHARS[c] =  '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
137    }
138    return _CHARS[c];
139}
140
141/**
142 * Replace certain Unicode characters that may be handled incorrectly by
143 * some browser implementations.
144 * @method _prepare
145 * @param s {String} parse input
146 * @return {String} sanitized JSON string ready to be validated/parsed
147 * @private
148 */
149function _prepare(s) {
150    return s.replace(_UNICODE_EXCEPTIONS, _char);
151}
152
153/**
154 * Four step determination whether a string is valid JSON.  In three steps,
155 * escape sequences, safe values, and properly placed open square brackets
156 * are replaced with placeholders or removed.  Then in the final step, the
157 * result of all these replacements is checked for invalid characters.
158 * @method _isValid
159 * @param str {String} JSON string to be tested
160 * @return {boolean} is the string safe for eval?
161 * @static
162 */
163function _isValid(str) {
164    return l.isString(str) &&
165            _INVALID.test(str.
166            replace(_ESCAPES,'@').
167            replace(_VALUES,']').
168            replace(_BRACKETS,''));
169}
170
171/**
172 * Enclose escaped strings in quotes
173 * @method _string
174 * @param s {String} string to wrap
175 * @return {String} '"'+s+'"' after s has had special characters escaped
176 * @private
177 */
178function _string(s) {
179    return '"' + s.replace(_SPECIAL_CHARS, _char) + '"';
180}
181
182/**
183 * Worker function used by public stringify.
184 * @method _stringify
185 * @param h {Object} object holding the key
186 * @param key {String} String key in object h to serialize
187 * @param depth {Number} depth to serialize
188 * @param w {Array|Function} array of whitelisted keys OR replacer function
189 * @param pstack {Array} used to protect against recursion
190 * @return {String} serialized version of o
191 */
192function _stringify(h,key,d,w,pstack) {
193    var o = typeof w === 'function' ? w.call(h,key,h[key]) : h[key],
194        i,len,j, // array iteration
195        k,v,     // object iteration
196        isArray, // forking in typeof 'object'
197        a;       // composition array for performance over string concat
198
199    if (o instanceof Date) {
200        o = l.JSON.dateToString(o);
201    } else if (o instanceof String || o instanceof Boolean || o instanceof Number) {
202        o = o.valueOf();
203    }
204
205    switch (typeof o) {
206        case 'string' : return _string(o);
207        case 'number' : return isFinite(o) ? String(o) : 'null';
208        case 'boolean': return String(o);
209        case 'object' :
210            // null
211            if (o === null) {
212                return 'null';
213            }
214
215            // Check for cyclical references
216            for (i = pstack.length - 1; i >= 0; --i) {
217                if (pstack[i] === o) {
218                    return 'null';
219                }
220            }
221
222            // Add the object to the processing stack
223            pstack[pstack.length] = o;
224
225            a = [];
226            isArray = l.isArray(o);
227
228            // Only recurse if we're above depth config
229            if (d > 0) {
230                // Array
231                if (isArray) {
232                    for (i = o.length - 1; i >= 0; --i) {
233                        a[i] = _stringify(o,i,d-1,w,pstack) || 'null';
234                    }
235
236                // Object
237                } else {
238                    j = 0;
239                    // Use whitelist keys if provided as an array
240                    if (l.isArray(w)) {
241                        for (i = 0, len = w.length; i < len; ++i) {
242                            k = w[i];
243                            v = _stringify(o,k,d-1,w,pstack);
244                            if (v) {
245                                a[j++] = _string(k) + ':' + v;
246                            }
247                        }
248                    } else {
249                        for (k in o) {
250                            if (typeof k === 'string' && l.hasOwnProperty(o,k)) {
251                                v = _stringify(o,k,d-1,w,pstack);
252                                if (v) {
253                                    a[j++] = _string(k) + ':' + v;
254                                }
255                            }
256                        }
257                    }
258
259                    // sort object keys for easier readability
260                    a.sort();
261                }
262            }
263
264            // remove the object from the stack
265            pstack.pop();
266
267            return isArray ? '['+a.join(',')+']' : '{'+a.join(',')+'}';
268    }
269
270    return undefined; // invalid input
271}
272
273// Return the public API
274return {
275    /**
276     * Four step determination whether a string is valid JSON.  In three steps,
277     * escape sequences, safe values, and properly placed open square brackets
278     * are replaced with placeholders or removed.  Then in the final step, the
279     * result of all these replacements is checked for invalid characters.
280     * @method isValid
281     * @param str {String} JSON string to be tested
282     * @return {boolean} is the string safe for eval?
283     * @static
284     */
285    isValid : function (s) {
286        return _isValid(_prepare(s));
287    },
288
289    /**
290     * Parse a JSON string, returning the native JavaScript representation.
291     * Only minor modifications from http://www.json.org/json2.js.
292     * @param s {string} JSON string data
293     * @param reviver {function} (optional) function(k,v) passed each key:value
294     *          pair of object literals, allowing pruning or altering values
295     * @return {MIXED} the native JavaScript representation of the JSON string
296     * @throws SyntaxError
297     * @method parse
298     * @static
299     */
300    parse : function (s,reviver) {
301        // sanitize
302        s = _prepare(s);
303
304        // Ensure valid JSON
305        if (_isValid(s)) {
306            // Eval the text into a JavaScript data structure, apply the
307            // reviver function if provided, and return
308            return _revive( eval('(' + s + ')'), reviver );
309        }
310
311        // The text is not valid JSON
312        throw new SyntaxError('parseJSON');
313    },
314
315    /**
316     * Converts an arbitrary value to a JSON string representation.
317     * Cyclical object or array references are replaced with null.
318     * If a whitelist is provided, only matching object keys will be included.
319     * If a depth limit is provided, objects and arrays at that depth will
320     * be stringified as empty.
321     * @method stringify
322     * @param o {MIXED} any arbitrary object to convert to JSON string
323     * @param w {Array|Function} (optional) whitelist of acceptable object keys to include OR a function(value,key) to alter values before serialization
324     * @param d {number} (optional) depth limit to recurse objects/arrays (practical minimum 1)
325     * @return {string} JSON string representation of the input
326     * @static
327     */
328    stringify : function (o,w,d) {
329        if (o !== undefined) {
330            // Ensure whitelist keys are unique (bug 2110391)
331            if (l.isArray(w)) {
332                w = (function (a) {
333                    var uniq=[],map={},v,i,j,len;
334                    for (i=0,j=0,len=a.length; i<len; ++i) {
335                        v = a[i];
336                        if (typeof v === 'string' && map[v] === undefined) {
337                            uniq[(map[v] = j++)] = v;
338                        }
339                    }
340                    return uniq;
341                })(w);
342            }
343
344            // Default depth to POSITIVE_INFINITY
345            d = d >= 0 ? d : 1/0;
346
347            // process the input
348            return _stringify({'':o},'',d,w,[]);
349        }
350
351        return undefined;
352    },
353
354    /**
355     * Serializes a Date instance as a UTC date string.  Used internally by
356     * stringify.  Override this method if you need Dates serialized in a
357     * different format.
358     * @method dateToString
359     * @param d {Date} The Date to serialize
360     * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
361     * @static
362     */
363    dateToString : function (d) {
364        function _zeroPad(v) {
365            return v < 10 ? '0' + v : v;
366        }
367
368        return d.getUTCFullYear()         + '-' +
369            _zeroPad(d.getUTCMonth() + 1) + '-' +
370            _zeroPad(d.getUTCDate())      + 'T' +
371            _zeroPad(d.getUTCHours())     + ':' +
372            _zeroPad(d.getUTCMinutes())   + ':' +
373            _zeroPad(d.getUTCSeconds())   + 'Z';
374    },
375
376    /**
377     * Reconstitute Date instances from the default JSON UTC serialization.
378     * Reference this from a reviver function to rebuild Dates during the
379     * parse operation.
380     * @method stringToDate
381     * @param str {String} String serialization of a Date
382     * @return {Date}
383     */
384    stringToDate : function (str) {
385        if (/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.test(str)) {
386            var d = new Date();
387            d.setUTCFullYear(RegExp.$1, (RegExp.$2|0)-1, RegExp.$3);
388            d.setUTCHours(RegExp.$4, RegExp.$5, RegExp.$6);
389            return d;
390        }
391        return str;
392    }
393};
394
395})();
396YAHOO.register("json", YAHOO.lang.JSON, {version: "2.7.0", build: "1799"});