Update vendors, builds, and docs.

Former-commit-id: 08cad493d719ec3ebaa85e0ff279c49f1e2b3655
This commit is contained in:
John-David Dalton
2013-07-03 23:14:06 -07:00
parent 112cdb4842
commit 63a1c8b63e
25 changed files with 4175 additions and 1522 deletions

View File

@@ -1,5 +1,5 @@
Copyright (c) 2012 Kit Cambridge.
http://kitcambridge.github.com
Copyright (c) 2012-2013 Kit Cambridge.
http://kitcambridge.be/
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

124
vendor/json3/README.md vendored
View File

@@ -1,124 +0,0 @@
# JSON 3 #
![JSON 3 Logo](http://bestiejs.github.com/json3/page/logo.png)
**JSON 3** is a modern JSON implementation compatible with a variety of JavaScript platforms, including Internet Explorer 6, Opera 7, Safari 2, and Netscape 6. The current version is **3.2.4**.
- [Development Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.js) *(36.5 KB; uncompressed with comments)*
- [Production Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.min.js) *(3.0 KB; compressed and `gzip`-ped)*
[JSON](http://json.org/) is a language-independent data interchange format based on a loose subset of the JavaScript grammar. Originally popularized by [Douglas Crockford](http://www.crockford.com/), the format was standardized in the [fifth edition](http://es5.github.com/) of the ECMAScript specification. The 5.1 edition, ratified in June 2011, incorporates several modifications to the grammar pertaining to the serialization of dates.
JSON 3 exposes two functions: `stringify()` for [serializing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify) a JavaScript value to JSON, and `parse()` for [producing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse) a JavaScript value from a JSON source string. It is a **drop-in replacement** for [JSON 2](http://json.org/js). The functions behave exactly as described in the ECMAScript spec, **except** for the date serialization discrepancy noted below.
The JSON 3 parser does **not** use `eval` or regular expressions. This provides security and performance benefits in obsolete and mobile environments, where the margin is particularly significant. The complete [benchmark suite](http://jsperf.com/json3) is available on [jsPerf](http://jsperf.com/).
The project is [hosted on GitHub](http://git.io/json3), along with the [unit tests](http://bestiejs.github.com/json3/test/test_browser.html). It is part of the [BestieJS](https://github.com/bestiejs) family, a collection of best-in-class JavaScript libraries that promote cross-platform support, specification precedents, unit testing, and plenty of documentation.
# Changes from JSON 2 #
JSON 3...
* Correctly serializes primitive wrapper objects.
* Throws a `TypeError` when serializing cyclic structures (JSON 2 recurses until the call stack overflows).
* Utilizes **feature tests** to detect broken or incomplete *native* JSON implementations (JSON 2 only checks for the presence of the native functions). The tests are only executed once at runtime, so there is no additional performance cost when parsing or serializing values.
**As of v3.2.3**, JSON 3 is compatible with [Prototype](http://prototypejs.org) 1.6.1 and older.
In contrast to JSON 2, JSON 3 **does not**...
* Add `toJSON()` methods to the `Boolean`, `Number`, and `String` prototypes. These are not part of any standard, and are made redundant by the design of the `stringify()` implementation.
* Add `toJSON()` or `toISOString()` methods to `Date.prototype`. See the note about date serialization below.
## Date Serialization
**JSON 3 deviates from the specification in one important way**: it does not define `Date#toISOString()` or `Date#toJSON()`. This preserves CommonJS compatibility and avoids polluting native prototypes. Instead, date serialization is performed internally by the `stringify()` implementation: if a date object does not define a custom `toJSON()` method, it is serialized as a [simplified ISO 8601 date-time string](http://es5.github.com/#x15.9.1.15).
**Several native `Date#toJSON()` implementations produce date time strings that do *not* conform to the grammar outlined in the spec**. For instance, all versions of Safari 4, as well as JSON 2, fail to serialize extended years correctly. Furthermore, JSON 2 and older implementations omit the milliseconds from the date-time string (optional in ES 5, but required in 5.1). Finally, in all versions of Safari 4 and 5, serializing an invalid date will produce the string `"Invalid Date"`, rather than `null`. Because these environments exhibit other serialization bugs, however, JSON 3 will override the native `stringify()` implementation.
Portions of the date serialization code are adapted from the [`date-shim`](https://github.com/Yaffle/date-shim) project.
# Usage #
## Web Browsers
<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.min.js"></script>
<script>
JSON.stringify({"Hello": 123});
// => '{"Hello":123}'
JSON.parse("[[1, 2, 3], 1, 2, 3, 4]", function (key, value) {
if (typeof value == "number") {
value = value % 2 ? "Odd" : "Even";
}
return value;
});
// => [["Odd", "Even", "Odd"], "Odd", "Even", "Odd", "Even"]
</script>
## CommonJS Environments
var JSON3 = require("./path/to/json3");
JSON3.parse("[1, 2, 3]");
// => [1, 2, 3]
## JavaScript Engines
load("path/to/json3.js");
JSON.stringify({"Hello": 123, "Good-bye": 456}, ["Hello"], "\t");
// => '{\n\t"Hello": 123\n}'
# Compatibility #
JSON 3 has been **tested** with the following web browsers, CommonJS environments, and JavaScript engines.
## Web Browsers
- Windows [Internet Explorer](http://www.microsoft.com/windows/internet-explorer), version 6.0 and higher
- Mozilla [Firefox](http://www.mozilla.com/firefox), version 1.0 and higher
- Apple [Safari](http://www.apple.com/safari), version 2.0 and higher
- [Opera](http://www.opera.com) 7.02 and higher
- [Mozilla](http://sillydog.org/narchive/gecko.php) 1.0, [Netscape](http://sillydog.org/narchive/) 6.2.3, and [SeaMonkey](http://www.seamonkey-project.org/) 1.0 and higher
## CommonJS Environments
- [Node](http://nodejs.org/) 0.2.6 and higher
- [RingoJS](http://ringojs.org/) 0.4 and higher
- [Narwhal](http://narwhaljs.org/) 0.3.2 and higher
## JavaScript Engines
- Mozilla [Rhino](http://www.mozilla.org/rhino) 1.5R5 and higher
- WebKit [JSC](https://trac.webkit.org/wiki/JSC)
- Google [V8](http://code.google.com/p/v8)
## Known Incompatibilities
* Attempting to serialize the `arguments` object may produce inconsistent results across environments due to specification version differences. As a workaround, please convert the `arguments` object to an array first: `JSON.stringify([].slice.call(arguments, 0))`.
## Required Native Methods
JSON 3 assumes that the following methods exist and function as described in the ECMAScript specification:
- The `Number`, `String`, `Array`, `Object`, `Date`, `SyntaxError`, and `TypeError` constructors.
- `String.fromCharCode`
- `Object#toString`
- `Function#call`
- `Math.floor`
- `Number#toString`
- `Date#valueOf`
- `String.prototype`: `indexOf`, `charCodeAt`, `charAt`, `slice`.
- `Array.prototype`: `push`, `pop`, `join`.
# Contribute #
Check out a working copy of the JSON 3 source code with [Git](http://git-scm.com/):
$ git clone git://github.com/bestiejs/json3.git
$ cd json3
$ git submodule update --init
If you'd like to contribute a feature or bug fix, you can [fork](http://help.github.com/fork-a-repo/) JSON 3, commit your changes, and [send a pull request](http://help.github.com/send-pull-requests/). Please make sure to update the unit tests in the `test` directory as well.
Alternatively, you can use the [GitHub issue tracker](https://github.com/bestiejs/json3/issues) to submit bug reports, feature requests, and questions, or send tweets to [@kitcambridge](http://twitter.com/kitcambridge).
JSON 3 is released under the [MIT License](http://kit.mit-license.org/).

View File

@@ -1,41 +1,36 @@
/*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */
;(function () {
/*! JSON v3.2.5 | 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, JSON3 = !isLoader && typeof exports == "object" && exports;
var isLoader = typeof define === "function" && define.amd, JSON3 = typeof exports == "object" && exports;
if (JSON3 || isLoader) {
if (typeof JSON == "object" && JSON) {
// Delegate to the native `stringify` and `parse` implementations in
// asynchronous module loaders and CommonJS environments.
if (isLoader) {
JSON3 = JSON;
} else {
if (JSON3) {
JSON3.stringify = JSON.stringify;
JSON3.parse = JSON.parse;
} else {
JSON3 = JSON;
}
} else if (isLoader) {
JSON3 = this.JSON = {};
JSON3 = window.JSON = {};
}
} else {
// Export for web browsers and JavaScript engines.
JSON3 = this.JSON || (this.JSON = {});
JSON3 = window.JSON || (window.JSON = {});
}
// Local variables.
var Escapes, toPaddedString, quote, serialize;
var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source;
// Test the `Date#getUTC*` methods. Based on work by @Yaffle.
var isExtended = new Date(-3509827334573292), floor, Months, getDay;
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 &&
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]).
@@ -45,11 +40,17 @@
// Internal: Determines whether the native `JSON.stringify` and `parse`
// implementations are spec-compliant. Based on work by Ken Snyder.
function has(name) {
var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
if (all || name == "json-stringify" || name == "json-parse") {
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.
return "a"[0] != "a";
}
var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', isAll = name == "json";
if (isAll || name == "json-stringify" || name == "json-parse") {
// Test `JSON.stringify`.
if (name == "json-stringify" || all) {
if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) {
if (name == "json-stringify" || isAll) {
var stringify = JSON3.stringify, stringifySupported = typeof stringify == "function" && isExtended;
if (stringifySupported) {
// A test function object with a custom `toJSON` method.
(value = function () {
return 1;
@@ -58,86 +59,88 @@
stringifySupported =
// Firefox 3.1b1 and b2 serialize string, number, and boolean
// primitives as object literals.
JSON3.stringify(0) === "0" &&
stringify(0) === "0" &&
// FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
// literals.
JSON3.stringify(new Number()) === "0" &&
JSON3.stringify(new String()) == '""' &&
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).
JSON3.stringify(getClass) === undef &&
stringify(getClass) === undef &&
// IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
// FF 3.1b3 pass this test.
JSON3.stringify(undef) === undef &&
stringify(undef) === undef &&
// Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
// respectively, if the value is omitted entirely.
JSON3.stringify() === undef &&
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.
JSON3.stringify(value) === "1" &&
JSON3.stringify([value]) == "[1]" &&
stringify(value) === "1" &&
stringify([value]) == "[1]" &&
// Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
// `"[null]"`.
JSON3.stringify([undef]) == "[null]" &&
stringify([undef]) == "[null]" &&
// YUI 3.0.0b1 fails to serialize `null` literals.
JSON3.stringify(null) == "null" &&
stringify(null) == "null" &&
// FF 3.1b1, 2 halts serialization if an array contains a function:
// `[1, true, getClass, 1]` serializes as "[1,true,],". These versions
// of Firefox also allow trailing commas in JSON objects and arrays.
// FF 3.1b3 elides non-JSON values from objects and arrays, unless they
// define custom `toJSON` methods.
JSON3.stringify([undef, getClass, null]) == "[null,null,null]" &&
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`).
JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized &&
stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized &&
// FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
JSON3.stringify(null, value) === "1" &&
JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
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.
JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
// The milliseconds are optional in ES 5, but required in 5.1.
JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
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.
JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
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.
JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
} catch (exception) {
stringifySupported = false;
}
}
if (!all) {
if (!isAll) {
return stringifySupported;
}
}
// Test `JSON.parse`.
if (name == "json-parse" || all) {
if (typeof JSON3.parse == "function") {
if (name == "json-parse" || isAll) {
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 (JSON3.parse("0") === 0 && !JSON3.parse(false)) {
if (parse("0") === 0 && !parse(false)) {
// Simple parsing test.
value = JSON3.parse(serialized);
if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) {
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 = !JSON3.parse('"\t"');
parseSupported = !parse('"\t"');
} catch (exception) {}
if (parseSupported) {
try {
// FF 4.0 and 4.0.1 allow leading `+` signs, and leading and
// trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also
// allow certain octal literals.
parseSupported = JSON3.parse("01") != 1;
parseSupported = parse("01") !== 1;
} catch (exception) {}
}
}
@@ -146,7 +149,7 @@
parseSupported = false;
}
}
if (!all) {
if (!isAll) {
return parseSupported;
}
}
@@ -155,19 +158,30 @@
}
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) {
floor = Math.floor;
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.
Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
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.
getDay = function (year, 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)) {
@@ -233,7 +247,7 @@
// IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
// properties.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, length;
var isFunction = getClass.call(object) == functionClass, property, length;
for (property in object) {
// Gecko <= 1.0 enumerates the `prototype` property of functions under
// certain conditions; IE does not.
@@ -248,7 +262,7 @@
// Safari <= 2.0.4 enumerates shadowed properties twice.
forEach = function (object, callback) {
// Create a set of iterated properties.
var members = {}, isFunction = getClass.call(object) == "[object Function]", property;
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-
@@ -261,7 +275,7 @@
} else {
// No bugs detected; use the standard `for...in` algorithm.
forEach = function (object, callback) {
var isFunction = getClass.call(object) == "[object Function]", property, isConstructor;
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);
@@ -285,48 +299,65 @@
// level of the output.
if (!has("json-stringify")) {
// Internal: A map of control characters and their escaped equivalents.
Escapes = {
"\\": "\\\\",
'"': '\\"',
"\b": "\\b",
"\f": "\\f",
"\n": "\\n",
"\r": "\\r",
"\t": "\\t"
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.
toPaddedString = function (width, value) {
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 ("000000" + (value || 0)).slice(-width);
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.
quote = function (value) {
var result = '"', index = 0, symbol;
for (; symbol = value.charAt(index); index++) {
// Escape the reverse solidus, double quote, backspace, form feed, line
// feed, carriage return, and tab characters.
result += '\\"\b\f\n\r\t'.indexOf(symbol) > -1 ? Escapes[symbol] :
// If the character is a control character, append its Unicode escape
// sequence; otherwise, append the character as-is.
(Escapes[symbol] = symbol < " " ? "\\u00" + toPaddedString(2, symbol.charCodeAt(0).toString(16)) : symbol);
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.
serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, hasMembers, result;
try {
// Necessary for host object support.
value = object[property];
} catch (exception) {}
if (typeof value == "object" && value) {
className = getClass.call(value);
if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
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
@@ -370,7 +401,7 @@
} else {
value = null;
}
} else if (typeof value.toJSON == "function" && ((className != "[object Number]" && className != "[object String]" && className != "[object Array]") || isProperty.call(value, "toJSON"))) {
} 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
@@ -387,14 +418,14 @@
return "null";
}
className = getClass.call(value);
if (className == "[object Boolean]") {
if (className == booleanClass) {
// Booleans are represented literally.
return "" + value;
} else if (className == "[object Number]") {
} 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 == "[object String]") {
} else if (className == stringClass) {
// Strings are double-quoted and escaped.
return quote(value);
}
@@ -414,13 +445,13 @@
// Save the current indentation level and indent one additional level.
prefix = indentation;
indentation += whitespace;
if (className == "[object Array]") {
if (className == arrayClass) {
// Recursively serialize array elements.
for (index = 0, length = value.length; index < length; any || (any = true), index++) {
for (index = 0, length = value.length; index < length; hasMembers || (hasMembers = true), index++) {
element = serialize(index, value, callback, properties, whitespace, indentation, stack);
results.push(element === undef ? "null" : element);
}
result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
result = hasMembers ? (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
@@ -436,9 +467,9 @@
// `JSON.stringify`.
results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
}
any || (any = true);
hasMembers || (hasMembers = true);
});
result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
result = hasMembers ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
}
// Remove the object from the traversed object stack.
stack.pop();
@@ -448,24 +479,24 @@
// Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
JSON3.stringify = function (source, filter, width) {
var whitespace, callback, properties, index, length, value;
var whitespace, callback, properties;
if (typeof filter == "function" || typeof filter == "object" && filter) {
if (getClass.call(filter) == "[object Function]") {
if (getClass.call(filter) == functionClass) {
callback = filter;
} else if (getClass.call(filter) == "[object Array]") {
} else if (getClass.call(filter) == arrayClass) {
// Convert the property names array into a makeshift set.
properties = {};
for (index = 0, length = filter.length; index < length; value = filter[index++], ((getClass.call(value) == "[object String]" || getClass.call(value) == "[object Number]") && (properties[value] = 1)));
for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((getClass.call(value) == stringClass || getClass.call(value) == numberClass) && (properties[value] = 1)));
}
}
if (width) {
if (getClass.call(width) == "[object Number]") {
if (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 (getClass.call(width) == "[object String]") {
} else if (getClass.call(width) == stringClass) {
whitespace = width.length <= 10 ? width : width.slice(0, 10);
}
}
@@ -478,22 +509,26 @@
// Public: Parses a JSON source string.
if (!has("json-parse")) {
fromCharCode = String.fromCharCode;
var fromCharCode = String.fromCharCode;
// Internal: A map of escaped control characters and their unescaped
// equivalents.
Unescapes = {
"\\": "\\",
'"': '"',
"/": "/",
"b": "\b",
"t": "\t",
"n": "\n",
"f": "\f",
"r": "\r"
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`.
abort = function() {
var abort = function() {
Index = Source = null;
throw SyntaxError();
};
@@ -501,140 +536,156 @@
// 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.
lex = function () {
var source = Source, length = source.length, symbol, value, begin, position, sign;
var lex = function () {
var source = Source, length = source.length, value, begin, position, isSigned, charCode;
while (Index < length) {
symbol = source.charAt(Index);
if ("\t\r\n ".indexOf(symbol) > -1) {
// Skip whitespace tokens, including tabs, carriage returns, line
// feeds, and space characters.
Index++;
} else if ("{}[]:,".indexOf(symbol) > -1) {
// Parse a punctuator token at the current position.
Index++;
return symbol;
} else if (symbol == '"') {
// Advance to the next character and parse a JSON string at the
// current position. String tokens are prefixed with the sentinel
// `@` character to distinguish them from punctuators.
for (value = "@", Index++; Index < length;) {
symbol = source.charAt(Index);
if (symbol < " ") {
// Unescaped ASCII control characters are not permitted.
abort();
} else if (symbol == "\\") {
// Parse escaped JSON control characters, `"`, `\`, `/`, and
// Unicode escape sequences.
symbol = source.charAt(++Index);
if ('\\"/btnfr'.indexOf(symbol) > -1) {
// Revive escaped control characters.
value += Unescapes[symbol];
Index++;
} else if (symbol == "u") {
// Advance to the first character of the escape sequence.
begin = ++Index;
// Validate the Unicode escape sequence.
for (position = Index + 4; Index < position; Index++) {
symbol = source.charAt(Index);
// A valid sequence comprises four hexdigits that form a
// single hexadecimal value.
if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
// Invalid Unicode escape sequence.
abort();
}
}
// Revive the escaped character.
value += fromCharCode("0x" + source.slice(begin, Index));
} else {
// Invalid escape sequence.
abort();
}
} else {
if (symbol == '"') {
// An unescaped double-quote character marks the end of the
// string.
break;
}
// Append the original character as-is.
value += symbol;
Index++;
}
}
if (source.charAt(Index) == '"') {
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 the revived string.
return value;
}
// Unterminated string.
abort();
} else {
// Parse numbers and literals.
begin = Index;
// Advance the scanner's position past the sign, if one is
// specified.
if (symbol == "-") {
sign = true;
symbol = source.charAt(++Index);
}
// Parse an integer or floating-point value.
if (symbol >= "0" && symbol <= "9") {
// Leading zeroes are interpreted as octal literals.
if (symbol == "0" && (symbol = source.charAt(Index + 1), symbol >= "0" && symbol <= "9")) {
// Illegal octal literal.
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();
}
sign = false;
// Parse the integer component.
for (; Index < length && (symbol = source.charAt(Index), symbol >= "0" && symbol <= "9"); Index++);
// Floats cannot contain a leading decimal point; however, this
// case is already accounted for by the parser.
if (source.charAt(Index) == ".") {
position = ++Index;
// Parse the decimal component.
for (; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
if (position == Index) {
// Illegal trailing decimal.
abort();
}
Index = position;
// `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;
}
// Parse exponents.
symbol = source.charAt(Index);
if (symbol == "e" || symbol == "E") {
// Skip past the sign following the exponent, if one is
// specified.
symbol = source.charAt(++Index);
if (symbol == "+" || symbol == "-") {
Index++;
}
// Parse the exponential component.
for (position = Index; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); 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 (sign) {
// Unrecognized token.
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
@@ -643,14 +694,14 @@
};
// Internal: Parses a JSON `value` token.
get = function (value) {
var results, any, key;
var get = function (value) {
var results, hasMembers;
if (value == "$") {
// Unexpected end of input.
abort();
}
if (typeof value == "string") {
if (value.charAt(0) == "@") {
if (value[0] == "@") {
// Remove the sentinel `@` character.
return value.slice(1);
}
@@ -658,7 +709,7 @@
if (value == "[") {
// Parses a JSON array, returning a new JavaScript array.
results = [];
for (;; any || (any = true)) {
for (;; hasMembers || (hasMembers = true)) {
value = lex();
// A closing square bracket marks the end of the array literal.
if (value == "]") {
@@ -667,7 +718,7 @@
// If the array literal contains elements, the current token
// should be a comma separating the previous element from the
// next.
if (any) {
if (hasMembers) {
if (value == ",") {
value = lex();
if (value == "]") {
@@ -689,7 +740,7 @@
} else if (value == "{") {
// Parses a JSON object, returning a new JavaScript object.
results = {};
for (;; any || (any = true)) {
for (;; hasMembers || (hasMembers = true)) {
value = lex();
// A closing curly brace marks the end of the object literal.
if (value == "}") {
@@ -697,7 +748,7 @@
}
// If the object literal contains members, the current token
// should be a comma separator.
if (any) {
if (hasMembers) {
if (value == ",") {
value = lex();
if (value == "}") {
@@ -712,7 +763,7 @@
// 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" || value.charAt(0) != "@" || lex() != ":") {
if (value == "," || typeof value != "string" || value[0] != "@" || lex() != ":") {
abort();
}
results[value.slice(1)] = get(lex());
@@ -726,7 +777,7 @@
};
// Internal: Updates a traversed object member.
update = function(source, property, callback) {
var update = function(source, property, callback) {
var element = walk(source, property, callback);
if (element === undef) {
delete source[property];
@@ -738,17 +789,17 @@
// 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.
walk = function (source, property, callback) {
var walk = function (source, property, callback) {
var value = source[property], length;
if (typeof value == "object" && value) {
if (getClass.call(value) == "[object Array]") {
// `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` can't be used to traverse an array in Opera <= 8.54,
// as `Object#hasOwnProperty` returns `false` for array indices
// (e.g., `![1, 2, 3].hasOwnProperty("0")`).
forEach(value, function (property) {
update(value, property, callback);
});
@@ -761,7 +812,7 @@
JSON3.parse = function (source, callback) {
var result, value;
Index = 0;
Source = source;
Source = "" + source;
result = get(lex());
// If a JSON string contains multiple tokens, it is invalid.
if (lex() != "$") {
@@ -769,7 +820,7 @@
}
// Reset the parser state.
Index = Source = null;
return callback && getClass.call(callback) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result;
return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result;
};
}
}
@@ -780,4 +831,4 @@
return JSON3;
});
}
}).call(this);
}(this));