提交 | 用户 | 时间
|
1ac2bc
|
1 |
/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */ |
懒 |
2 |
;(function (window) { |
|
3 |
// Convenience aliases. |
|
4 |
var getClass = {}.toString, isProperty, forEach, undef; |
|
5 |
|
|
6 |
// Detect the `define` function exposed by asynchronous module loaders. The |
|
7 |
// strict `define` check is necessary for compatibility with `r.js`. |
|
8 |
var isLoader = typeof define === "function" && define.amd; |
|
9 |
|
|
10 |
// Detect native implementations. |
|
11 |
var nativeJSON = typeof JSON == "object" && JSON; |
|
12 |
|
|
13 |
// Set up the JSON 3 namespace, preferring the CommonJS `exports` object if |
|
14 |
// available. |
|
15 |
var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports; |
|
16 |
|
|
17 |
if (JSON3 && nativeJSON) { |
|
18 |
// Explicitly delegate to the native `stringify` and `parse` |
|
19 |
// implementations in CommonJS environments. |
|
20 |
JSON3.stringify = nativeJSON.stringify; |
|
21 |
JSON3.parse = nativeJSON.parse; |
|
22 |
} else { |
|
23 |
// Export for web browsers, JavaScript engines, and asynchronous module |
|
24 |
// loaders, using the global `JSON` object if available. |
|
25 |
JSON3 = window.JSON = nativeJSON || {}; |
|
26 |
} |
|
27 |
|
|
28 |
// Test the `Date#getUTC*` methods. Based on work by @Yaffle. |
|
29 |
var isExtended = new Date(-3509827334573292); |
|
30 |
try { |
|
31 |
// The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical |
|
32 |
// results for certain dates in Opera >= 10.53. |
|
33 |
isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && |
|
34 |
// Safari < 2.0.2 stores the internal millisecond time value correctly, |
|
35 |
// but clips the values returned by the date methods to the range of |
|
36 |
// signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). |
|
37 |
isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; |
|
38 |
} catch (exception) {} |
|
39 |
|
|
40 |
// Internal: Determines whether the native `JSON.stringify` and `parse` |
|
41 |
// implementations are spec-compliant. Based on work by Ken Snyder. |
|
42 |
function has(name) { |
|
43 |
if (has[name] !== undef) { |
|
44 |
// Return cached feature test result. |
|
45 |
return has[name]; |
|
46 |
} |
|
47 |
|
|
48 |
var isSupported; |
|
49 |
if (name == "bug-string-char-index") { |
|
50 |
// IE <= 7 doesn't support accessing string characters using square |
|
51 |
// bracket notation. IE 8 only supports this for primitives. |
|
52 |
isSupported = "a"[0] != "a"; |
|
53 |
} else if (name == "json") { |
|
54 |
// Indicates whether both `JSON.stringify` and `JSON.parse` are |
|
55 |
// supported. |
|
56 |
isSupported = has("json-stringify") && has("json-parse"); |
|
57 |
} else { |
|
58 |
var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; |
|
59 |
// Test `JSON.stringify`. |
|
60 |
if (name == "json-stringify") { |
|
61 |
var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended; |
|
62 |
if (stringifySupported) { |
|
63 |
// A test function object with a custom `toJSON` method. |
|
64 |
(value = function () { |
|
65 |
return 1; |
|
66 |
}).toJSON = value; |
|
67 |
try { |
|
68 |
stringifySupported = |
|
69 |
// Firefox 3.1b1 and b2 serialize string, number, and boolean |
|
70 |
// primitives as object literals. |
|
71 |
stringify(0) === "0" && |
|
72 |
// FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object |
|
73 |
// literals. |
|
74 |
stringify(new Number()) === "0" && |
|
75 |
stringify(new String()) == '""' && |
|
76 |
// FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or |
|
77 |
// does not define a canonical JSON representation (this applies to |
|
78 |
// objects with `toJSON` properties as well, *unless* they are nested |
|
79 |
// within an object or array). |
|
80 |
stringify(getClass) === undef && |
|
81 |
// IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and |
|
82 |
// FF 3.1b3 pass this test. |
|
83 |
stringify(undef) === undef && |
|
84 |
// Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, |
|
85 |
// respectively, if the value is omitted entirely. |
|
86 |
stringify() === undef && |
|
87 |
// FF 3.1b1, 2 throw an error if the given value is not a number, |
|
88 |
// string, array, object, Boolean, or `null` literal. This applies to |
|
89 |
// objects with custom `toJSON` methods as well, unless they are nested |
|
90 |
// inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` |
|
91 |
// methods entirely. |
|
92 |
stringify(value) === "1" && |
|
93 |
stringify([value]) == "[1]" && |
|
94 |
// Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of |
|
95 |
// `"[null]"`. |
|
96 |
stringify([undef]) == "[null]" && |
|
97 |
// YUI 3.0.0b1 fails to serialize `null` literals. |
|
98 |
stringify(null) == "null" && |
|
99 |
// FF 3.1b1, 2 halts serialization if an array contains a function: |
|
100 |
// `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 |
|
101 |
// elides non-JSON values from objects and arrays, unless they |
|
102 |
// define custom `toJSON` methods. |
|
103 |
stringify([undef, getClass, null]) == "[null,null,null]" && |
|
104 |
// Simple serialization test. FF 3.1b1 uses Unicode escape sequences |
|
105 |
// where character escape codes are expected (e.g., `\b` => `\u0008`). |
|
106 |
stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && |
|
107 |
// FF 3.1b1 and b2 ignore the `filter` and `width` arguments. |
|
108 |
stringify(null, value) === "1" && |
|
109 |
stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && |
|
110 |
// JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly |
|
111 |
// serialize extended years. |
|
112 |
stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && |
|
113 |
// The milliseconds are optional in ES 5, but required in 5.1. |
|
114 |
stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && |
|
115 |
// Firefox <= 11.0 incorrectly serializes years prior to 0 as negative |
|
116 |
// four-digit years instead of six-digit years. Credits: @Yaffle. |
|
117 |
stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && |
|
118 |
// Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond |
|
119 |
// values less than 1000. Credits: @Yaffle. |
|
120 |
stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; |
|
121 |
} catch (exception) { |
|
122 |
stringifySupported = false; |
|
123 |
} |
|
124 |
} |
|
125 |
isSupported = stringifySupported; |
|
126 |
} |
|
127 |
// Test `JSON.parse`. |
|
128 |
if (name == "json-parse") { |
|
129 |
var parse = JSON3.parse; |
|
130 |
if (typeof parse == "function") { |
|
131 |
try { |
|
132 |
// FF 3.1b1, b2 will throw an exception if a bare literal is provided. |
|
133 |
// Conforming implementations should also coerce the initial argument to |
|
134 |
// a string prior to parsing. |
|
135 |
if (parse("0") === 0 && !parse(false)) { |
|
136 |
// Simple parsing test. |
|
137 |
value = parse(serialized); |
|
138 |
var parseSupported = value["a"].length == 5 && value["a"][0] === 1; |
|
139 |
if (parseSupported) { |
|
140 |
try { |
|
141 |
// Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. |
|
142 |
parseSupported = !parse('"\t"'); |
|
143 |
} catch (exception) {} |
|
144 |
if (parseSupported) { |
|
145 |
try { |
|
146 |
// FF 4.0 and 4.0.1 allow leading `+` signs and leading |
|
147 |
// decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow |
|
148 |
// certain octal literals. |
|
149 |
parseSupported = parse("01") !== 1; |
|
150 |
} catch (exception) {} |
|
151 |
} |
|
152 |
if (parseSupported) { |
|
153 |
try { |
|
154 |
// FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal |
|
155 |
// points. These environments, along with FF 3.1b1 and 2, |
|
156 |
// also allow trailing commas in JSON objects and arrays. |
|
157 |
parseSupported = parse("1.") !== 1; |
|
158 |
} catch (exception) {} |
|
159 |
} |
|
160 |
} |
|
161 |
} |
|
162 |
} catch (exception) { |
|
163 |
parseSupported = false; |
|
164 |
} |
|
165 |
} |
|
166 |
isSupported = parseSupported; |
|
167 |
} |
|
168 |
} |
|
169 |
return has[name] = !!isSupported; |
|
170 |
} |
|
171 |
|
|
172 |
if (!has("json")) { |
|
173 |
// Common `[[Class]]` name aliases. |
|
174 |
var functionClass = "[object Function]"; |
|
175 |
var dateClass = "[object Date]"; |
|
176 |
var numberClass = "[object Number]"; |
|
177 |
var stringClass = "[object String]"; |
|
178 |
var arrayClass = "[object Array]"; |
|
179 |
var booleanClass = "[object Boolean]"; |
|
180 |
|
|
181 |
// Detect incomplete support for accessing string characters by index. |
|
182 |
var charIndexBuggy = has("bug-string-char-index"); |
|
183 |
|
|
184 |
// Define additional utility methods if the `Date` methods are buggy. |
|
185 |
if (!isExtended) { |
|
186 |
var floor = Math.floor; |
|
187 |
// A mapping between the months of the year and the number of days between |
|
188 |
// January 1st and the first of the respective month. |
|
189 |
var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; |
|
190 |
// Internal: Calculates the number of days between the Unix epoch and the |
|
191 |
// first day of the given month. |
|
192 |
var getDay = function (year, month) { |
|
193 |
return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); |
|
194 |
}; |
|
195 |
} |
|
196 |
|
|
197 |
// Internal: Determines if a property is a direct property of the given |
|
198 |
// object. Delegates to the native `Object#hasOwnProperty` method. |
|
199 |
if (!(isProperty = {}.hasOwnProperty)) { |
|
200 |
isProperty = function (property) { |
|
201 |
var members = {}, constructor; |
|
202 |
if ((members.__proto__ = null, members.__proto__ = { |
|
203 |
// The *proto* property cannot be set multiple times in recent |
|
204 |
// versions of Firefox and SeaMonkey. |
|
205 |
"toString": 1 |
|
206 |
}, members).toString != getClass) { |
|
207 |
// Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but |
|
208 |
// supports the mutable *proto* property. |
|
209 |
isProperty = function (property) { |
|
210 |
// Capture and break the object's prototype chain (see section 8.6.2 |
|
211 |
// of the ES 5.1 spec). The parenthesized expression prevents an |
|
212 |
// unsafe transformation by the Closure Compiler. |
|
213 |
var original = this.__proto__, result = property in (this.__proto__ = null, this); |
|
214 |
// Restore the original prototype chain. |
|
215 |
this.__proto__ = original; |
|
216 |
return result; |
|
217 |
}; |
|
218 |
} else { |
|
219 |
// Capture a reference to the top-level `Object` constructor. |
|
220 |
constructor = members.constructor; |
|
221 |
// Use the `constructor` property to simulate `Object#hasOwnProperty` in |
|
222 |
// other environments. |
|
223 |
isProperty = function (property) { |
|
224 |
var parent = (this.constructor || constructor).prototype; |
|
225 |
return property in this && !(property in parent && this[property] === parent[property]); |
|
226 |
}; |
|
227 |
} |
|
228 |
members = null; |
|
229 |
return isProperty.call(this, property); |
|
230 |
}; |
|
231 |
} |
|
232 |
|
|
233 |
// Internal: A set of primitive types used by `isHostType`. |
|
234 |
var PrimitiveTypes = { |
|
235 |
'boolean': 1, |
|
236 |
'number': 1, |
|
237 |
'string': 1, |
|
238 |
'undefined': 1 |
|
239 |
}; |
|
240 |
|
|
241 |
// Internal: Determines if the given object `property` value is a |
|
242 |
// non-primitive. |
|
243 |
var isHostType = function (object, property) { |
|
244 |
var type = typeof object[property]; |
|
245 |
return type == 'object' ? !!object[property] : !PrimitiveTypes[type]; |
|
246 |
}; |
|
247 |
|
|
248 |
// Internal: Normalizes the `for...in` iteration algorithm across |
|
249 |
// environments. Each enumerated key is yielded to a `callback` function. |
|
250 |
forEach = function (object, callback) { |
|
251 |
var size = 0, Properties, members, property; |
|
252 |
|
|
253 |
// Tests for bugs in the current environment's `for...in` algorithm. The |
|
254 |
// `valueOf` property inherits the non-enumerable flag from |
|
255 |
// `Object.prototype` in older versions of IE, Netscape, and Mozilla. |
|
256 |
(Properties = function () { |
|
257 |
this.valueOf = 0; |
|
258 |
}).prototype.valueOf = 0; |
|
259 |
|
|
260 |
// Iterate over a new instance of the `Properties` class. |
|
261 |
members = new Properties(); |
|
262 |
for (property in members) { |
|
263 |
// Ignore all properties inherited from `Object.prototype`. |
|
264 |
if (isProperty.call(members, property)) { |
|
265 |
size++; |
|
266 |
} |
|
267 |
} |
|
268 |
Properties = members = null; |
|
269 |
|
|
270 |
// Normalize the iteration algorithm. |
|
271 |
if (!size) { |
|
272 |
// A list of non-enumerable properties inherited from `Object.prototype`. |
|
273 |
members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; |
|
274 |
// IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable |
|
275 |
// properties. |
|
276 |
forEach = function (object, callback) { |
|
277 |
var isFunction = getClass.call(object) == functionClass, property, length; |
|
278 |
var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty; |
|
279 |
for (property in object) { |
|
280 |
// Gecko <= 1.0 enumerates the `prototype` property of functions under |
|
281 |
// certain conditions; IE does not. |
|
282 |
if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { |
|
283 |
callback(property); |
|
284 |
} |
|
285 |
} |
|
286 |
// Manually invoke the callback for each non-enumerable property. |
|
287 |
for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); |
|
288 |
}; |
|
289 |
} else if (size == 2) { |
|
290 |
// Safari <= 2.0.4 enumerates shadowed properties twice. |
|
291 |
forEach = function (object, callback) { |
|
292 |
// Create a set of iterated properties. |
|
293 |
var members = {}, isFunction = getClass.call(object) == functionClass, property; |
|
294 |
for (property in object) { |
|
295 |
// Store each property name to prevent double enumeration. The |
|
296 |
// `prototype` property of functions is not enumerated due to cross- |
|
297 |
// environment inconsistencies. |
|
298 |
if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { |
|
299 |
callback(property); |
|
300 |
} |
|
301 |
} |
|
302 |
}; |
|
303 |
} else { |
|
304 |
// No bugs detected; use the standard `for...in` algorithm. |
|
305 |
forEach = function (object, callback) { |
|
306 |
var isFunction = getClass.call(object) == functionClass, property, isConstructor; |
|
307 |
for (property in object) { |
|
308 |
if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { |
|
309 |
callback(property); |
|
310 |
} |
|
311 |
} |
|
312 |
// Manually invoke the callback for the `constructor` property due to |
|
313 |
// cross-environment inconsistencies. |
|
314 |
if (isConstructor || isProperty.call(object, (property = "constructor"))) { |
|
315 |
callback(property); |
|
316 |
} |
|
317 |
}; |
|
318 |
} |
|
319 |
return forEach(object, callback); |
|
320 |
}; |
|
321 |
|
|
322 |
// Public: Serializes a JavaScript `value` as a JSON string. The optional |
|
323 |
// `filter` argument may specify either a function that alters how object and |
|
324 |
// array members are serialized, or an array of strings and numbers that |
|
325 |
// indicates which properties should be serialized. The optional `width` |
|
326 |
// argument may be either a string or number that specifies the indentation |
|
327 |
// level of the output. |
|
328 |
if (!has("json-stringify")) { |
|
329 |
// Internal: A map of control characters and their escaped equivalents. |
|
330 |
var Escapes = { |
|
331 |
92: "\\\\", |
|
332 |
34: '\\"', |
|
333 |
8: "\\b", |
|
334 |
12: "\\f", |
|
335 |
10: "\\n", |
|
336 |
13: "\\r", |
|
337 |
9: "\\t" |
|
338 |
}; |
|
339 |
|
|
340 |
// Internal: Converts `value` into a zero-padded string such that its |
|
341 |
// length is at least equal to `width`. The `width` must be <= 6. |
|
342 |
var leadingZeroes = "000000"; |
|
343 |
var toPaddedString = function (width, value) { |
|
344 |
// The `|| 0` expression is necessary to work around a bug in |
|
345 |
// Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. |
|
346 |
return (leadingZeroes + (value || 0)).slice(-width); |
|
347 |
}; |
|
348 |
|
|
349 |
// Internal: Double-quotes a string `value`, replacing all ASCII control |
|
350 |
// characters (characters with code unit values between 0 and 31) with |
|
351 |
// their escaped equivalents. This is an implementation of the |
|
352 |
// `Quote(value)` operation defined in ES 5.1 section 15.12.3. |
|
353 |
var unicodePrefix = "\\u00"; |
|
354 |
var quote = function (value) { |
|
355 |
var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols; |
|
356 |
if (isLarge) { |
|
357 |
symbols = value.split(""); |
|
358 |
} |
|
359 |
for (; index < length; index++) { |
|
360 |
var charCode = value.charCodeAt(index); |
|
361 |
// If the character is a control character, append its Unicode or |
|
362 |
// shorthand escape sequence; otherwise, append the character as-is. |
|
363 |
switch (charCode) { |
|
364 |
case 8: case 9: case 10: case 12: case 13: case 34: case 92: |
|
365 |
result += Escapes[charCode]; |
|
366 |
break; |
|
367 |
default: |
|
368 |
if (charCode < 32) { |
|
369 |
result += unicodePrefix + toPaddedString(2, charCode.toString(16)); |
|
370 |
break; |
|
371 |
} |
|
372 |
result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index]; |
|
373 |
} |
|
374 |
} |
|
375 |
return result + '"'; |
|
376 |
}; |
|
377 |
|
|
378 |
// Internal: Recursively serializes an object. Implements the |
|
379 |
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. |
|
380 |
var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { |
|
381 |
var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; |
|
382 |
try { |
|
383 |
// Necessary for host object support. |
|
384 |
value = object[property]; |
|
385 |
} catch (exception) {} |
|
386 |
if (typeof value == "object" && value) { |
|
387 |
className = getClass.call(value); |
|
388 |
if (className == dateClass && !isProperty.call(value, "toJSON")) { |
|
389 |
if (value > -1 / 0 && value < 1 / 0) { |
|
390 |
// Dates are serialized according to the `Date#toJSON` method |
|
391 |
// specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 |
|
392 |
// for the ISO 8601 date time string format. |
|
393 |
if (getDay) { |
|
394 |
// Manually compute the year, month, date, hours, minutes, |
|
395 |
// seconds, and milliseconds if the `getUTC*` methods are |
|
396 |
// buggy. Adapted from @Yaffle's `date-shim` project. |
|
397 |
date = floor(value / 864e5); |
|
398 |
for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); |
|
399 |
for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); |
|
400 |
date = 1 + date - getDay(year, month); |
|
401 |
// The `time` value specifies the time within the day (see ES |
|
402 |
// 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used |
|
403 |
// to compute `A modulo B`, as the `%` operator does not |
|
404 |
// correspond to the `modulo` operation for negative numbers. |
|
405 |
time = (value % 864e5 + 864e5) % 864e5; |
|
406 |
// The hours, minutes, seconds, and milliseconds are obtained by |
|
407 |
// decomposing the time within the day. See section 15.9.1.10. |
|
408 |
hours = floor(time / 36e5) % 24; |
|
409 |
minutes = floor(time / 6e4) % 60; |
|
410 |
seconds = floor(time / 1e3) % 60; |
|
411 |
milliseconds = time % 1e3; |
|
412 |
} else { |
|
413 |
year = value.getUTCFullYear(); |
|
414 |
month = value.getUTCMonth(); |
|
415 |
date = value.getUTCDate(); |
|
416 |
hours = value.getUTCHours(); |
|
417 |
minutes = value.getUTCMinutes(); |
|
418 |
seconds = value.getUTCSeconds(); |
|
419 |
milliseconds = value.getUTCMilliseconds(); |
|
420 |
} |
|
421 |
// Serialize extended years correctly. |
|
422 |
value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + |
|
423 |
"-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + |
|
424 |
// Months, dates, hours, minutes, and seconds should have two |
|
425 |
// digits; milliseconds should have three. |
|
426 |
"T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + |
|
427 |
// Milliseconds are optional in ES 5.0, but required in 5.1. |
|
428 |
"." + toPaddedString(3, milliseconds) + "Z"; |
|
429 |
} else { |
|
430 |
value = null; |
|
431 |
} |
|
432 |
} else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { |
|
433 |
// Prototype <= 1.6.1 adds non-standard `toJSON` methods to the |
|
434 |
// `Number`, `String`, `Date`, and `Array` prototypes. JSON 3 |
|
435 |
// ignores all `toJSON` methods on these objects unless they are |
|
436 |
// defined directly on an instance. |
|
437 |
value = value.toJSON(property); |
|
438 |
} |
|
439 |
} |
|
440 |
if (callback) { |
|
441 |
// If a replacement function was provided, call it to obtain the value |
|
442 |
// for serialization. |
|
443 |
value = callback.call(object, property, value); |
|
444 |
} |
|
445 |
if (value === null) { |
|
446 |
return "null"; |
|
447 |
} |
|
448 |
className = getClass.call(value); |
|
449 |
if (className == booleanClass) { |
|
450 |
// Booleans are represented literally. |
|
451 |
return "" + value; |
|
452 |
} else if (className == numberClass) { |
|
453 |
// JSON numbers must be finite. `Infinity` and `NaN` are serialized as |
|
454 |
// `"null"`. |
|
455 |
return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; |
|
456 |
} else if (className == stringClass) { |
|
457 |
// Strings are double-quoted and escaped. |
|
458 |
return quote("" + value); |
|
459 |
} |
|
460 |
// Recursively serialize objects and arrays. |
|
461 |
if (typeof value == "object") { |
|
462 |
// Check for cyclic structures. This is a linear search; performance |
|
463 |
// is inversely proportional to the number of unique nested objects. |
|
464 |
for (length = stack.length; length--;) { |
|
465 |
if (stack[length] === value) { |
|
466 |
// Cyclic structures cannot be serialized by `JSON.stringify`. |
|
467 |
throw TypeError(); |
|
468 |
} |
|
469 |
} |
|
470 |
// Add the object to the stack of traversed objects. |
|
471 |
stack.push(value); |
|
472 |
results = []; |
|
473 |
// Save the current indentation level and indent one additional level. |
|
474 |
prefix = indentation; |
|
475 |
indentation += whitespace; |
|
476 |
if (className == arrayClass) { |
|
477 |
// Recursively serialize array elements. |
|
478 |
for (index = 0, length = value.length; index < length; index++) { |
|
479 |
element = serialize(index, value, callback, properties, whitespace, indentation, stack); |
|
480 |
results.push(element === undef ? "null" : element); |
|
481 |
} |
|
482 |
result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; |
|
483 |
} else { |
|
484 |
// Recursively serialize object members. Members are selected from |
|
485 |
// either a user-specified list of property names, or the object |
|
486 |
// itself. |
|
487 |
forEach(properties || value, function (property) { |
|
488 |
var element = serialize(property, value, callback, properties, whitespace, indentation, stack); |
|
489 |
if (element !== undef) { |
|
490 |
// According to ES 5.1 section 15.12.3: "If `gap` {whitespace} |
|
491 |
// is not the empty string, let `member` {quote(property) + ":"} |
|
492 |
// be the concatenation of `member` and the `space` character." |
|
493 |
// The "`space` character" refers to the literal space |
|
494 |
// character, not the `space` {width} argument provided to |
|
495 |
// `JSON.stringify`. |
|
496 |
results.push(quote(property) + ":" + (whitespace ? " " : "") + element); |
|
497 |
} |
|
498 |
}); |
|
499 |
result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; |
|
500 |
} |
|
501 |
// Remove the object from the traversed object stack. |
|
502 |
stack.pop(); |
|
503 |
return result; |
|
504 |
} |
|
505 |
}; |
|
506 |
|
|
507 |
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3. |
|
508 |
JSON3.stringify = function (source, filter, width) { |
|
509 |
var whitespace, callback, properties, className; |
|
510 |
if (typeof filter == "function" || typeof filter == "object" && filter) { |
|
511 |
if ((className = getClass.call(filter)) == functionClass) { |
|
512 |
callback = filter; |
|
513 |
} else if (className == arrayClass) { |
|
514 |
// Convert the property names array into a makeshift set. |
|
515 |
properties = {}; |
|
516 |
for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); |
|
517 |
} |
|
518 |
} |
|
519 |
if (width) { |
|
520 |
if ((className = getClass.call(width)) == numberClass) { |
|
521 |
// Convert the `width` to an integer and create a string containing |
|
522 |
// `width` number of space characters. |
|
523 |
if ((width -= width % 1) > 0) { |
|
524 |
for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); |
|
525 |
} |
|
526 |
} else if (className == stringClass) { |
|
527 |
whitespace = width.length <= 10 ? width : width.slice(0, 10); |
|
528 |
} |
|
529 |
} |
|
530 |
// Opera <= 7.54u2 discards the values associated with empty string keys |
|
531 |
// (`""`) only if they are used directly within an object member list |
|
532 |
// (e.g., `!("" in { "": 1})`). |
|
533 |
return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); |
|
534 |
}; |
|
535 |
} |
|
536 |
|
|
537 |
// Public: Parses a JSON source string. |
|
538 |
if (!has("json-parse")) { |
|
539 |
var fromCharCode = String.fromCharCode; |
|
540 |
|
|
541 |
// Internal: A map of escaped control characters and their unescaped |
|
542 |
// equivalents. |
|
543 |
var Unescapes = { |
|
544 |
92: "\\", |
|
545 |
34: '"', |
|
546 |
47: "/", |
|
547 |
98: "\b", |
|
548 |
116: "\t", |
|
549 |
110: "\n", |
|
550 |
102: "\f", |
|
551 |
114: "\r" |
|
552 |
}; |
|
553 |
|
|
554 |
// Internal: Stores the parser state. |
|
555 |
var Index, Source; |
|
556 |
|
|
557 |
// Internal: Resets the parser state and throws a `SyntaxError`. |
|
558 |
var abort = function() { |
|
559 |
Index = Source = null; |
|
560 |
throw SyntaxError(); |
|
561 |
}; |
|
562 |
|
|
563 |
// Internal: Returns the next token, or `"$"` if the parser has reached |
|
564 |
// the end of the source string. A token may be a string, number, `null` |
|
565 |
// literal, or Boolean literal. |
|
566 |
var lex = function () { |
|
567 |
var source = Source, length = source.length, value, begin, position, isSigned, charCode; |
|
568 |
while (Index < length) { |
|
569 |
charCode = source.charCodeAt(Index); |
|
570 |
switch (charCode) { |
|
571 |
case 9: case 10: case 13: case 32: |
|
572 |
// Skip whitespace tokens, including tabs, carriage returns, line |
|
573 |
// feeds, and space characters. |
|
574 |
Index++; |
|
575 |
break; |
|
576 |
case 123: case 125: case 91: case 93: case 58: case 44: |
|
577 |
// Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at |
|
578 |
// the current position. |
|
579 |
value = charIndexBuggy ? source.charAt(Index) : source[Index]; |
|
580 |
Index++; |
|
581 |
return value; |
|
582 |
case 34: |
|
583 |
// `"` delimits a JSON string; advance to the next character and |
|
584 |
// begin parsing the string. String tokens are prefixed with the |
|
585 |
// sentinel `@` character to distinguish them from punctuators and |
|
586 |
// end-of-string tokens. |
|
587 |
for (value = "@", Index++; Index < length;) { |
|
588 |
charCode = source.charCodeAt(Index); |
|
589 |
if (charCode < 32) { |
|
590 |
// Unescaped ASCII control characters (those with a code unit |
|
591 |
// less than the space character) are not permitted. |
|
592 |
abort(); |
|
593 |
} else if (charCode == 92) { |
|
594 |
// A reverse solidus (`\`) marks the beginning of an escaped |
|
595 |
// control character (including `"`, `\`, and `/`) or Unicode |
|
596 |
// escape sequence. |
|
597 |
charCode = source.charCodeAt(++Index); |
|
598 |
switch (charCode) { |
|
599 |
case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: |
|
600 |
// Revive escaped control characters. |
|
601 |
value += Unescapes[charCode]; |
|
602 |
Index++; |
|
603 |
break; |
|
604 |
case 117: |
|
605 |
// `\u` marks the beginning of a Unicode escape sequence. |
|
606 |
// Advance to the first character and validate the |
|
607 |
// four-digit code point. |
|
608 |
begin = ++Index; |
|
609 |
for (position = Index + 4; Index < position; Index++) { |
|
610 |
charCode = source.charCodeAt(Index); |
|
611 |
// A valid sequence comprises four hexdigits (case- |
|
612 |
// insensitive) that form a single hexadecimal value. |
|
613 |
if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { |
|
614 |
// Invalid Unicode escape sequence. |
|
615 |
abort(); |
|
616 |
} |
|
617 |
} |
|
618 |
// Revive the escaped character. |
|
619 |
value += fromCharCode("0x" + source.slice(begin, Index)); |
|
620 |
break; |
|
621 |
default: |
|
622 |
// Invalid escape sequence. |
|
623 |
abort(); |
|
624 |
} |
|
625 |
} else { |
|
626 |
if (charCode == 34) { |
|
627 |
// An unescaped double-quote character marks the end of the |
|
628 |
// string. |
|
629 |
break; |
|
630 |
} |
|
631 |
charCode = source.charCodeAt(Index); |
|
632 |
begin = Index; |
|
633 |
// Optimize for the common case where a string is valid. |
|
634 |
while (charCode >= 32 && charCode != 92 && charCode != 34) { |
|
635 |
charCode = source.charCodeAt(++Index); |
|
636 |
} |
|
637 |
// Append the string as-is. |
|
638 |
value += source.slice(begin, Index); |
|
639 |
} |
|
640 |
} |
|
641 |
if (source.charCodeAt(Index) == 34) { |
|
642 |
// Advance to the next character and return the revived string. |
|
643 |
Index++; |
|
644 |
return value; |
|
645 |
} |
|
646 |
// Unterminated string. |
|
647 |
abort(); |
|
648 |
default: |
|
649 |
// Parse numbers and literals. |
|
650 |
begin = Index; |
|
651 |
// Advance past the negative sign, if one is specified. |
|
652 |
if (charCode == 45) { |
|
653 |
isSigned = true; |
|
654 |
charCode = source.charCodeAt(++Index); |
|
655 |
} |
|
656 |
// Parse an integer or floating-point value. |
|
657 |
if (charCode >= 48 && charCode <= 57) { |
|
658 |
// Leading zeroes are interpreted as octal literals. |
|
659 |
if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { |
|
660 |
// Illegal octal literal. |
|
661 |
abort(); |
|
662 |
} |
|
663 |
isSigned = false; |
|
664 |
// Parse the integer component. |
|
665 |
for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); |
|
666 |
// Floats cannot contain a leading decimal point; however, this |
|
667 |
// case is already accounted for by the parser. |
|
668 |
if (source.charCodeAt(Index) == 46) { |
|
669 |
position = ++Index; |
|
670 |
// Parse the decimal component. |
|
671 |
for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); |
|
672 |
if (position == Index) { |
|
673 |
// Illegal trailing decimal. |
|
674 |
abort(); |
|
675 |
} |
|
676 |
Index = position; |
|
677 |
} |
|
678 |
// Parse exponents. The `e` denoting the exponent is |
|
679 |
// case-insensitive. |
|
680 |
charCode = source.charCodeAt(Index); |
|
681 |
if (charCode == 101 || charCode == 69) { |
|
682 |
charCode = source.charCodeAt(++Index); |
|
683 |
// Skip past the sign following the exponent, if one is |
|
684 |
// specified. |
|
685 |
if (charCode == 43 || charCode == 45) { |
|
686 |
Index++; |
|
687 |
} |
|
688 |
// Parse the exponential component. |
|
689 |
for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); |
|
690 |
if (position == Index) { |
|
691 |
// Illegal empty exponent. |
|
692 |
abort(); |
|
693 |
} |
|
694 |
Index = position; |
|
695 |
} |
|
696 |
// Coerce the parsed value to a JavaScript number. |
|
697 |
return +source.slice(begin, Index); |
|
698 |
} |
|
699 |
// A negative sign may only precede numbers. |
|
700 |
if (isSigned) { |
|
701 |
abort(); |
|
702 |
} |
|
703 |
// `true`, `false`, and `null` literals. |
|
704 |
if (source.slice(Index, Index + 4) == "true") { |
|
705 |
Index += 4; |
|
706 |
return true; |
|
707 |
} else if (source.slice(Index, Index + 5) == "false") { |
|
708 |
Index += 5; |
|
709 |
return false; |
|
710 |
} else if (source.slice(Index, Index + 4) == "null") { |
|
711 |
Index += 4; |
|
712 |
return null; |
|
713 |
} |
|
714 |
// Unrecognized token. |
|
715 |
abort(); |
|
716 |
} |
|
717 |
} |
|
718 |
// Return the sentinel `$` character if the parser has reached the end |
|
719 |
// of the source string. |
|
720 |
return "$"; |
|
721 |
}; |
|
722 |
|
|
723 |
// Internal: Parses a JSON `value` token. |
|
724 |
var get = function (value) { |
|
725 |
var results, hasMembers; |
|
726 |
if (value == "$") { |
|
727 |
// Unexpected end of input. |
|
728 |
abort(); |
|
729 |
} |
|
730 |
if (typeof value == "string") { |
|
731 |
if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { |
|
732 |
// Remove the sentinel `@` character. |
|
733 |
return value.slice(1); |
|
734 |
} |
|
735 |
// Parse object and array literals. |
|
736 |
if (value == "[") { |
|
737 |
// Parses a JSON array, returning a new JavaScript array. |
|
738 |
results = []; |
|
739 |
for (;; hasMembers || (hasMembers = true)) { |
|
740 |
value = lex(); |
|
741 |
// A closing square bracket marks the end of the array literal. |
|
742 |
if (value == "]") { |
|
743 |
break; |
|
744 |
} |
|
745 |
// If the array literal contains elements, the current token |
|
746 |
// should be a comma separating the previous element from the |
|
747 |
// next. |
|
748 |
if (hasMembers) { |
|
749 |
if (value == ",") { |
|
750 |
value = lex(); |
|
751 |
if (value == "]") { |
|
752 |
// Unexpected trailing `,` in array literal. |
|
753 |
abort(); |
|
754 |
} |
|
755 |
} else { |
|
756 |
// A `,` must separate each array element. |
|
757 |
abort(); |
|
758 |
} |
|
759 |
} |
|
760 |
// Elisions and leading commas are not permitted. |
|
761 |
if (value == ",") { |
|
762 |
abort(); |
|
763 |
} |
|
764 |
results.push(get(value)); |
|
765 |
} |
|
766 |
return results; |
|
767 |
} else if (value == "{") { |
|
768 |
// Parses a JSON object, returning a new JavaScript object. |
|
769 |
results = {}; |
|
770 |
for (;; hasMembers || (hasMembers = true)) { |
|
771 |
value = lex(); |
|
772 |
// A closing curly brace marks the end of the object literal. |
|
773 |
if (value == "}") { |
|
774 |
break; |
|
775 |
} |
|
776 |
// If the object literal contains members, the current token |
|
777 |
// should be a comma separator. |
|
778 |
if (hasMembers) { |
|
779 |
if (value == ",") { |
|
780 |
value = lex(); |
|
781 |
if (value == "}") { |
|
782 |
// Unexpected trailing `,` in object literal. |
|
783 |
abort(); |
|
784 |
} |
|
785 |
} else { |
|
786 |
// A `,` must separate each object member. |
|
787 |
abort(); |
|
788 |
} |
|
789 |
} |
|
790 |
// Leading commas are not permitted, object property names must be |
|
791 |
// double-quoted strings, and a `:` must separate each property |
|
792 |
// name and value. |
|
793 |
if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { |
|
794 |
abort(); |
|
795 |
} |
|
796 |
results[value.slice(1)] = get(lex()); |
|
797 |
} |
|
798 |
return results; |
|
799 |
} |
|
800 |
// Unexpected token encountered. |
|
801 |
abort(); |
|
802 |
} |
|
803 |
return value; |
|
804 |
}; |
|
805 |
|
|
806 |
// Internal: Updates a traversed object member. |
|
807 |
var update = function(source, property, callback) { |
|
808 |
var element = walk(source, property, callback); |
|
809 |
if (element === undef) { |
|
810 |
delete source[property]; |
|
811 |
} else { |
|
812 |
source[property] = element; |
|
813 |
} |
|
814 |
}; |
|
815 |
|
|
816 |
// Internal: Recursively traverses a parsed JSON object, invoking the |
|
817 |
// `callback` function for each value. This is an implementation of the |
|
818 |
// `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. |
|
819 |
var walk = function (source, property, callback) { |
|
820 |
var value = source[property], length; |
|
821 |
if (typeof value == "object" && value) { |
|
822 |
// `forEach` can't be used to traverse an array in Opera <= 8.54 |
|
823 |
// because its `Object#hasOwnProperty` implementation returns `false` |
|
824 |
// for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). |
|
825 |
if (getClass.call(value) == arrayClass) { |
|
826 |
for (length = value.length; length--;) { |
|
827 |
update(value, length, callback); |
|
828 |
} |
|
829 |
} else { |
|
830 |
forEach(value, function (property) { |
|
831 |
update(value, property, callback); |
|
832 |
}); |
|
833 |
} |
|
834 |
} |
|
835 |
return callback.call(source, property, value); |
|
836 |
}; |
|
837 |
|
|
838 |
// Public: `JSON.parse`. See ES 5.1 section 15.12.2. |
|
839 |
JSON3.parse = function (source, callback) { |
|
840 |
var result, value; |
|
841 |
Index = 0; |
|
842 |
Source = "" + source; |
|
843 |
result = get(lex()); |
|
844 |
// If a JSON string contains multiple tokens, it is invalid. |
|
845 |
if (lex() != "$") { |
|
846 |
abort(); |
|
847 |
} |
|
848 |
// Reset the parser state. |
|
849 |
Index = Source = null; |
|
850 |
return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; |
|
851 |
}; |
|
852 |
} |
|
853 |
} |
|
854 |
|
|
855 |
// Export for asynchronous module loaders. |
|
856 |
if (isLoader) { |
|
857 |
define(function () { |
|
858 |
return JSON3; |
|
859 |
}); |
|
860 |
} |
|
861 |
}(this)); |