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