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, strict: false, regexp: false */ |
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' : null; |
204 f(this.getUTCSeconds()) + 'Z' |
|
205 : null; |
186 }; |
206 }; |
187 |
207 |
188 String.prototype.toJSON = |
208 Boolean.prototype.toJSON = this_value; |
189 Number.prototype.toJSON = |
209 Number.prototype.toJSON = this_value; |
190 Boolean.prototype.toJSON = function (key) { |
210 String.prototype.toJSON = this_value; |
191 return this.valueOf(); |
211 } |
192 }; |
212 |
193 } |
213 var gap, |
194 |
|
195 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
196 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
|
197 gap, |
|
198 indent, |
214 indent, |
199 meta = { // table of character substitutions |
215 meta, |
200 '\b': '\\b', |
|
201 '\t': '\\t', |
|
202 '\n': '\\n', |
|
203 '\f': '\\f', |
|
204 '\r': '\\r', |
|
205 '"' : '\\"', |
|
206 '\\': '\\\\' |
|
207 }, |
|
208 rep; |
216 rep; |
209 |
217 |
210 |
218 |
211 function quote(string) { |
219 function quote(string) { |
212 |
220 |
213 // 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 |
214 // backslash characters, then we can safely slap some quotes around it. |
222 // backslash characters, then we can safely slap some quotes around it. |
215 // Otherwise we must also replace the offending characters with safe escape |
223 // Otherwise we must also replace the offending characters with safe escape |
216 // sequences. |
224 // sequences. |
217 |
225 |
218 escapable.lastIndex = 0; |
226 rx_escapable.lastIndex = 0; |
219 return escapable.test(string) ? '"' + string.replace(escapable, function (a) { |
227 return rx_escapable.test(string) |
220 var c = meta[a]; |
228 ? '"' + string.replace(rx_escapable, function (a) { |
221 return typeof c === 'string' ? c : |
229 var c = meta[a]; |
222 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
230 return typeof c === 'string' |
223 }) + '"' : '"' + string + '"'; |
231 ? c |
|
232 : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
|
233 }) + '"' |
|
234 : '"' + string + '"'; |
224 } |
235 } |
225 |
236 |
226 |
237 |
227 function str(key, holder) { |
238 function str(key, holder) { |
228 |
239 |
329 |
348 |
330 for (k in value) { |
349 for (k in value) { |
331 if (Object.prototype.hasOwnProperty.call(value, k)) { |
350 if (Object.prototype.hasOwnProperty.call(value, k)) { |
332 v = str(k, value); |
351 v = str(k, value); |
333 if (v) { |
352 if (v) { |
334 partial.push(quote(k) + (gap ? ': ' : ':') + v); |
353 partial.push(quote(k) + ( |
|
354 gap |
|
355 ? ': ' |
|
356 : ':' |
|
357 ) + v); |
335 } |
358 } |
336 } |
359 } |
337 } |
360 } |
338 } |
361 } |
339 |
362 |
340 // Join all of the member texts together, separated with commas, |
363 // Join all of the member texts together, separated with commas, |
341 // and wrap them in braces. |
364 // and wrap them in braces. |
342 |
365 |
343 v = partial.length === 0 ? '{}' : gap ? |
366 v = partial.length === 0 |
344 '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : |
367 ? '{}' |
345 '{' + partial.join(',') + '}'; |
368 : gap |
|
369 ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' |
|
370 : '{' + partial.join(',') + '}'; |
346 gap = mind; |
371 gap = mind; |
347 return v; |
372 return v; |
348 } |
373 } |
349 } |
374 } |
350 |
375 |
351 // 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. |
352 |
377 |
353 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 }; |
354 JSON.stringify = function (value, replacer, space) { |
388 JSON.stringify = function (value, replacer, space) { |
355 |
389 |
356 // 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 |
357 // 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 |
358 // 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. |
451 // replace all simple value tokens with ']' characters. Third, we delete all |
485 // replace all simple value tokens with ']' characters. Third, we delete all |
452 // 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, |
453 // 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 |
454 // ',' 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. |
455 |
489 |
456 if (/^[\],:{}\s]*$/ |
490 if ( |
457 .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') |
491 rx_one.test( |
458 .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') |
492 text |
459 .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
493 .replace(rx_two, '@') |
|
494 .replace(rx_three, ']') |
|
495 .replace(rx_four, '') |
|
496 ) |
|
497 ) { |
460 |
498 |
461 // 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 |
462 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity |
500 // 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 |
501 // in JavaScript: it can begin a block or an object literal. We wrap the text |
464 // in parens to eliminate the ambiguity. |
502 // in parens to eliminate the ambiguity. |