144 |
149 |
145 This is a reference implementation. You are free to copy, modify, or |
150 This is a reference implementation. You are free to copy, modify, or |
146 redistribute. |
151 redistribute. |
147 */ |
152 */ |
148 |
153 |
149 /*jslint evil: true, regexp: true */ |
154 /*jslint |
150 |
155 eval, for, this |
151 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, |
156 */ |
152 call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, |
157 |
|
158 /*property |
|
159 JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, |
153 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, |
160 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, |
154 lastIndex, length, parse, prototype, push, replace, slice, stringify, |
161 lastIndex, length, parse, prototype, push, replace, slice, stringify, |
155 test, toJSON, toString, valueOf |
162 test, toJSON, toString, valueOf |
156 */ |
163 */ |
157 |
164 |
158 |
165 |
159 // Create a JSON object only if one does not already exist. We create the |
166 // Create a JSON object only if one does not already exist. We create the |
160 // methods in a closure to avoid creating global variables. |
167 // methods in a closure to avoid creating global variables. |
161 |
168 |
162 var JSON; |
169 if (typeof JSON !== 'object') { |
163 if (!JSON) { |
|
164 JSON = {}; |
170 JSON = {}; |
165 } |
171 } |
166 |
172 |
167 (function () { |
173 (function () { |
168 'use strict'; |
174 'use strict'; |
|
175 |
|
176 var rx_one = /^[\],:{}\s]*$/, |
|
177 rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, |
|
178 rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, |
|
179 rx_four = /(?:^|:|,)(?:\s*\[)+/g, |
|
180 rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
181 rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; |
169 |
182 |
170 function f(n) { |
183 function f(n) { |
171 // Format integers to have at least two digits. |
184 // Format integers to have at least two digits. |
172 return n < 10 ? '0' + n : n; |
185 return n < 10 |
|
186 ? '0' + n |
|
187 : n; |
|
188 } |
|
189 |
|
190 function this_value() { |
|
191 return this.valueOf(); |
173 } |
192 } |
174 |
193 |
175 if (typeof Date.prototype.toJSON !== 'function') { |
194 if (typeof Date.prototype.toJSON !== 'function') { |
176 |
195 |
177 Date.prototype.toJSON = function (key) { |
196 Date.prototype.toJSON = function () { |
178 |
197 |
179 return isFinite(this.valueOf()) |
198 return isFinite(this.valueOf()) |
180 ? this.getUTCFullYear() + '-' + |
199 ? this.getUTCFullYear() + '-' + |
181 f(this.getUTCMonth() + 1) + '-' + |
200 f(this.getUTCMonth() + 1) + '-' + |
182 f(this.getUTCDate()) + 'T' + |
201 f(this.getUTCDate()) + 'T' + |
183 f(this.getUTCHours()) + ':' + |
202 f(this.getUTCHours()) + ':' + |
184 f(this.getUTCMinutes()) + ':' + |
203 f(this.getUTCMinutes()) + ':' + |
185 f(this.getUTCSeconds()) + 'Z' |
204 f(this.getUTCSeconds()) + 'Z' |
186 : null; |
205 : null; |
187 }; |
206 }; |
188 |
207 |
189 String.prototype.toJSON = |
208 Boolean.prototype.toJSON = this_value; |
190 Number.prototype.toJSON = |
209 Number.prototype.toJSON = this_value; |
191 Boolean.prototype.toJSON = function (key) { |
210 String.prototype.toJSON = this_value; |
192 return this.valueOf(); |
211 } |
193 }; |
212 |
194 } |
213 var gap, |
195 |
|
196 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
197 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
198 gap, |
|
199 indent, |
214 indent, |
200 meta = { // table of character substitutions |
215 meta, |
201 '\b': '\\b', |
|
202 '\t': '\\t', |
|
203 '\n': '\\n', |
|
204 '\f': '\\f', |
|
205 '\r': '\\r', |
|
206 '"' : '\\"', |
|
207 '\\': '\\\\' |
|
208 }, |
|
209 rep; |
216 rep; |
210 |
217 |
211 |
218 |
212 function quote(string) { |
219 function quote(string) { |
213 |
220 |
214 // If the string contains no control characters, no quote characters, and no |
221 // If the string contains no control characters, no quote characters, and no |
215 // backslash characters, then we can safely slap some quotes around it. |
222 // backslash characters, then we can safely slap some quotes around it. |
216 // Otherwise we must also replace the offending characters with safe escape |
223 // Otherwise we must also replace the offending characters with safe escape |
217 // sequences. |
224 // sequences. |
218 |
225 |
219 escapable.lastIndex = 0; |
226 rx_escapable.lastIndex = 0; |
220 return escapable.test(string) ? '"' + string.replace(escapable, function (a) { |
227 return rx_escapable.test(string) |
221 var c = meta[a]; |
228 ? '"' + string.replace(rx_escapable, function (a) { |
222 return typeof c === 'string' |
229 var c = meta[a]; |
223 ? c |
230 return typeof c === 'string' |
224 : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
231 ? c |
225 }) + '"' : '"' + string + '"'; |
232 : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
|
233 }) + '"' |
|
234 : '"' + string + '"'; |
226 } |
235 } |
227 |
236 |
228 |
237 |
229 function str(key, holder) { |
238 function str(key, holder) { |
230 |
239 |
345 // and wrap them in braces. |
364 // and wrap them in braces. |
346 |
365 |
347 v = partial.length === 0 |
366 v = partial.length === 0 |
348 ? '{}' |
367 ? '{}' |
349 : gap |
368 : gap |
350 ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' |
369 ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' |
351 : '{' + partial.join(',') + '}'; |
370 : '{' + partial.join(',') + '}'; |
352 gap = mind; |
371 gap = mind; |
353 return v; |
372 return v; |
354 } |
373 } |
355 } |
374 } |
356 |
375 |
357 // If the JSON object does not yet have a stringify method, give it one. |
376 // If the JSON object does not yet have a stringify method, give it one. |
358 |
377 |
359 if (typeof JSON.stringify !== 'function') { |
378 if (typeof JSON.stringify !== 'function') { |
|
379 meta = { // table of character substitutions |
|
380 '\b': '\\b', |
|
381 '\t': '\\t', |
|
382 '\n': '\\n', |
|
383 '\f': '\\f', |
|
384 '\r': '\\r', |
|
385 '"': '\\"', |
|
386 '\\': '\\\\' |
|
387 }; |
360 JSON.stringify = function (value, replacer, space) { |
388 JSON.stringify = function (value, replacer, space) { |
361 |
389 |
362 // The stringify method takes a value and an optional replacer, and an optional |
390 // The stringify method takes a value and an optional replacer, and an optional |
363 // space parameter, and returns a JSON text. The replacer can be a function |
391 // space parameter, and returns a JSON text. The replacer can be a function |
364 // that can replace values, or an array of strings that will select the keys. |
392 // that can replace values, or an array of strings that will select the keys. |
436 // Parsing happens in four stages. In the first stage, we replace certain |
464 // Parsing happens in four stages. In the first stage, we replace certain |
437 // Unicode characters with escape sequences. JavaScript handles many characters |
465 // Unicode characters with escape sequences. JavaScript handles many characters |
438 // incorrectly, either silently deleting them, or treating them as line endings. |
466 // incorrectly, either silently deleting them, or treating them as line endings. |
439 |
467 |
440 text = String(text); |
468 text = String(text); |
441 cx.lastIndex = 0; |
469 rx_dangerous.lastIndex = 0; |
442 if (cx.test(text)) { |
470 if (rx_dangerous.test(text)) { |
443 text = text.replace(cx, function (a) { |
471 text = text.replace(rx_dangerous, function (a) { |
444 return '\\u' + |
472 return '\\u' + |
445 ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
473 ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
446 }); |
474 }); |
447 } |
475 } |
448 |
476 |
449 // In the second stage, we run the text against regular expressions that look |
477 // In the second stage, we run the text against regular expressions that look |
450 // for non-JSON patterns. We are especially concerned with '()' and 'new' |
478 // for non-JSON patterns. We are especially concerned with '()' and 'new' |
457 // replace all simple value tokens with ']' characters. Third, we delete all |
485 // replace all simple value tokens with ']' characters. Third, we delete all |
458 // open brackets that follow a colon or comma or that begin the text. Finally, |
486 // open brackets that follow a colon or comma or that begin the text. Finally, |
459 // we look to see that the remaining characters are only whitespace or ']' or |
487 // we look to see that the remaining characters are only whitespace or ']' or |
460 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. |
488 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. |
461 |
489 |
462 if (/^[\],:{}\s]*$/ |
490 if ( |
463 .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') |
491 rx_one.test( |
464 .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') |
492 text |
465 .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
493 .replace(rx_two, '@') |
|
494 .replace(rx_three, ']') |
|
495 .replace(rx_four, '') |
|
496 ) |
|
497 ) { |
466 |
498 |
467 // In the third stage we use the eval function to compile the text into a |
499 // In the third stage we use the eval function to compile the text into a |
468 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity |
500 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity |
469 // in JavaScript: it can begin a block or an object literal. We wrap the text |
501 // in JavaScript: it can begin a block or an object literal. We wrap the text |
470 // in parens to eliminate the ambiguity. |
502 // in parens to eliminate the ambiguity. |