[6a3a178] | 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);
|
---|