From 28d505658407c49a962fa209a55ed6e595f71d13 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Fri, 12 Oct 2012 01:11:58 -0700 Subject: [PATCH] Add json3.js, remove json2 and jslitmus from vendors. --- test/backbone.html | 6 +- test/underscore.html | 6 +- vendor/backbone/test/vendor/jslitmus.js | 649 ------------------ vendor/backbone/test/vendor/json2.js | 481 ------------- vendor/json3/LICENSE | 20 + vendor/json3/README.md | 124 ++++ vendor/json3/lib/json3.js | 783 ++++++++++++++++++++++ vendor/underscore/test/vendor/jslitmus.js | 670 ------------------ 8 files changed, 931 insertions(+), 1808 deletions(-) delete mode 100644 vendor/backbone/test/vendor/jslitmus.js delete mode 100644 vendor/backbone/test/vendor/json2.js create mode 100644 vendor/json3/LICENSE create mode 100644 vendor/json3/README.md create mode 100644 vendor/json3/lib/json3.js delete mode 100644 vendor/underscore/test/vendor/jslitmus.js diff --git a/test/backbone.html b/test/backbone.html index ca1f4942da..77f3549ae2 100644 --- a/test/backbone.html +++ b/test/backbone.html @@ -8,9 +8,6 @@ body > #qunit-header { display: none; } - #jslitmus { - display: none; - } @@ -21,9 +18,8 @@

Backbone Speed Suite

Test

- + - + - + + +## 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/). \ No newline at end of file diff --git a/vendor/json3/lib/json3.js b/vendor/json3/lib/json3.js new file mode 100644 index 0000000000..b152b27ffb --- /dev/null +++ b/vendor/json3/lib/json3.js @@ -0,0 +1,783 @@ +/*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */ +;(function () { + // 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; + + 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 { + JSON3.stringify = JSON.stringify; + JSON3.parse = JSON.parse; + } + } else if (isLoader) { + JSON3 = this.JSON = {}; + } + } else { + // Export for web browsers and JavaScript engines. + JSON3 = this.JSON || (this.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; + + 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) { + 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") { + // Test `JSON.stringify`. + if (name == "json-stringify" || all) { + if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) { + // 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. + JSON3.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()) == '""' && + // 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 && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + JSON3.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 && + // 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]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + JSON3.stringify([undef]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + JSON3.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]" && + // 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 && + // 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]" && + // 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"' && + // The milliseconds are optional in ES 5, but required in 5.1. + JSON3.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"' && + // 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"'; + } catch (exception) { + stringifySupported = false; + } + } + if (!all) { + return stringifySupported; + } + } + // Test `JSON.parse`. + if (name == "json-parse" || all) { + if (typeof JSON3.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)) { + // Simple parsing test. + value = JSON3.parse(serialized); + if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) { + try { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !JSON3.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; + } catch (exception) {} + } + } + } + } catch (exception) { + parseSupported = false; + } + } + if (!all) { + return parseSupported; + } + } + return stringifySupported && parseSupported; + } + } + + if (!has("json")) { + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + 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]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + 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: 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, forEach; + + // 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) == "[object Function]", property, length; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && isProperty.call(object, property)) { + callback(property); + } + } + // Manually invoke the callback for each non-enumerable property. + for (length = members.length; property = members[--length]; isProperty.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) == "[object Function]", 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) == "[object Function]", 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. + Escapes = { + "\\": "\\\\", + '"': '\\"', + "\b": "\\b", + "\f": "\\f", + "\n": "\\n", + "\r": "\\r", + "\t": "\\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) { + // 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); + }; + + // 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); + } + 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; + if (typeof value == "object" && value) { + className = getClass.call(value); + if (className == "[object Date]" && !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 != "[object Number]" && className != "[object String]" && className != "[object Array]") || 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 == "[object Boolean]") { + // Booleans are represented literally. + return "" + value; + } else if (className == "[object Number]") { + // 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]") { + // 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 == "[object Array]") { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; any || (any = 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(",") + "]")) : "[]"; + } 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); + } + any || (any = true); + }); + result = any ? (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, index, length, value; + if (typeof filter == "function" || typeof filter == "object" && filter) { + if (getClass.call(filter) == "[object Function]") { + callback = filter; + } else if (getClass.call(filter) == "[object Array]") { + // 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))); + } + } + if (width) { + if (getClass.call(width) == "[object Number]") { + // 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]") { + 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")) { + 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" + }; + + // Internal: Resets the parser state and throws a `SyntaxError`. + 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. + lex = function () { + var source = Source, length = source.length, symbol, value, begin, position, sign; + 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) == '"') { + 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. + 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; + } + // 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) { + 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. + get = function (value) { + var results, any, key; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if (value.charAt(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 (;; any || (any = 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 (any) { + 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 (;; any || (any = 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 (any) { + 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" || value.charAt(0) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); + } + return results; + } + // Unexpected token encountered. + abort(); + } + return value; + }; + + // Internal: Updates a traversed object member. + 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. + walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + if (getClass.call(value) == "[object Array]") { + 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); + }); + } + } + 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) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; + } + } + + // Export for asynchronous module loaders. + if (isLoader) { + define(function () { + return JSON3; + }); + } +}).call(this); \ No newline at end of file diff --git a/vendor/underscore/test/vendor/jslitmus.js b/vendor/underscore/test/vendor/jslitmus.js deleted file mode 100644 index a0e9f806fa..0000000000 --- a/vendor/underscore/test/vendor/jslitmus.js +++ /dev/null @@ -1,670 +0,0 @@ -// JSLitmus.js -// -// History: -// 2008-10-27: Initial release -// 2008-11-09: Account for iteration loop overhead -// 2008-11-13: Added OS detection -// 2009-02-25: Create tinyURL automatically, shift-click runs tests in reverse -// -// Copyright (c) 2008-2009, Robert Kieffer -// All Rights Reserved -// -// 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 the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -(function() { - // Private methods and state - - // Get platform info but don't go crazy trying to recognize everything - // that's out there. This is just for the major platforms and OSes. - var platform = 'unknown platform', ua = navigator.userAgent; - - // Detect OS - var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|'); - var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null; - if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null; - - // Detect browser - var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null; - - // Detect version - var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)'); - var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null; - var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform'; - - /** - * A smattering of methods that are needed to implement the JSLitmus testbed. - */ - var jsl = { - /** - * Enhanced version of escape() - */ - escape: function(s) { - s = s.replace(/,/g, '\\,'); - s = escape(s); - s = s.replace(/\+/g, '%2b'); - s = s.replace(/ /g, '+'); - return s; - }, - - /** - * Get an element by ID. - */ - $: function(id) { - return document.getElementById(id); - }, - - /** - * Null function - */ - F: function() {}, - - /** - * Set the status shown in the UI - */ - status: function(msg) { - var el = jsl.$('jsl_status'); - if (el) el.innerHTML = msg || ''; - }, - - /** - * Convert a number to an abbreviated string like, "15K" or "10M" - */ - toLabel: function(n) { - if (n == Infinity) { - return 'Infinity'; - } else if (n > 1e9) { - n = Math.round(n/1e8); - return n/10 + 'B'; - } else if (n > 1e6) { - n = Math.round(n/1e5); - return n/10 + 'M'; - } else if (n > 1e3) { - n = Math.round(n/1e2); - return n/10 + 'K'; - } - return n; - }, - - /** - * Copy properties from src to dst - */ - extend: function(dst, src) { - for (var k in src) dst[k] = src[k]; return dst; - }, - - /** - * Like Array.join(), but for the key-value pairs in an object - */ - join: function(o, delimit1, delimit2) { - if (o.join) return o.join(delimit1); // If it's an array - var pairs = []; - for (var k in o) pairs.push(k + delimit1 + o[k]); - return pairs.join(delimit2); - }, - - /** - * Array#indexOf isn't supported in IE, so we use this as a cross-browser solution - */ - indexOf: function(arr, o) { - if (arr.indexOf) return arr.indexOf(o); - for (var i = 0; i < this.length; i++) if (arr[i] === o) return i; - return -1; - } - }; - - /** - * Test manages a single test (created with - * JSLitmus.test()) - * - * @private - */ - var Test = function (name, f) { - if (!f) throw new Error('Undefined test function'); - if (!(/function[^\(]*\(([^,\)]*)/).test(f.toString())) { - throw new Error('"' + name + '" test: Test is not a valid Function object'); - } - this.loopArg = RegExp.$1; - this.name = name; - this.f = f; - }; - - jsl.extend(Test, /** @lends Test */ { - /** Calibration tests for establishing iteration loop overhead */ - CALIBRATIONS: [ - new Test('calibrating loop', function(count) {while (count--);}), - new Test('calibrating function', jsl.F) - ], - - /** - * Run calibration tests. Returns true if calibrations are not yet - * complete (in which case calling code should run the tests yet again). - * onCalibrated - Callback to invoke when calibrations have finished - */ - calibrate: function(onCalibrated) { - for (var i = 0; i < Test.CALIBRATIONS.length; i++) { - var cal = Test.CALIBRATIONS[i]; - if (cal.running) return true; - if (!cal.count) { - cal.isCalibration = true; - cal.onStop = onCalibrated; - //cal.MIN_TIME = .1; // Do calibrations quickly - cal.run(2e4); - return true; - } - } - return false; - } - }); - - jsl.extend(Test.prototype, {/** @lends Test.prototype */ - /** Initial number of iterations */ - INIT_COUNT: 10, - /** Max iterations allowed (i.e. used to detect bad looping functions) */ - MAX_COUNT: 1e9, - /** Minimum time a test should take to get valid results (secs) */ - MIN_TIME: .5, - - /** Callback invoked when test state changes */ - onChange: jsl.F, - - /** Callback invoked when test is finished */ - onStop: jsl.F, - - /** - * Reset test state - */ - reset: function() { - delete this.count; - delete this.time; - delete this.running; - delete this.error; - }, - - /** - * Run the test (in a timeout). We use a timeout to make sure the browser - * has a chance to finish rendering any UI changes we've made, like - * updating the status message. - */ - run: function(count) { - count = count || this.INIT_COUNT; - jsl.status(this.name + ' x ' + count); - this.running = true; - var me = this; - setTimeout(function() {me._run(count);}, 200); - }, - - /** - * The nuts and bolts code that actually runs a test - */ - _run: function(count) { - var me = this; - - // Make sure calibration tests have run - if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return; - this.error = null; - - try { - var start, f = this.f, now, i = count; - - // Start the timer - start = new Date(); - - // Now for the money shot. If this is a looping function ... - if (this.loopArg) { - // ... let it do the iteration itself - f(count); - } else { - // ... otherwise do the iteration for it - while (i--) f(); - } - - // Get time test took (in secs) - this.time = Math.max(1,new Date() - start)/1000; - - // Store iteration count and per-operation time taken - this.count = count; - this.period = this.time/count; - - // Do we need to do another run? - this.running = this.time <= this.MIN_TIME; - - // ... if so, compute how many times we should iterate - if (this.running) { - // Bump the count to the nearest power of 2 - var x = this.MIN_TIME/this.time; - var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2)))); - count *= pow; - if (count > this.MAX_COUNT) { - throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.'); - } - } - } catch (e) { - // Exceptions are caught and displayed in the test UI - this.reset(); - this.error = e; - } - - // Figure out what to do next - if (this.running) { - me.run(count); - } else { - jsl.status(''); - me.onStop(me); - } - - // Finish up - this.onChange(this); - }, - - /** - * Get the number of operations per second for this test. - * - * @param normalize if true, iteration loop overhead taken into account - */ - getHz: function(/**Boolean*/ normalize) { - var p = this.period; - - // Adjust period based on the calibration test time - if (normalize && !this.isCalibration) { - var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1]; - - // If the period is within 20% of the calibration time, then zero the - // it out - p = p < cal.period*1.2 ? 0 : p - cal.period; - } - - return Math.round(1/p); - }, - - /** - * Get a friendly string describing the test - */ - toString: function() { - return this.name + ' - ' + this.time/this.count + ' secs'; - } - }); - - // CSS we need for the UI - var STYLESHEET = ''; - - // HTML markup for the UI - var MARKUP = '
\ - \ - \ -
\ -
\ - Normalize results \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
' + platform + '
TestOps/sec
\ -
\ - \ - Powered by JSLitmus \ -
'; - - /** - * The public API for creating and running tests - */ - window.JSLitmus = { - /** The list of all tests that have been registered with JSLitmus.test */ - _tests: [], - /** The queue of tests that need to be run */ - _queue: [], - - /** - * The parsed query parameters the current page URL. This is provided as a - * convenience for test functions - it's not used by JSLitmus proper - */ - params: {}, - - /** - * Initialize - */ - _init: function() { - // Parse query params into JSLitmus.params[] hash - var match = (location + '').match(/([^?#]*)(#.*)?$/); - if (match) { - var pairs = match[1].split('&'); - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i].split('='); - if (pair.length > 1) { - var key = pair.shift(); - var value = pair.length > 1 ? pair.join('=') : pair[0]; - this.params[key] = value; - } - } - } - - // Write out the stylesheet. We have to do this here because IE - // doesn't honor sheets written after the document has loaded. - document.write(STYLESHEET); - - // Setup the rest of the UI once the document is loaded - if (window.addEventListener) { - window.addEventListener('load', this._setup, false); - } else if (document.addEventListener) { - document.addEventListener('load', this._setup, false); - } else if (window.attachEvent) { - window.attachEvent('onload', this._setup); - } - - return this; - }, - - /** - * Set up the UI - */ - _setup: function() { - var el = jsl.$('jslitmus_container'); - if (!el) document.body.appendChild(el = document.createElement('div')); - - el.innerHTML = MARKUP; - - // Render the UI for all our tests - for (var i=0; i < JSLitmus._tests.length; i++) - JSLitmus.renderTest(JSLitmus._tests[i]); - }, - - /** - * (Re)render all the test results - */ - renderAll: function() { - for (var i = 0; i < JSLitmus._tests.length; i++) - JSLitmus.renderTest(JSLitmus._tests[i]); - JSLitmus.renderChart(); - }, - - /** - * (Re)render the chart graphics - */ - renderChart: function() { - var url = JSLitmus.chartUrl(); - jsl.$('chart_link').href = url; - jsl.$('chart_image').src = url; - jsl.$('chart').style.display = ''; - - // Update the tiny URL - jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url); - }, - - /** - * (Re)render the results for a specific test - */ - renderTest: function(test) { - // Make a new row if needed - if (!test._row) { - var trow = jsl.$('test_row_template'); - if (!trow) return; - - test._row = trow.cloneNode(true); - test._row.style.display = ''; - test._row.id = ''; - test._row.onclick = function() {JSLitmus._queueTest(test);}; - test._row.title = 'Run ' + test.name + ' test'; - trow.parentNode.appendChild(test._row); - test._row.cells[0].innerHTML = test.name; - } - - var cell = test._row.cells[1]; - var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping']; - - if (test.error) { - cns.push('test_error'); - cell.innerHTML = - '
' + test.error + '
' + - ''; - } else { - if (test.running) { - cns.push('test_running'); - cell.innerHTML = 'running'; - } else if (jsl.indexOf(JSLitmus._queue, test) >= 0) { - cns.push('test_pending'); - cell.innerHTML = 'pending'; - } else if (test.count) { - cns.push('test_done'); - var hz = test.getHz(jsl.$('test_normalize').checked); - cell.innerHTML = hz != Infinity ? hz : '∞'; - } else { - cell.innerHTML = 'ready'; - } - } - cell.className = cns.join(' '); - }, - - /** - * Create a new test - */ - test: function(name, f) { - // Create the Test object - var test = new Test(name, f); - JSLitmus._tests.push(test); - - // Re-render if the test state changes - test.onChange = JSLitmus.renderTest; - - // Run the next test if this one finished - test.onStop = function(test) { - if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test); - JSLitmus.currentTest = null; - JSLitmus._nextTest(); - }; - - // Render the new test - this.renderTest(test); - }, - - /** - * Add all tests to the run queue - */ - runAll: function(e) { - e = e || window.event; - var reverse = e && e.shiftKey, len = JSLitmus._tests.length; - for (var i = 0; i < len; i++) { - JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]); - } - }, - - /** - * Remove all tests from the run queue. The current test has to finish on - * it's own though - */ - stop: function() { - while (JSLitmus._queue.length) { - var test = JSLitmus._queue.shift(); - JSLitmus.renderTest(test); - } - }, - - /** - * Run the next test in the run queue - */ - _nextTest: function() { - if (!JSLitmus.currentTest) { - var test = JSLitmus._queue.shift(); - if (test) { - jsl.$('stop_button').disabled = false; - JSLitmus.currentTest = test; - test.run(); - JSLitmus.renderTest(test); - if (JSLitmus.onTestStart) JSLitmus.onTestStart(test); - } else { - jsl.$('stop_button').disabled = true; - JSLitmus.renderChart(); - } - } - }, - - /** - * Add a test to the run queue - */ - _queueTest: function(test) { - if (jsl.indexOf(JSLitmus._queue, test) >= 0) return; - JSLitmus._queue.push(test); - JSLitmus.renderTest(test); - JSLitmus._nextTest(); - }, - - /** - * Generate a Google Chart URL that shows the data for all tests - */ - chartUrl: function() { - var n = JSLitmus._tests.length, markers = [], data = []; - var d, min = 0, max = -1e10; - var normalize = jsl.$('test_normalize').checked; - - // Gather test data - for (var i=0; i < JSLitmus._tests.length; i++) { - var test = JSLitmus._tests[i]; - if (test.count) { - var hz = test.getHz(normalize); - var v = hz != Infinity ? hz : 0; - data.push(v); - markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' + - markers.length + ',10'); - max = Math.max(v, max); - } - } - if (markers.length <= 0) return null; - - // Build chart title - var title = document.getElementsByTagName('title'); - title = (title && title.length) ? title[0].innerHTML : null; - var chart_title = []; - if (title) chart_title.push(title); - chart_title.push('Ops/sec (' + platform + ')'); - - // Build labels - var labels = [jsl.toLabel(min), jsl.toLabel(max)]; - - var w = 250, bw = 15; - var bs = 5; - var h = markers.length*(bw + bs) + 30 + chart_title.length*20; - - var params = { - chtt: escape(chart_title.join('|')), - chts: '000000,10', - cht: 'bhg', // chart type - chd: 't:' + data.join(','), // data set - chds: min + ',' + max, // max/min of data - chxt: 'x', // label axes - chxl: '0:|' + labels.join('|'), // labels - chsp: '0,1', - chm: markers.join('|'), // test names - chbh: [bw, 0, bs].join(','), // bar widths - // chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient - chs: w + 'x' + h - }; - return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&'); - } - }; - - JSLitmus._init(); -})(); \ No newline at end of file