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