1 /*
  2     http://www.JSON.org/json2.js
  3     2008-06-04
  4 
  5     Public Domain.
  6 
  7     NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  8 
  9     See http://www.JSON.org/js.html
 10 
 11     This file creates a global JSON object containing two methods: stringify
 12     and parse.
 13 
 14 
 15         JSON.stringify(value, replacer, space, linebreak)
 16             value       any JavaScript value, usually an object or array.
 17 
 18             replacer    an optional parameter that determines how object
 19                         values are stringified for objects without a toJSON
 20                         method. It can be a function or an array.
 21 
 22             space       an optional parameter that specifies the indentation
 23                         of nested structures. If it is omitted, the text will
 24                         be packed without extra whitespace. If it is a number,
 25                         it will specify the number of spaces to indent at each
 26                         level. If it is a string (such as '\t' or ' '),
 27                         it contains the characters used to indent at each level.
 28 
 29             linebreak   an optional parameter that specifies the text used to
 30                         break lines, such as '<br>' or '\r\n'. It is used with
 31                         the space parameter. The default is '\n'.
 32 
 33             This method produces a JSON text from a JavaScript value.
 34 
 35             When an object value is found, if the object contains a toJSON
 36             method, its toJSON method will be called and the result will be
 37             stringified. A toJSON method does not serialize: it returns the
 38             value represented by the name/value pair that should be serialized,
 39             or undefined if nothing should be serialized. The toJSON method
 40             will be passed the key associated with the value, and this will be
 41             bound to the object holding the key.
 42 
 43             For example, this would serialize Dates as ISO strings.
 44 
 45                 Date.prototype.toJSON = function (key) {
 46                     function f(n) {
 47                         // Format integers to have at least two digits.
 48                         return n < 10 ? '0' + n : n;
 49                     }
 50 
 51                     return this.getUTCFullYear()   + '-' +
 52                          f(this.getUTCMonth() + 1) + '-' +
 53                          f(this.getUTCDate())      + 'T' +
 54                          f(this.getUTCHours())     + ':' +
 55                          f(this.getUTCMinutes())   + ':' +
 56                          f(this.getUTCSeconds())   + 'Z';
 57                 };
 58 
 59             You can provide an optional replacer method. It will be passed the
 60             key and value of each member, with this bound to the containing
 61             object. The value that is returned from your method will be
 62             serialized. If your method returns undefined, then the member will
 63             be excluded from the serialization.
 64 
 65             If the replacer parameter is an array, then it will be used to
 66             select the members to be serialized. It filters the results such
 67             that only members with keys listed in the replacer array are
 68             stringified.
 69 
 70             Values that do not have JSON representaions, such as undefined or
 71             functions, will not be serialized. Such values in objects will be
 72             dropped; in arrays they will be replaced with null. You can use
 73             a replacer function to replace those with JSON values.
 74             JSON.stringify(undefined) returns undefined.
 75 
 76             The optional space parameter produces a stringification of the
 77             value that is filled with line breaks and indentation to make it
 78             easier to read.
 79 
 80             If the space parameter is a non-empty string, then that string will
 81             be used for indentation. If the space parameter is a number, then
 82             then indentation will be that many spaces.
 83 
 84             Example:
 85 
 86             text = JSON.stringify(['e', {pluribus: 'unum'}]);
 87             // text is '["e",{"pluribus":"unum"}]'
 88 
 89 
 90             text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
 91             // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
 92 
 93             text = JSON.stringify([new Date()], function (key, value) {
 94                 return this[key] instanceof Date ?
 95                     'Date(' + this[key] + ')' : value;
 96             });
 97             // text is '["Date(---current time---)"]'
 98 
 99 
100         JSON.parse(text, reviver)
101             This method parses a JSON text to produce an object or array.
102             It can throw a SyntaxError exception.
103 
104             The optional reviver parameter is a function that can filter and
105             transform the results. It receives each of the keys and values,
106             and its return value is used instead of the original value.
107             If it returns what it received, then the structure is not modified.
108             If it returns undefined then the member is deleted.
109 
110             Example:
111 
112             // Parse the text. Values that look like ISO date strings will
113             // be converted to Date objects.
114 
115             myData = JSON.parse(text, function (key, value) {
116                 var a;
117                 if (typeof value === 'string') {
118                     a =
119 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
120                     if (a) {
121                         return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
122                             +a[5], +a[6]));
123                     }
124                 }
125                 return value;
126             });
127 
128             myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
129                 var d;
130                 if (typeof value === 'string' &&
131                         value.slice(0, 5) === 'Date(' &&
132                         value.slice(-1) === ')') {
133                     d = new Date(value.slice(5, -1));
134                     if (d) {
135                         return d;
136                     }
137                 }
138                 return value;
139             });
140 
141 
142     This is a reference implementation. You are free to copy, modify, or
143     redistribute.
144 
145     This code should be minified before deployment.
146     See http://javascript.crockford.com/jsmin.html
147 
148     USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD THIRD PARTY
149     CODE INTO YOUR PAGES.
150 */
151 
152 /*jslint evil: true */
153 
154 /*global JSON */
155 
156 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
157     charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
158     getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
159     parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
160     test, toJSON, toString
161 */
162 
163 if (!this.JSON) {
164 
165 // Create a JSON object only if one does not already exist. We create the
166 // object in a closure to avoid global variables.
167 
168     JSON = function () {
169 
170         function f(n) {
171             // Format integers to have at least two digits.
172             return n < 10 ? '0' + n : n;
173         }
174 
175         Date.prototype.toJSON = function (key) {
176 
177             return this.getUTCFullYear()   + '-' +
178                  f(this.getUTCMonth() + 1) + '-' +
179                  f(this.getUTCDate())      + 'T' +
180                  f(this.getUTCHours())     + ':' +
181                  f(this.getUTCMinutes())   + ':' +
182                  f(this.getUTCSeconds())   + 'Z';
183         };
184 
185         var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
186             escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
187             gap,
188             indent,
189             breaker,
190             meta = {    // table of character substitutions
191                 '\b': '\\b',
192                 '\t': '\\t',
193                 '\n': '\\n',
194                 '\f': '\\f',
195                 '\r': '\\r',
196                 '"' : '\\"',
197                 '\\': '\\\\'
198             },
199             rep;
200 
201 
202         function quote(string) {
203 
204 // If the string contains no control characters, no quote characters, and no
205 // backslash characters, then we can safely slap some quotes around it.
206 // Otherwise we must also replace the offending characters with safe escape
207 // sequences.
208 
209             escapeable.lastIndex = 0;
210             return escapeable.test(string) ?
211                 '"' + string.replace(escapeable, function (a) {
212                     var c = meta[a];
213                     if (typeof c === 'string') {
214                         return c;
215                     }
216                     return '\\u' + ('0000' +
217                             (+(a.charCodeAt(0))).toString(16)).slice(-4);
218                 }) + '"' :
219                 '"' + string + '"';
220         }
221 
222 
223         function str(key, holder) {
224 
225 // Produce a string from holder[key].
226 
227             var i,          // The loop counter.
228                 k,          // The member key.
229                 v,          // The member value.
230                 length,
231                 mind = gap,
232                 partial,
233                 value = holder[key];
234 
235 // If the value has a toJSON method, call it to obtain a replacement value.
236 
237             if (value && typeof value === 'object' &&
238                     typeof value.toJSON === 'function') {
239                 value = value.toJSON(key);
240             }
241 
242 // If we were called with a replacer function, then call the replacer to
243 // obtain a replacement value.
244 
245             if (typeof rep === 'function') {
246                 value = rep.call(holder, key, value);
247             }
248 
249 // What happens next depends on the value's type.
250 
251             switch (typeof value) {
252             case 'string':
253                 return quote(value);
254 
255             case 'number':
256 
257 // JSON numbers must be finite. Encode non-finite numbers as null.
258 
259                 return isFinite(value) ? String(value) : 'null';
260 
261             case 'boolean':
262             case 'null':
263 
264 // If the value is a boolean or null, convert it to a string. Note:
265 // typeof null does not produce 'null'. The case is included here in
266 // the remote chance that this gets fixed someday.
267 
268                 return String(value);
269 
270 // If the type is 'object', we might be dealing with an object or an array or
271 // null.
272 
273             case 'object':
274 
275 // Due to a specification blunder in ECMAScript, typeof null is 'object',
276 // so watch out for that case.
277 
278                 if (!value) {
279                     return 'null';
280                 }
281 
282 // Make an array to hold the partial results of stringifying this object value.
283 
284                 gap += indent;
285                 partial = [];
286 
287 // If the object has a dontEnum length property, we'll treat it as an array.
288 
289                 if (typeof value.length === 'number' &&
290                         !(value.propertyIsEnumerable('length'))) {
291 
292 // The object is an array. Stringify every element. Use null as a placeholder
293 // for non-JSON values.
294 
295                     length = value.length;
296                     for (i = 0; i < length; i += 1) {
297                         partial[i] = str(i, value) || 'null';
298                     }
299 
300 // Join all of the elements together, separated with commas, and wrap them in
301 // brackets.
302 
303                     v = partial.length === 0 ? '[]' :
304                         gap ? '[' + breaker + gap +
305                                 partial.join(',' + breaker + gap) + breaker +
306                                     mind + ']' :
307                               '[' + partial.join(',') + ']';
308                     gap = mind;
309                     return v;
310                 }
311 
312 // If the replacer is an array, use it to select the members to be stringified.
313 
314                 if (rep && typeof rep === 'object') {
315                     length = rep.length;
316                     for (i = 0; i < length; i += 1) {
317                         k = rep[i];
318                         if (typeof k === 'string') {
319                             v = str(k, value, rep);
320                             if (v) {
321                                 partial.push(quote(k) + (gap ? ': ' : ':') + v);
322                             }
323                         }
324                     }
325                 } else {
326 
327 // Otherwise, iterate through all of the keys in the object.
328 
329                     for (k in value) {
330                         if (Object.hasOwnProperty.call(value, k)) {
331                             v = str(k, value, rep);
332                             if (v) {
333                                 partial.push(quote(k) + (gap ? ': ' : ':') + v);
334                             }
335                         }
336                     }
337                 }
338 
339 // Join all of the member texts together, separated with commas,
340 // and wrap them in braces.
341 
342                 v = partial.length === 0 ? '{}' :
343                     gap ? '{' + breaker + gap +
344                             partial.join(',' + breaker + gap) + breaker +
345                             mind + '}' :
346                           '{' + partial.join(',') + '}';
347                 gap = mind;
348                 return v;
349             }
350         }
351 
352 
353 // Return the JSON object containing the stringify, parse, and quote methods.
354 
355         return {
356             stringify: function (value, replacer, space, linebreak) {
357 
358 // The stringify method takes a value and an optional replacer, and an optional
359 // space parameter, and returns a JSON text. The replacer can be a function
360 // that can replace values, or an array of strings that will select the keys.
361 // A default replacer method can be provided. Use of the space parameter can
362 // produce text that is more easily readable.
363 
364                 var i;
365                 gap = '';
366                 indent = '';
367                 if (space) {
368 
369 // If the space parameter is a number, make an indent string containing that
370 // many spaces.
371 
372                     if (typeof space === 'number') {
373                         for (i = 0; i < space; i += 1) {
374                             indent += ' ';
375                         }
376 
377 // If the space parameter is a string, it will be used as the indent string.
378 
379                     } else if (typeof space === 'string') {
380                         indent = space;
381                     }
382                     breaker = typeof linebreak !== 'string' ? '\n' : linebreak;
383                 }
384 
385 // If there is a replacer, it must be a function or an array.
386 // Otherwise, throw an error.
387 
388                 rep = replacer;
389                 if (replacer && typeof replacer !== 'function' &&
390                         (typeof replacer !== 'object' ||
391                          typeof replacer.length !== 'number')) {
392                     throw new Error('JSON.stringify');
393                 }
394 
395 // Make a fake root object containing our value under the key of ''.
396 // Return the result of stringifying the value.
397 
398                 return str('', {'': value});
399             },
400 
401 
402             parse: function (text, reviver) {
403 
404 // The parse method takes a text and an optional reviver function, and returns
405 // a JavaScript value if the text is a valid JSON text.
406 
407                 var j;
408 
409                 function walk(holder, key) {
410 
411 // The walk method is used to recursively walk the resulting structure so
412 // that modifications can be made.
413 
414                     var k, v, value = holder[key];
415                     if (value && typeof value === 'object') {
416                         for (k in value) {
417                             if (Object.hasOwnProperty.call(value, k)) {
418                                 v = walk(value, k);
419                                 if (v !== undefined) {
420                                     value[k] = v;
421                                 } else {
422                                     delete value[k];
423                                 }
424                             }
425                         }
426                     }
427                     return reviver.call(holder, key, value);
428                 }
429 
430 
431 // Parsing happens in four stages. In the first stage, we replace certain
432 // Unicode characters with escape sequences. JavaScript handles many characters
433 // incorrectly, either silently deleting them, or treating them as line endings.
434 
435                 cx.lastIndex = 0;
436                 if (cx.test(text)) {
437                     text = text.replace(cx, function (a) {
438                         return '\\u' + ('0000' +
439                                 (+(a.charCodeAt(0))).toString(16)).slice(-4);
440                     });
441                 }
442 
443 // In the second stage, we run the text against
444 // regular expressions that look for non-JSON patterns. We are especially
445 // concerned with '()' and 'new' because they can cause invocation, and '='
446 // because it can cause mutation. But just to be safe, we want to reject all
447 // unexpected forms.
448 
449 // We split the second stage into 4 regexp operations in order to work around
450 // crippling inefficiencies in IE's and Safari's regexp engines. First we
451 // replace all backslash pairs with '@' (a non-JSON character). Second, we
452 // replace all simple value tokens with ']' characters. Third, we delete all
453 // open brackets that follow a colon or comma or that begin the text. Finally,
454 // we look to see that the remaining characters are only whitespace or ']' or
455 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
456 
457                 if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
458 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
459 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
460 
461 // In the third stage we use the eval function to compile the text into a
462 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
463 // in JavaScript: it can begin a block or an object literal. We wrap the text
464 // in parens to eliminate the ambiguity.
465 
466                     j = eval('(' + text + ')');
467 
468 // In the optional fourth stage, we recursively walk the new structure, passing
469 // each name/value pair to a reviver function for possible transformation.
470 
471                     return typeof reviver === 'function' ?
472                         walk({'': j}, '') : j;
473                 }
474 
475 // If the text is not JSON parseable, then a SyntaxError is thrown.
476 
477                 throw new SyntaxError('JSON.parse');
478             }
479         };
480     }();
481 }
482