|
|
/*! JSON v3.2.6 | http://bestiejs.github.io/json3 | Copyright 2012-2013, Kit Cambridge | http://kit.mit-license.org */ ;(function (window) { // Convenience aliases.
var getClass = {}.toString, isProperty, forEach, undef;
// Detect the `define` function exposed by asynchronous module loaders. The
// strict `define` check is necessary for compatibility with `r.js`.
var isLoader = typeof define === "function" && define.amd;
// Detect native implementations.
var nativeJSON = typeof JSON == "object" && JSON;
// Set up the JSON 3 namespace, preferring the CommonJS `exports` object if
// available.
var JSON3 = typeof exports == "object" && exports && !exports.nodeType && exports;
if (JSON3 && nativeJSON) { // Explicitly delegate to the native `stringify` and `parse`
// implementations in CommonJS environments.
JSON3.stringify = nativeJSON.stringify; JSON3.parse = nativeJSON.parse; } else { // Export for web browsers, JavaScript engines, and asynchronous module
// loaders, using the global `JSON` object if available.
JSON3 = window.JSON = nativeJSON || {}; }
// Test the `Date#getUTC*` methods. Based on work by @Yaffle.
var isExtended = new Date(-3509827334573292); try { // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
// results for certain dates in Opera >= 10.53.
isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && // Safari < 2.0.2 stores the internal millisecond time value correctly,
// but clips the values returned by the date methods to the range of
// signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; } catch (exception) {}
// Internal: Determines whether the native `JSON.stringify` and `parse`
// implementations are spec-compliant. Based on work by Ken Snyder.
function has(name) { if (has[name] !== undef) { // Return cached feature test result.
return has[name]; }
var isSupported; if (name == "bug-string-char-index") { // IE <= 7 doesn't support accessing string characters using square
// bracket notation. IE 8 only supports this for primitives.
isSupported = "a"[0] != "a"; } else if (name == "json") { // Indicates whether both `JSON.stringify` and `JSON.parse` are
// supported.
isSupported = has("json-stringify") && has("json-parse"); } else { var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; // Test `JSON.stringify`.
if (name == "json-stringify") { var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended; if (stringifySupported) { // A test function object with a custom `toJSON` method.
(value = function () { return 1; }).toJSON = value; try { stringifySupported = // Firefox 3.1b1 and b2 serialize string, number, and boolean
// primitives as object literals.
stringify(0) === "0" && // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
// literals.
stringify(new Number()) === "0" && stringify(new String()) == '""' && // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
// does not define a canonical JSON representation (this applies to
// objects with `toJSON` properties as well, *unless* they are nested
// within an object or array).
stringify(getClass) === undef && // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
// FF 3.1b3 pass this test.
stringify(undef) === undef && // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
// respectively, if the value is omitted entirely.
stringify() === undef && // FF 3.1b1, 2 throw an error if the given value is not a number,
// string, array, object, Boolean, or `null` literal. This applies to
// objects with custom `toJSON` methods as well, unless they are nested
// inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
// methods entirely.
stringify(value) === "1" && stringify([value]) == "[1]" && // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
// `"[null]"`.
stringify([undef]) == "[null]" && // YUI 3.0.0b1 fails to serialize `null` literals.
stringify(null) == "null" && // FF 3.1b1, 2 halts serialization if an array contains a function:
// `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3
// elides non-JSON values from objects and arrays, unless they
// define custom `toJSON` methods.
stringify([undef, getClass, null]) == "[null,null,null]" && // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
// where character escape codes are expected (e.g., `\b` => `\u0008`).
stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
stringify(null, value) === "1" && stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
// serialize extended years.
stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && // The milliseconds are optional in ES 5, but required in 5.1.
stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
// four-digit years instead of six-digit years. Credits: @Yaffle.
stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
// values less than 1000. Credits: @Yaffle.
stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; } catch (exception) { stringifySupported = false; } } isSupported = stringifySupported; } // Test `JSON.parse`.
if (name == "json-parse") { var parse = JSON3.parse; if (typeof parse == "function") { try { // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
// Conforming implementations should also coerce the initial argument to
// a string prior to parsing.
if (parse("0") === 0 && !parse(false)) { // Simple parsing test.
value = parse(serialized); var parseSupported = value["a"].length == 5 && value["a"][0] === 1; if (parseSupported) { try { // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
parseSupported = !parse('"\t"'); } catch (exception) {} if (parseSupported) { try { // FF 4.0 and 4.0.1 allow leading `+` signs and leading
// decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow
// certain octal literals.
parseSupported = parse("01") !== 1; } catch (exception) {} } if (parseSupported) { try { // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal
// points. These environments, along with FF 3.1b1 and 2,
// also allow trailing commas in JSON objects and arrays.
parseSupported = parse("1.") !== 1; } catch (exception) {} } } } } catch (exception) { parseSupported = false; } } isSupported = parseSupported; } } return has[name] = !!isSupported; }
if (!has("json")) { // Common `[[Class]]` name aliases.
var functionClass = "[object Function]"; var dateClass = "[object Date]"; var numberClass = "[object Number]"; var stringClass = "[object String]"; var arrayClass = "[object Array]"; var booleanClass = "[object Boolean]";
// Detect incomplete support for accessing string characters by index.
var charIndexBuggy = has("bug-string-char-index");
// Define additional utility methods if the `Date` methods are buggy.
if (!isExtended) { var floor = Math.floor; // A mapping between the months of the year and the number of days between
// January 1st and the first of the respective month.
var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; // Internal: Calculates the number of days between the Unix epoch and the
// first day of the given month.
var getDay = function (year, month) { return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); }; }
// Internal: Determines if a property is a direct property of the given
// object. Delegates to the native `Object#hasOwnProperty` method.
if (!(isProperty = {}.hasOwnProperty)) { isProperty = function (property) { var members = {}, constructor; if ((members.__proto__ = null, members.__proto__ = { // The *proto* property cannot be set multiple times in recent
// versions of Firefox and SeaMonkey.
"toString": 1 }, members).toString != getClass) { // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
// supports the mutable *proto* property.
isProperty = function (property) { // Capture and break the object's prototype chain (see section 8.6.2
// of the ES 5.1 spec). The parenthesized expression prevents an
// unsafe transformation by the Closure Compiler.
var original = this.__proto__, result = property in (this.__proto__ = null, this); // Restore the original prototype chain.
this.__proto__ = original; return result; }; } else { // Capture a reference to the top-level `Object` constructor.
constructor = members.constructor; // Use the `constructor` property to simulate `Object#hasOwnProperty` in
// other environments.
isProperty = function (property) { var parent = (this.constructor || constructor).prototype; return property in this && !(property in parent && this[property] === parent[property]); }; } members = null; return isProperty.call(this, property); }; }
// Internal: A set of primitive types used by `isHostType`.
var PrimitiveTypes = { 'boolean': 1, 'number': 1, 'string': 1, 'undefined': 1 };
// Internal: Determines if the given object `property` value is a
// non-primitive.
var isHostType = function (object, property) { var type = typeof object[property]; return type == 'object' ? !!object[property] : !PrimitiveTypes[type]; };
// Internal: Normalizes the `for...in` iteration algorithm across
// environments. Each enumerated key is yielded to a `callback` function.
forEach = function (object, callback) { var size = 0, Properties, members, property;
// Tests for bugs in the current environment's `for...in` algorithm. The
// `valueOf` property inherits the non-enumerable flag from
// `Object.prototype` in older versions of IE, Netscape, and Mozilla.
(Properties = function () { this.valueOf = 0; }).prototype.valueOf = 0;
// Iterate over a new instance of the `Properties` class.
members = new Properties(); for (property in members) { // Ignore all properties inherited from `Object.prototype`.
if (isProperty.call(members, property)) { size++; } } Properties = members = null;
// Normalize the iteration algorithm.
if (!size) { // A list of non-enumerable properties inherited from `Object.prototype`.
members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
// properties.
forEach = function (object, callback) { var isFunction = getClass.call(object) == functionClass, property, length; var hasProperty = !isFunction && typeof object.constructor != 'function' && isHostType(object, 'hasOwnProperty') ? object.hasOwnProperty : isProperty; for (property in object) { // Gecko <= 1.0 enumerates the `prototype` property of functions under
// certain conditions; IE does not.
if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { callback(property); } } // Manually invoke the callback for each non-enumerable property.
for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property)); }; } else if (size == 2) { // Safari <= 2.0.4 enumerates shadowed properties twice.
forEach = function (object, callback) { // Create a set of iterated properties.
var members = {}, isFunction = getClass.call(object) == functionClass, property; for (property in object) { // Store each property name to prevent double enumeration. The
// `prototype` property of functions is not enumerated due to cross-
// environment inconsistencies.
if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) { callback(property); } } }; } else { // No bugs detected; use the standard `for...in` algorithm.
forEach = function (object, callback) { var isFunction = getClass.call(object) == functionClass, property, isConstructor; for (property in object) { if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { callback(property); } } // Manually invoke the callback for the `constructor` property due to
// cross-environment inconsistencies.
if (isConstructor || isProperty.call(object, (property = "constructor"))) { callback(property); } }; } return forEach(object, callback); };
// Public: Serializes a JavaScript `value` as a JSON string. The optional
// `filter` argument may specify either a function that alters how object and
// array members are serialized, or an array of strings and numbers that
// indicates which properties should be serialized. The optional `width`
// argument may be either a string or number that specifies the indentation
// level of the output.
if (!has("json-stringify")) { // Internal: A map of control characters and their escaped equivalents.
var Escapes = { 92: "\\\\", 34: '\\"', 8: "\\b", 12: "\\f", 10: "\\n", 13: "\\r", 9: "\\t" };
// Internal: Converts `value` into a zero-padded string such that its
// length is at least equal to `width`. The `width` must be <= 6.
var leadingZeroes = "000000"; var toPaddedString = function (width, value) { // The `|| 0` expression is necessary to work around a bug in
// Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
return (leadingZeroes + (value || 0)).slice(-width); };
// Internal: Double-quotes a string `value`, replacing all ASCII control
// characters (characters with code unit values between 0 and 31) with
// their escaped equivalents. This is an implementation of the
// `Quote(value)` operation defined in ES 5.1 section 15.12.3.
var unicodePrefix = "\\u00"; var quote = function (value) { var result = '"', index = 0, length = value.length, isLarge = length > 10 && charIndexBuggy, symbols; if (isLarge) { symbols = value.split(""); } for (; index < length; index++) { var charCode = value.charCodeAt(index); // If the character is a control character, append its Unicode or
// shorthand escape sequence; otherwise, append the character as-is.
switch (charCode) { case 8: case 9: case 10: case 12: case 13: case 34: case 92: result += Escapes[charCode]; break; default: if (charCode < 32) { result += unicodePrefix + toPaddedString(2, charCode.toString(16)); break; } result += isLarge ? symbols[index] : charIndexBuggy ? value.charAt(index) : value[index]; } } return result + '"'; };
// Internal: Recursively serializes an object. Implements the
// `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result; try { // Necessary for host object support.
value = object[property]; } catch (exception) {} if (typeof value == "object" && value) { className = getClass.call(value); if (className == dateClass && !isProperty.call(value, "toJSON")) { if (value > -1 / 0 && value < 1 / 0) { // Dates are serialized according to the `Date#toJSON` method
// specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
// for the ISO 8601 date time string format.
if (getDay) { // Manually compute the year, month, date, hours, minutes,
// seconds, and milliseconds if the `getUTC*` methods are
// buggy. Adapted from @Yaffle's `date-shim` project.
date = floor(value / 864e5); for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); date = 1 + date - getDay(year, month); // The `time` value specifies the time within the day (see ES
// 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
// to compute `A modulo B`, as the `%` operator does not
// correspond to the `modulo` operation for negative numbers.
time = (value % 864e5 + 864e5) % 864e5; // The hours, minutes, seconds, and milliseconds are obtained by
// decomposing the time within the day. See section 15.9.1.10.
hours = floor(time / 36e5) % 24; minutes = floor(time / 6e4) % 60; seconds = floor(time / 1e3) % 60; milliseconds = time % 1e3; } else { year = value.getUTCFullYear(); month = value.getUTCMonth(); date = value.getUTCDate(); hours = value.getUTCHours(); minutes = value.getUTCMinutes(); seconds = value.getUTCSeconds(); milliseconds = value.getUTCMilliseconds(); } // Serialize extended years correctly.
value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + // Months, dates, hours, minutes, and seconds should have two
// digits; milliseconds should have three.
"T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + // Milliseconds are optional in ES 5.0, but required in 5.1.
"." + toPaddedString(3, milliseconds) + "Z"; } else { value = null; } } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) { // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
// `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
// ignores all `toJSON` methods on these objects unless they are
// defined directly on an instance.
value = value.toJSON(property); } } if (callback) { // If a replacement function was provided, call it to obtain the value
// for serialization.
value = callback.call(object, property, value); } if (value === null) { return "null"; } className = getClass.call(value); if (className == booleanClass) { // Booleans are represented literally.
return "" + value; } else if (className == numberClass) { // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
// `"null"`.
return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; } else if (className == stringClass) { // Strings are double-quoted and escaped.
return quote("" + value); } // Recursively serialize objects and arrays.
if (typeof value == "object") { // Check for cyclic structures. This is a linear search; performance
// is inversely proportional to the number of unique nested objects.
for (length = stack.length; length--;) { if (stack[length] === value) { // Cyclic structures cannot be serialized by `JSON.stringify`.
throw TypeError(); } } // Add the object to the stack of traversed objects.
stack.push(value); results = []; // Save the current indentation level and indent one additional level.
prefix = indentation; indentation += whitespace; if (className == arrayClass) { // Recursively serialize array elements.
for (index = 0, length = value.length; index < length; index++) { element = serialize(index, value, callback, properties, whitespace, indentation, stack); results.push(element === undef ? "null" : element); } result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; } else { // Recursively serialize object members. Members are selected from
// either a user-specified list of property names, or the object
// itself.
forEach(properties || value, function (property) { var element = serialize(property, value, callback, properties, whitespace, indentation, stack); if (element !== undef) { // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
// is not the empty string, let `member` {quote(property) + ":"}
// be the concatenation of `member` and the `space` character."
// The "`space` character" refers to the literal space
// character, not the `space` {width} argument provided to
// `JSON.stringify`.
results.push(quote(property) + ":" + (whitespace ? " " : "") + element); } }); result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; } // Remove the object from the traversed object stack.
stack.pop(); return result; } };
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
JSON3.stringify = function (source, filter, width) { var whitespace, callback, properties, className; if (typeof filter == "function" || typeof filter == "object" && filter) { if ((className = getClass.call(filter)) == functionClass) { callback = filter; } else if (className == arrayClass) { // Convert the property names array into a makeshift set.
properties = {}; for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1)); } } if (width) { if ((className = getClass.call(width)) == numberClass) { // Convert the `width` to an integer and create a string containing
// `width` number of space characters.
if ((width -= width % 1) > 0) { for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " "); } } else if (className == stringClass) { whitespace = width.length <= 10 ? width : width.slice(0, 10); } } // Opera <= 7.54u2 discards the values associated with empty string keys
// (`""`) only if they are used directly within an object member list
// (e.g., `!("" in { "": 1})`).
return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); }; }
// Public: Parses a JSON source string.
if (!has("json-parse")) { var fromCharCode = String.fromCharCode;
// Internal: A map of escaped control characters and their unescaped
// equivalents.
var Unescapes = { 92: "\\", 34: '"', 47: "/", 98: "\b", 116: "\t", 110: "\n", 102: "\f", 114: "\r" };
// Internal: Stores the parser state.
var Index, Source;
// Internal: Resets the parser state and throws a `SyntaxError`.
var abort = function() { Index = Source = null; throw SyntaxError(); };
// Internal: Returns the next token, or `"$"` if the parser has reached
// the end of the source string. A token may be a string, number, `null`
// literal, or Boolean literal.
var lex = function () { var source = Source, length = source.length, value, begin, position, isSigned, charCode; while (Index < length) { charCode = source.charCodeAt(Index); switch (charCode) { case 9: case 10: case 13: case 32: // Skip whitespace tokens, including tabs, carriage returns, line
// feeds, and space characters.
Index++; break; case 123: case 125: case 91: case 93: case 58: case 44: // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
// the current position.
value = charIndexBuggy ? source.charAt(Index) : source[Index]; Index++; return value; case 34: // `"` delimits a JSON string; advance to the next character and
// begin parsing the string. String tokens are prefixed with the
// sentinel `@` character to distinguish them from punctuators and
// end-of-string tokens.
for (value = "@", Index++; Index < length;) { charCode = source.charCodeAt(Index); if (charCode < 32) { // Unescaped ASCII control characters (those with a code unit
// less than the space character) are not permitted.
abort(); } else if (charCode == 92) { // A reverse solidus (`\`) marks the beginning of an escaped
// control character (including `"`, `\`, and `/`) or Unicode
// escape sequence.
charCode = source.charCodeAt(++Index); switch (charCode) { case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: // Revive escaped control characters.
value += Unescapes[charCode]; Index++; break; case 117: // `\u` marks the beginning of a Unicode escape sequence.
// Advance to the first character and validate the
// four-digit code point.
begin = ++Index; for (position = Index + 4; Index < position; Index++) { charCode = source.charCodeAt(Index); // A valid sequence comprises four hexdigits (case-
// insensitive) that form a single hexadecimal value.
if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { // Invalid Unicode escape sequence.
abort(); } } // Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index)); break; default: // Invalid escape sequence.
abort(); } } else { if (charCode == 34) { // An unescaped double-quote character marks the end of the
// string.
break; } charCode = source.charCodeAt(Index); begin = Index; // Optimize for the common case where a string is valid.
while (charCode >= 32 && charCode != 92 && charCode != 34) { charCode = source.charCodeAt(++Index); } // Append the string as-is.
value += source.slice(begin, Index); } } if (source.charCodeAt(Index) == 34) { // Advance to the next character and return the revived string.
Index++; return value; } // Unterminated string.
abort(); default: // Parse numbers and literals.
begin = Index; // Advance past the negative sign, if one is specified.
if (charCode == 45) { isSigned = true; charCode = source.charCodeAt(++Index); } // Parse an integer or floating-point value.
if (charCode >= 48 && charCode <= 57) { // Leading zeroes are interpreted as octal literals.
if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { // Illegal octal literal.
abort(); } isSigned = false; // Parse the integer component.
for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); // Floats cannot contain a leading decimal point; however, this
// case is already accounted for by the parser.
if (source.charCodeAt(Index) == 46) { position = ++Index; // Parse the decimal component.
for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); if (position == Index) { // Illegal trailing decimal.
abort(); } Index = position; } // Parse exponents. The `e` denoting the exponent is
// case-insensitive.
charCode = source.charCodeAt(Index); if (charCode == 101 || charCode == 69) { charCode = source.charCodeAt(++Index); // Skip past the sign following the exponent, if one is
// specified.
if (charCode == 43 || charCode == 45) { Index++; } // Parse the exponential component.
for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++); if (position == Index) { // Illegal empty exponent.
abort(); } Index = position; } // Coerce the parsed value to a JavaScript number.
return +source.slice(begin, Index); } // A negative sign may only precede numbers.
if (isSigned) { abort(); } // `true`, `false`, and `null` literals.
if (source.slice(Index, Index + 4) == "true") { Index += 4; return true; } else if (source.slice(Index, Index + 5) == "false") { Index += 5; return false; } else if (source.slice(Index, Index + 4) == "null") { Index += 4; return null; } // Unrecognized token.
abort(); } } // Return the sentinel `$` character if the parser has reached the end
// of the source string.
return "$"; };
// Internal: Parses a JSON `value` token.
var get = function (value) { var results, hasMembers; if (value == "$") { // Unexpected end of input.
abort(); } if (typeof value == "string") { if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { // Remove the sentinel `@` character.
return value.slice(1); } // Parse object and array literals.
if (value == "[") { // Parses a JSON array, returning a new JavaScript array.
results = []; for (;; hasMembers || (hasMembers = true)) { value = lex(); // A closing square bracket marks the end of the array literal.
if (value == "]") { break; } // If the array literal contains elements, the current token
// should be a comma separating the previous element from the
// next.
if (hasMembers) { if (value == ",") { value = lex(); if (value == "]") { // Unexpected trailing `,` in array literal.
abort(); } } else { // A `,` must separate each array element.
abort(); } } // Elisions and leading commas are not permitted.
if (value == ",") { abort(); } results.push(get(value)); } return results; } else if (value == "{") { // Parses a JSON object, returning a new JavaScript object.
results = {}; for (;; hasMembers || (hasMembers = true)) { value = lex(); // A closing curly brace marks the end of the object literal.
if (value == "}") { break; } // If the object literal contains members, the current token
// should be a comma separator.
if (hasMembers) { if (value == ",") { value = lex(); if (value == "}") { // Unexpected trailing `,` in object literal.
abort(); } } else { // A `,` must separate each object member.
abort(); } } // Leading commas are not permitted, object property names must be
// double-quoted strings, and a `:` must separate each property
// name and value.
if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { abort(); } results[value.slice(1)] = get(lex()); } return results; } // Unexpected token encountered.
abort(); } return value; };
// Internal: Updates a traversed object member.
var update = function(source, property, callback) { var element = walk(source, property, callback); if (element === undef) { delete source[property]; } else { source[property] = element; } };
// Internal: Recursively traverses a parsed JSON object, invoking the
// `callback` function for each value. This is an implementation of the
// `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
var walk = function (source, property, callback) { var value = source[property], length; if (typeof value == "object" && value) { // `forEach` can't be used to traverse an array in Opera <= 8.54
// because its `Object#hasOwnProperty` implementation returns `false`
// for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`).
if (getClass.call(value) == arrayClass) { for (length = value.length; length--;) { update(value, length, callback); } } else { forEach(value, function (property) { update(value, property, callback); }); } } return callback.call(source, property, value); };
// Public: `JSON.parse`. See ES 5.1 section 15.12.2.
JSON3.parse = function (source, callback) { var result, value; Index = 0; Source = "" + source; result = get(lex()); // If a JSON string contains multiple tokens, it is invalid.
if (lex() != "$") { abort(); } // Reset the parser state.
Index = Source = null; return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; }; } }
// Export for asynchronous module loaders.
if (isLoader) { define(function () { return JSON3; }); } }(this));
|