暫無描述

json3.js 42KB

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