18 |
JSON 3
19 |
20 |
21 |
22 |
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.
23 |
24 |
25 |
29 |
JSON is a language-independent data interchange format based on a loose subset of the JavaScript grammar. Originally popularized by Douglas Crockford, the format was standardized in the fifth edition of the ECMAScript specification. The 5.1 edition, ratified in June 2011, incorporates several modifications to the grammar pertaining to the serialization of dates.
30 |
31 |
32 |
JSON 3 exposes two functions: stringify() for serializing a JavaScript value to JSON, and parse() for producing a JavaScript value from a JSON source string. It is a drop-in replacement for JSON 2. The functions behave exactly as described in the ECMAScript spec, except for the date serialization discrepancy noted below.
33 |
34 |
35 |
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 is available on jsPerf.
36 |
37 |
38 |
The project is hosted on GitHub, along with the unit tests. It is part of the BestieJS family, a collection of best-in-class JavaScript libraries that promote cross-platform support, specification precedents, unit testing, and plenty of documentation.
39 |
40 |
41 |
Changes from JSON 2
42 |
JSON 3...
43 |
44 |
45 |
46 | - Correctly serializes primitive wrapper objects.
47 | - Throws a
TypeError when serializing cyclic structures (JSON 2 recurses until the call stack overflows).
48 | - 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.
49 |
50 |
As of v3.2.3, JSON 3 is compatible with Prototype 1.6.1 and older.
51 |
52 |
53 |
In contrast to JSON 2, JSON 3 does not...
54 |
55 |
56 |
57 | - 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.
58 | - Add
toJSON() or toISOString() methods to Date.prototype. See the note about date serialization below.
59 |
60 |
Date Serialization
61 |
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.
62 |
63 |
64 |
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.
65 |
66 |
67 |
Portions of the date serialization code are adapted from the date-shim project.
68 |
69 |
70 |
Usage
71 |
Web Browsers
72 |
<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.min.js"></script>
73 | <script>
74 | JSON.stringify({"Hello": 123});
75 | // => '{"Hello":123}'
76 | JSON.parse("[[1, 2, 3], 1, 2, 3, 4]", function (key, value) {
77 | if (typeof value == "number") {
78 | value = value % 2 ? "Odd" : "Even";
79 | }
80 | return value;
81 | });
82 | // => [["Odd", "Even", "Odd"], "Odd", "Even", "Odd", "Even"]
83 | </script>
84 |
CommonJS Environments
85 |
var JSON3 = require("./path/to/json3");
86 | JSON3.parse("[1, 2, 3]");
87 | // => [1, 2, 3]
88 |
JavaScript Engines
89 |
load("path/to/json3.js");
90 | JSON.stringify({"Hello": 123, "Good-bye": 456}, ["Hello"], "\t");
91 | // => '{\n\t"Hello": 123\n}'
92 |
Compatibility
93 |
JSON 3 has been tested with the following web browsers, CommonJS environments, and JavaScript engines.
94 |
95 |
96 |
Web Browsers
97 |
104 |
CommonJS Environments
105 |
106 | - Node 0.2.6 and higher
107 | - RingoJS 0.4 and higher
108 | - Narwhal 0.3.2 and higher
109 |
110 |
JavaScript Engines
111 |
112 | - Mozilla Rhino 1.5R5 and higher
113 | - WebKit JSC
114 | - Google V8
115 |
116 |
Known Incompatibilities
117 |
118 | - 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)).
119 |
120 |
Required Native Methods
121 |
JSON 3 assumes that the following methods exist and function as described in the ECMAScript specification:
122 |
123 |
124 |
125 | - The
Number, String, Array, Object, Date, SyntaxError, and TypeError constructors.
126 | String.fromCharCode
127 | Object#toString
128 | Function#call
129 | Math.floor
130 | Number#toString
131 | Date#valueOf
132 | String.prototype: indexOf, charCodeAt, charAt, slice.
133 | Array.prototype: push, pop, join.
134 |
135 |
Contribute
136 |
Check out a working copy of the JSON 3 source code with Git:
137 |
138 |
139 |
$ git clone git://github.com/bestiejs/json3.git
140 | $ cd json3
141 | $ git submodule update --init
142 |
If you'd like to contribute a feature or bug fix, you can fork JSON 3, commit your changes, and send a pull request. Please make sure to update the unit tests in the test directory as well.
143 |
144 |
145 |
Alternatively, you can use the GitHub issue tracker to submit bug reports, feature requests, and questions, or send tweets to @kitcambridge.
146 |
147 |
148 |
JSON 3 is released under the MIT License.
149 |
150 |
151 |
154 |
155 |
--------------------------------------------------------------------------------
/test/test_json3.js:
--------------------------------------------------------------------------------
1 | /* JSON 3 Unit Test Suite | http://bestiejs.github.com/json3 */
2 | (function (root) {
3 | var isLoader = typeof define == "function" && !!define.amd,
4 | isModule = typeof require == "function" && typeof exports == "object" && exports && !isLoader,
5 | isBrowser = "window" in root && root.window == root && typeof root.navigator != "undefined",
6 | isEngine = !isBrowser && !isModule && typeof root.load == "function",
7 |
8 | load = function load(module, path) {
9 | return root[module] || (isModule ? require(path) : isEngine ?
10 | (root.load(path.replace(/\.js$/, "") + ".js"), root[module]) : null);
11 | },
12 |
13 | // Load Spec, Newton, and JSON 3.
14 | Spec = load("Spec", "./../vendor/spec/lib/spec"), Newton = load("Newton", "./../vendor/spec/lib/newton"), JSON = load("JSON", "../lib/json3"),
15 |
16 | // Create the test suite.
17 | testSuite = JSON.testSuite = new Spec.Suite("JSON 3 Unit Tests");
18 |
19 | // Create and attach the logger event handler.
20 | testSuite.on("all", isBrowser ? Newton.createReport("suite") : Newton.createConsole(function (value) {
21 | if (typeof console != "undefined" && console.log) {
22 | console.log(value);
23 | } else if (typeof print == "function" && !isBrowser) {
24 | // In browsers, the global `print` function prints the current page.
25 | print(value);
26 | } else {
27 | throw value;
28 | }
29 | }));
30 |
31 | // Ensures that `JSON.parse` throws an exception when parsing the given
32 | // `source` string.
33 | Spec.Test.prototype.parseError = function (source, message, callback) {
34 | return this.error(function () {
35 | JSON.parse(source, callback);
36 | }, function (exception) {
37 | return exception.name == "SyntaxError";
38 | }, message);
39 | };
40 |
41 | // Ensures that `JSON.parse` parses the given source string correctly.
42 | Spec.Test.prototype.parses = function (expected, source, message, callback) {
43 | return this.deepEqual(JSON.parse(source, callback), expected, message);
44 | };
45 |
46 | // Ensures that `JSON.stringify` serializes the given object correctly.
47 | Spec.Test.prototype.serializes = function (expected, value, message, filter, width) {
48 | return this.strictEqual(JSON.stringify(value, filter, width), expected, message);
49 | };
50 |
51 | // Ensures that `JSON.stringify` throws a `TypeError` if the given object
52 | // contains a circular reference.
53 | Spec.Test.prototype.cyclicError = function (value, message) {
54 | return this.error(function () {
55 | JSON.stringify(value);
56 | }, function (exception) {
57 | return exception.name == "TypeError";
58 | }, message);
59 | };
60 |
61 | // Tests
62 | // -----
63 |
64 | testSuite.addTest("`parse`: Empty Source Strings", function () {
65 | this.parseError("", "Empty JSON source string");
66 | this.parseError("\n\n\r\n", "Source string containing only line terminators");
67 | this.parseError(" ", "Source string containing a single space character");
68 | this.parseError(" ", "Source string containing multiple space characters");
69 | this.done(4);
70 | });
71 |
72 | testSuite.addTest("`parse`: Whitespace", function (test) {
73 | // The only valid JSON whitespace characters are tabs, spaces, and line
74 | // terminators. All other Unicode category `Z` (`Zs`, `Zl`, and `Zp`)
75 | // characters are invalid (note that the `Zs` category includes the
76 | // space character).
77 | var characters = ["{\u00a0}", "{\u1680}", "{\u180e}", "{\u2000}", "{\u2001}",
78 | "{\u2002}", "{\u2003}", "{\u2004}", "{\u2005}", "{\u2006}", "{\u2007}",
79 | "{\u2008}", "{\u2009}", "{\u200a}", "{\u202f}", "{\u205f}", "{\u3000}",
80 | "{\u2028}", "{\u2029}"];
81 |
82 | Spec.forEach(characters, function (value) {
83 | test.parseError(value, "Source string containing an invalid Unicode whitespace character");
84 | });
85 |
86 | this.parseError("{\u000b}", "Source string containing a vertical tab");
87 | this.parseError("{\u000c}", "Source string containing a form feed");
88 | this.parseError("{\ufeff}", "Source string containing a byte-order mark");
89 |
90 | this.parses({}, "{\r\n}", "Source string containing a CRLF line ending");
91 | this.parses({}, "{\n\n\r\n}", "Source string containing multiple line terminators");
92 | this.parses({}, "{\t}", "Source string containing a tab character");
93 | this.parses({}, "{ }", "Source string containing a space character");
94 | this.done(26);
95 | });
96 |
97 | testSuite.addTest("`parse`: Octal Values", function (test) {
98 | // `08` and `018` are invalid octal values.
99 | Spec.forEach(["00", "01", "02", "03", "04", "05", "06", "07", "010", "011", "08", "018"], function (value) {
100 | test.parseError(value, "Octal literal");
101 | test.parseError("-" + value, "Negative octal literal");
102 | test.parseError('"\\' + value + '"', "Octal escape sequence in a string");
103 | test.parseError('"\\x' + value + '"', "Hex escape sequence in a string");
104 | });
105 | this.done(48);
106 | });
107 |
108 | testSuite.addTest("`parse`: Numeric Literals", function () {
109 | this.parses(100, "100", "Integer");
110 | this.parses(-100, "-100", "Negative integer");
111 | this.parses(10.5, "10.5", "Float");
112 | this.parses(-3.141, "-3.141", "Negative float");
113 | this.parses(0.625, "0.625", "Decimal");
114 | this.parses(-0.03125, "-0.03125", "Negative decimal");
115 | this.parses(1000, "1e3", "Exponential");
116 | this.parses(100, "1e+2", "Positive exponential");
117 | this.parses(-0.01, "-1e-2", "Negative exponential");
118 | this.parses(3125, "0.03125e+5", "Decimalized exponential");
119 | this.parses(100, "1E2", "Case-insensitive exponential delimiter");
120 |
121 | this.parseError("+1", "Leading `+`");
122 | this.parseError("1.", "Trailing decimal point");
123 | this.parseError(".1", "Leading decimal point");
124 | this.parseError("1e", "Missing exponent");
125 | this.parseError("1e-", "Missing signed exponent");
126 | this.parseError("--1", "Leading `--`");
127 | this.parseError("1-+", "Trailing `-+`");
128 | this.parseError("0xaf", "Hex literal");
129 |
130 | // The native `JSON.parse` implementation in IE 9 allows this syntax, but
131 | // the feature tests should detect the broken implementation.
132 | this.parseError("- 5", "Invalid negative sign");
133 |
134 | this.done(20);
135 | });
136 |
137 | testSuite.addTest("`parse`: String Literals", function (test) {
138 | var expected = 49, controlCharacters = ["\u0001", "\u0002", "\u0003",
139 | "\u0004", "\u0005", "\u0006", "\u0007", "\b", "\t", "\n", "\u000b", "\f",
140 | "\r", "\u000e", "\u000f", "\u0010", "\u0011", "\u0012", "\u0013",
141 | "\u0014", "\u0015", "\u0016", "\u0017", "\u0018", "\u0019", "\u001a",
142 | "\u001b", "\u001c", "\u001d", "\u001e", "\u001f"];
143 |
144 | // Opera 7 discards null characters in strings.
145 | if ("\0".length) {
146 | expected += 1;
147 | controlCharacters.push("\u0000");
148 | }
149 |
150 | this.parses("value", '"value"', "Double-quoted string literal");
151 | this.parses("", '""', "Empty string literal");
152 |
153 | this.parses("\u2028", '"\\u2028"', "String containing an escaped Unicode line separator");
154 | this.parses("\u2029", '"\\u2029"', "String containing an escaped Unicode paragraph separator");
155 | this.parses("\ud834\udf06", '"\\ud834\\udf06"', "String containing an escaped Unicode surrogate pair");
156 | this.parses("\ud834\udf06", '"\ud834\udf06"', "String containing an unescaped Unicode surrogate pair");
157 | this.parses("\u0001", '"\\u0001"', "String containing an escaped ASCII control character");
158 | this.parses("\b", '"\\b"', "String containing an escaped backspace");
159 | this.parses("\f", '"\\f"', "String containing an escaped form feed");
160 | this.parses("\n", '"\\n"', "String containing an escaped line feed");
161 | this.parses("\r", '"\\r"', "String containing an escaped carriage return");
162 | this.parses("\t", '"\\t"', "String containing an escaped tab");
163 |
164 | this.parses("hello/world", '"hello\\/world"', "String containing an escaped solidus");
165 | this.parses("hello\\world", '"hello\\\\world"', "String containing an escaped reverse solidus");
166 | this.parses("hello\"world", '"hello\\"world"', "String containing an escaped double-quote character");
167 |
168 | this.parseError("'hello'", "Single-quoted string literal");
169 | this.parseError('"\\x61"', "String containing a hex escape sequence");
170 | this.parseError('"hello \r\n world"', "String containing an unescaped CRLF line ending");
171 |
172 | Spec.forEach(controlCharacters, function (value) {
173 | test.parseError('"' + value + '"', "String containing an unescaped ASCII control character");
174 | });
175 |
176 | this.done(expected);
177 | });
178 |
179 | testSuite.addTest("`parse`: Array Literals", function () {
180 | this.parseError("[1, 2, 3,]", "Trailing comma in array literal");
181 | this.parses([1, 2, [3, [4, 5]], 6, [true, false], [null], [[]]], "[1, 2, [3, [4, 5]], 6, [true, false], [null], [[]]]", "Nested arrays");
182 | this.parses([{}], "[{}]", "Array containing empty object literal");
183 | this.parses([100, true, false, null, {"a": ["hello"], "b": ["world"]}, [0.01]], "[1e2, true, false, null, {\"a\": [\"hello\"], \"b\": [\"world\"]}, [1e-2]]", "Mixed array");
184 | this.done(4);
185 | });
186 |
187 | testSuite.addTest("`parse`: Object Literals", function () {
188 | this.parses({"hello": "world"}, "{\"hello\": \"world\"}", "Object literal containing one member");
189 | this.parses({"hello": "world", "foo": ["bar", true], "fox": {"quick": true, "purple": false}}, "{\"hello\": \"world\", \"foo\": [\"bar\", true], \"fox\": {\"quick\": true, \"purple\": false}}", "Object literal containing multiple members");
190 |
191 | this.parseError("{key: 1}", "Unquoted identifier used as a property name");
192 | this.parseError("{false: 1}", "`false` used as a property name");
193 | this.parseError("{true: 1}", "`true` used as a property name");
194 | this.parseError("{null: 1}", "`null` used as a property name");
195 | this.parseError("{'key': 1}", "Single-quoted string used as a property name");
196 | this.parseError("{1: 2, 3: 4}", "Number used as a property name");
197 |
198 | this.parseError("{\"hello\": \"world\", \"foo\": \"bar\",}", "Trailing comma in object literal");
199 | this.done(9);
200 | });
201 |
202 | // JavaScript expressions should never be evaluated, as JSON 3 does not use
203 | // `eval`.
204 | testSuite.addTest("`parse`: Invalid Expressions", function (test) {
205 | Spec.forEach(["1 + 1", "1 * 2", "var value = 123;", "{});value = 123;({}", "call()", "1, 2, 3, \"value\""], function (expression) {
206 | test.parseError(expression, "Source string containing a JavaScript expression");
207 | });
208 | this.done(6);
209 | });
210 |
211 | testSuite.addTest("`stringify` and `parse`: Optional Arguments", function () {
212 | this.parses({"a": 1, "b": 16}, '{"a": 1, "b": "10000"}', "Callback function provided", function (key, value) {
213 | return typeof value == "string" ? parseInt(value, 2) : value;
214 | });
215 | this.serializes("{\n \"bar\": 456\n}", {"foo": 123, "bar": 456}, "Object; optional `filter` and `whitespace` arguments", ["bar"], 2);
216 | // Test adapted from the Opera JSON test suite via Ken Snyder.
217 | // See http://testsuites.opera.com/JSON/correctness/scripts/045.js
218 | this.serializes('{"PI":3.141592653589793}', Math, "List of non-enumerable property names specified as the `filter` argument", ["PI"]);
219 | this.equal(3, JSON.parse("[1, 2, 3]", function (key, value) {
220 | if (typeof value == "object" && value) {
221 | return value;
222 | }
223 | }).length, "Issue #10: `walk` should not use `splice` when removing an array element");
224 | this.done(4);
225 | });
226 |
227 | testSuite.addTest("`stringify`", function () {
228 | var expected = 29, value, pattern;
229 |
230 | // Special values.
231 | this.serializes("null", null, "`null` is represented literally");
232 | this.serializes("null", 1 / 0, "`Infinity` is serialized as `null`");
233 | this.serializes("null", 0 / 0, "`NaN` is serialized as `null`");
234 | this.serializes("null", -1 / 0, "`-Infinity` is serialized as `null`");
235 | this.serializes("true", true, "Boolean primitives are represented literally");
236 | this.serializes("false", new Boolean(false), "Boolean objects are represented literally");
237 | this.serializes('"\\\\\\"How\\bquickly\\tdaft\\njumping\\fzebras\\rvex\\""', new String('\\"How\bquickly\tdaft\njumping\fzebras\rvex"'), "All control characters in strings are escaped");
238 |
239 | this.serializes("[false,1,\"Kit\"]", [new Boolean, new Number(1), new String("Kit")], "Arrays are serialized recursively");
240 | this.serializes("[null]", [void 0], "`[undefined]` is serialized as `[null]`");
241 |
242 | // Property enumeration is implementation-dependent.
243 | value = {
244 | "jdalton": ["John-David", 29],
245 | "kitcambridge": ["Kit", 18],
246 | "mathias": ["Mathias", 23]
247 | };
248 | this.parses(value, JSON.stringify(value), "Objects are serialized recursively");
249 |
250 | // Complex cyclic structures.
251 | value = { "foo": { "b": { "foo": { "c": { "foo": null} } } } };
252 | this.serializes('{"foo":{"b":{"foo":{"c":{"foo":null}}}}}', value, "Nested objects containing identically-named properties should serialize correctly");
253 |
254 | var S = [], N = {};
255 | S.push(N, N);
256 | this.serializes('[{},{}]', S, "Objects containing duplicate references should not throw a `TypeError`");
257 |
258 | value.foo.b.foo.c.foo = value;
259 | this.cyclicError(value, "Objects containing complex circular references should throw a `TypeError`");
260 |
261 | // Sparse arrays.
262 | value = [];
263 | value[5] = 1;
264 | this.serializes("[null,null,null,null,null,1]", value, "Sparse arrays should serialize correctly");
265 |
266 | // Dates.
267 | this.serializes('"1994-07-03T00:00:00.000Z"', new Date(Date.UTC(1994, 6, 3)), "Dates should be serialized according to the simplified date time string format");
268 | this.serializes('"1993-06-02T02:10:28.224Z"', new Date(Date.UTC(1993, 5, 2, 2, 10, 28, 224)), "The date time string should conform to the format outlined in the spec");
269 | this.serializes('"-271821-04-20T00:00:00.000Z"', new Date(-8.64e15), "The minimum valid date value should serialize correctly");
270 | this.serializes('"+275760-09-13T00:00:00.000Z"', new Date(8.64e15), "The maximum valid date value should serialize correctly");
271 | this.serializes('"+010000-01-01T00:00:00.000Z"', new Date(Date.UTC(10000, 0, 1)), "https://bugs.ecmascript.org/show_bug.cgi?id=119");
272 |
273 | // Tests based on research by @Yaffle. See kriskowal/es5-shim#111.
274 | this.serializes('"1969-12-31T23:59:59.999Z"', new Date(-1), "Millisecond values < 1000 should be serialized correctly");
275 | this.serializes('"-000001-01-01T00:00:00.000Z"', new Date(-621987552e5), "Years prior to 0 should be serialized as extended years");
276 | this.serializes('"+010000-01-01T00:00:00.000Z"', new Date(2534023008e5), "Years after 9999 should be serialized as extended years");
277 | this.serializes('"-109252-01-01T10:37:06.708Z"', new Date(-3509827334573292), "Issue #4: Opera > 9.64 should correctly serialize a date with a year of `-109252`");
278 |
279 | // Opera 7 normalizes dates with invalid time values to represent the
280 | // current date.
281 | value = new Date("Kit");
282 | if (!isFinite(value)) {
283 | expected += 1;
284 | this.serializes("null", value, "Invalid dates should serialize as `null`");
285 | }
286 |
287 | // Additional arguments.
288 | this.serializes("[\n 1,\n 2,\n 3,\n [\n 4,\n 5\n ]\n]", [1, 2, 3, [4, 5]], "Nested arrays; optional `whitespace` argument", null, " ");
289 | this.serializes("[]", [], "Empty array; optional string `whitespace` argument", null, " ");
290 | this.serializes("{}", {}, "Empty object; optional numeric `whitespace` argument", null, 2);
291 | this.serializes("[\n 1\n]", [1], "Single-element array; optional numeric `whitespace` argument", null, 2);
292 | this.serializes("{\n \"foo\": 123\n}", { "foo": 123 }, "Single-member object; optional string `whitespace` argument", null, " ");
293 | this.serializes("{\n \"foo\": {\n \"bar\": [\n 123\n ]\n }\n}", {"foo": {"bar": [123]}}, "Nested objects; optional numeric `whitespace` argument", null, 2);
294 |
295 | this.done(expected);
296 | });
297 |
298 | /*
299 | * The following tests are adapted from the ECMAScript 5 Conformance Suite.
300 | * Copyright 2009, Microsoft Corporation. Distributed under the New BSD License.
301 | *
302 | * Redistribution and use in source and binary forms, with or without
303 | * modification, are permitted provided that the following conditions are met:
304 | *
305 | * - Redistributions of source code must retain the above copyright notice,
306 | * this list of conditions and the following disclaimer.
307 | * - Redistributions in binary form must reproduce the above copyright notice,
308 | * this list of conditions and the following disclaimer in the documentation
309 | * and/or other materials provided with the distribution.
310 | * - Neither the name of Microsoft nor the names of its contributors may be
311 | * used to endorse or promote products derived from this software without
312 | * specific prior written permission.
313 | *
314 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
315 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
316 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
317 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
318 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
319 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
320 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
321 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
322 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
323 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
324 | * POSSIBILITY OF SUCH DAMAGE.
325 | */
326 | testSuite.addTest("ECMAScript 5 Conformance", function () {
327 | var value = { "a1": { "b1": [1, 2, 3, 4], "b2": { "c1": 1, "c2": 2 } }, "a2": "a2" };
328 |
329 | // Section 15.12.1.1: The JSON Grammar.
330 | // ------------------------------------
331 |
332 | // Tests 15.12.1.1-0-1 thru 15.12.1.1-0-8.
333 | this.parseError("12\t\r\n 34", "Valid whitespace characters may not separate two discrete tokens");
334 | this.parseError("\u000b1234", "The vertical tab is not a valid whitespace character");
335 | this.parseError("\u000c1234", "The form feed is not a valid whitespace character");
336 | this.parseError("\u00a01234", "The non-breaking space is not a valid whitespace character");
337 | this.parseError("\u200b1234", "The zero-width space is not a valid whitespace character");
338 | this.parseError("\ufeff1234", "The byte order mark (zero-width non-breaking space) is not a valid whitespace character");
339 | this.parseError("\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u30001234", "Other Unicode category `Z` characters are not valid whitespace characters");
340 | this.parseError("\u2028\u20291234", "The line (U+2028) and paragraph (U+2029) separators are not valid whitespace characters");
341 |
342 | // Test 15.12.1.1-0-9.
343 | this.parses({ "property": {}, "prop2": [true, null, 123.456] },
344 | '\t\r \n{\t\r \n' +
345 | '"property"\t\r \n:\t\r \n{\t\r \n}\t\r \n,\t\r \n' +
346 | '"prop2"\t\r \n:\t\r \n' +
347 | '[\t\r \ntrue\t\r \n,\t\r \nnull\t\r \n,123.456\t\r \n]' +
348 | '\t\r \n}\t\r \n',
349 | "Valid whitespace characters may precede and follow all tokens");
350 |
351 | // Tests 15.12.1.1-g1-1 thru 15.12.1.1-g1-4.
352 | this.parses(1234, "\t1234", "Leading tab characters should be ignored");
353 | this.parseError("12\t34", "A tab character may not separate two disparate tokens");
354 | this.parses(1234, "\r1234", "Leading carriage returns should be ignored");
355 | this.parseError("12\r34", "A carriage return may not separate two disparate tokens");
356 | this.parses(1234, "\n1234", "Leading line feeds should be ignored");
357 | this.parseError("12\n34", "A line feed may not separate two disparate tokens");
358 | this.parses(1234, " 1234", "Leading space characters should be ignored");
359 | this.parseError("12 34", "A space character may not separate two disparate tokens");
360 |
361 | // Tests 15.12.1.1-g2-1 thru 15.12.1.1-g2-5.
362 | this.parses("abc", '"abc"', "Strings must be enclosed in double quotes");
363 | this.parseError("'abc'", "Single-quoted strings are not permitted");
364 | // Note: the original test 15.12.1.1-g2-3 (`"\u0022abc\u0022"`) is incorrect,
365 | // as the JavaScript interpreter will always convert `\u0022` to `"`.
366 | this.parseError("\\u0022abc\\u0022", "Unicode-escaped double quote delimiters are not permitted");
367 | this.parseError('"ab'+"c'", "Strings must terminate with a double quote character");
368 | this.parses("", '""', "Strings may be empty");
369 |
370 | // Tests 15.12.1.1-g4-1 thru 15.12.1.1-g4-4.
371 | this.parseError('"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007"', "Unescaped control characters in the range [U+0000, U+0007] are not permitted within strings");
372 | this.parseError('"\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f"', "Unescaped control characters in the range [U+0008, U+000F] are not permitted within strings");
373 | this.parseError('"\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"', "Unescaped control characters in the range [U+0010, U+0017] are not permitted within strings");
374 | this.parseError('"\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"', "Unescaped control characters in the range [U+0018, U+001F] are not permitted within strings");
375 |
376 | // Tests 15.12.1.1-g5-1 thru 15.12.1.1-g5-3.
377 | this.parses("X", '"\\u0058"', "Unicode escape sequences are permitted within strings");
378 | this.parseError('"\\u005"', "Unicode escape sequences may not comprise fewer than four hexdigits");
379 | this.parseError('"\\u0X50"', "Unicode escape sequences may not contain non-hex characters");
380 |
381 | // Tests 15.12.1.1-g6-1 thru 15.12.1.1-g6-7.
382 | this.parses("/", '"\\/"', "Escaped solidus");
383 | this.parses("\\", '"\\\\"', "Escaped reverse solidus");
384 | this.parses("\b", '"\\b"', "Escaped backspace");
385 | this.parses("\f", '"\\f"', "Escaped form feed");
386 | this.parses("\n", '"\\n"', "Escaped line feed");
387 | this.parses("\r", '"\\r"', "Escaped carriage return");
388 | this.parses("\t", '"\\t"', "Escaped tab");
389 |
390 | // Section 15.12.3: `JSON.stringify()`.
391 | // ------------------------------------
392 |
393 | // Test 15.12.3-11-1 thru 5.12.3-11-15.
394 | this.serializes(void 0, void 0, "`JSON.stringify(undefined)` should return `undefined`");
395 | this.serializes('"replacement"', void 0, "The `JSON.stringify` callback function can be called on a top-level `undefined` value", function (key, value) {
396 | return "replacement";
397 | });
398 | this.serializes('"a string"', "a string", "`JSON.stringify` should serialize top-level string primitives");
399 | this.serializes("123", 123, "`JSON.stringify` should serialize top-level number primitives");
400 | this.serializes("true", true, "`JSON.stringify` should serialize top-level Boolean primitives");
401 | this.serializes("null", null, "`JSON.stringify` should serialize top-level `null` values");
402 | this.serializes("42", new Number(42), "`JSON.stringify` should serialize top-level number objects");
403 | this.serializes('"wrapped"', new String("wrapped"), "`JSON.stringify` should serialize top-level string objects");
404 | this.serializes("false", new Boolean(false), "`JSON.stringify` should serialize top-level Boolean objects");
405 | this.serializes(void 0, 42, "The `JSON.stringify` callback function may return `undefined` when called on a top-level number primitive", function () {
406 | return void 0;
407 | });
408 | this.serializes(void 0, { "prop": 1 }, "The `JSON.stringify` callback function may return `undefined` when called on a top-level object", function () {
409 | return void 0;
410 | });
411 | this.serializes("[4,2]", 42, "The `JSON.stringify` callback function may return an array when called on a top-level number primitive", function (key, value) {
412 | return value == 42 ? [4, 2] : value;
413 | });
414 | this.serializes('{"forty":2}', 42, "The `JSON.stringify` callback function may return an object literal when called on a top-level number primitive", function (key, value) {
415 | return value == 42 ? { "forty": 2 } : value;
416 | });
417 | this.serializes(void 0, function () {}, "`JSON.stringify` should return `undefined` when called on a top-level function");
418 | this.serializes("99", function () {}, "The `JSON.stringify` callback function may return a number primitive when called on a top-level function", function () {
419 | return 99;
420 | });
421 |
422 | // Test 15.12.3-4-1.
423 | this.serializes("[42]", [42], "`JSON.stringify` should ignore `filter` arguments that are not functions or arrays", {});
424 |
425 | // Test 15.12.3-5-a-i-1 and 15.12.3-5-b-i-1.
426 | this.equal(JSON.stringify(value, null, new Number(5)), JSON.stringify(value, null, 5), "Optional `width` argument: Number object and primitive width values should produce identical results");
427 | this.equal(JSON.stringify(value, null, new String("xxx")), JSON.stringify(value, null, "xxx"), "Optional `width` argument: String object and primitive width values should produce identical results");
428 |
429 | // Test 15.12.3-6-a-1 and 15.12.3-6-a-2.
430 | this.equal(JSON.stringify(value, null, 10), JSON.stringify(value, null, 100), "Optional `width` argument: The maximum numeric width value should be 10");
431 | this.equal(JSON.stringify(value, null, 5.99999), JSON.stringify(value, null, 5), "Optional `width` argument: Numeric values should be converted to integers");
432 |
433 | // Test 15.12.3-6-b-1 and 15.12.3-6-b-4.
434 | this.equal(JSON.stringify(value, null, 0.999999), JSON.stringify(value), "Optional `width` argument: Numeric width values between 0 and 1 should be ignored");
435 | this.equal(JSON.stringify(value, null, 0), JSON.stringify(value), "Optional `width` argument: Zero should be ignored");
436 | this.equal(JSON.stringify(value, null, -5), JSON.stringify(value), "Optional `width` argument: Negative numeric values should be ignored");
437 | this.equal(JSON.stringify(value, null, 5), JSON.stringify(value, null, " "), "Optional `width` argument: Numeric width values in the range [1, 10] should produce identical results to that of string values containing `width` spaces");
438 |
439 | // Test 15.12.3-7-a-1.
440 | this.equal(JSON.stringify(value, null, "0123456789xxxxxxxxx"), JSON.stringify(value, null, "0123456789"), "Optional `width` argument: String width values longer than 10 characters should be truncated");
441 |
442 | // Test 15.12.3-8-a-1 thru 15.12.3-8-a-5.
443 | this.equal(JSON.stringify(value, null, ""), JSON.stringify(value), "Empty string `width` arguments should be ignored");
444 | this.equal(JSON.stringify(value, null, true), JSON.stringify(value), "Boolean primitive `width` arguments should be ignored");
445 | this.equal(JSON.stringify(value, null, null), JSON.stringify(value), "`null` `width` arguments should be ignored");
446 | this.equal(JSON.stringify(value, null, new Boolean(false)), JSON.stringify(value), "Boolean object `width` arguments should be ignored");
447 | this.equal(JSON.stringify(value, null, value), JSON.stringify(value), "Object literal `width` arguments should be ignored");
448 |
449 | // Test 15.12.3@2-2-b-i-1.
450 | this.serializes('["fortytwo objects"]', [{
451 | "prop": 42,
452 | "toJSON": function () {
453 | return "fortytwo objects";
454 | }
455 | }], "An object literal with a custom `toJSON` method nested within an array may return a string primitive for serialization");
456 |
457 | // Test 15.12.3@2-2-b-i-2.
458 | this.serializes('[42]', [{
459 | "prop": 42,
460 | "toJSON": function () {
461 | return new Number(42);
462 | }
463 | }], "An object literal with a custom `toJSON` method nested within an array may return a number object for serialization");
464 |
465 | // Test 15.12.3@2-2-b-i-3.
466 | this.serializes('[true]', [{
467 | "prop": 42,
468 | "toJSON": function () {
469 | return new Boolean(true);
470 | }
471 | }], "An object liyeral with a custom `toJSON` method nested within an array may return a Boolean object for serialization");
472 |
473 | // Test 15.12.3@2-3-a-1.
474 | this.serializes('["fortytwo"]', [42], "The `JSON.stringify` callback function may return a string object when called on an array", function (key, value) {
475 | return value === 42 ? new String("fortytwo") : value;
476 | });
477 |
478 | // Test 15.12.3@2-3-a-2.
479 | this.serializes('[84]', [42], "The `JSON.stringify` callback function may return a number object when called on an array", function (key, value) {
480 | return value === 42 ? new Number(84) : value;
481 | });
482 |
483 | // Test 15.12.3@2-3-a-3.
484 | this.serializes('[false]', [42], "The `JSON.stringify` callback function may return a Boolean object when called on an array", function (key, value) {
485 | return value === 42 ? new Boolean(false) : value;
486 | });
487 |
488 | // Test 15.12.3@4-1-2. 15.12.3@4-1-1 only tests whether an exception is
489 | // thrown; the type of the exception is not checked.
490 | value = {};
491 | value.prop = value;
492 | this.cyclicError(value, "An object containing a circular reference should throw a `TypeError`");
493 |
494 | // Test 15.12.3@4-1-3, modified to ensure that a `TypeError` is thrown.
495 | value = { "p1": { "p2": {} } };
496 | value.p1.p2.prop = value;
497 | this.cyclicError(value, "A nested cyclic structure should throw a `TypeError`");
498 | this.done(74);
499 | });
500 |
501 | // This test may fail in certain implementations.
502 | testSuite.addTest("Anticipated ECMAScript 6 Additions", function () {
503 | var expected = 0, value;
504 | try {
505 | value = {};
506 | // IE 8 only allows properties to be defined on DOM elements. Credits:
507 | // John-David Dalton and Juriy Zaytsev.
508 | if (Object.defineProperty(value, value, value), "value" in Object.getOwnPropertyDescriptor(value, value)) {
509 | expected += 1;
510 | value = [0, 1, 2, 3];
511 | Object.prototype[3] = 3;
512 | Object.defineProperty(value, 1, {
513 | "get": function () {
514 | Object.defineProperty(value, 4, { "value": 4 });
515 | delete value[2];
516 | delete value[3];
517 | value[5] = 5;
518 | return 1;
519 | }
520 | });
521 | // Test by Jeff Walden and Allen Wirfs-Brock.
522 | this.serializes('{"0":{"1":{"3":{"3":3}},"3":3},"3":3}', { 0: { 1: { 3: { 4: { 5: { 2: "omitted" } } } } } }, "Issue #12: `parse` should process property name arrays sequentially", value);
523 | }
524 | } catch (exception) {}
525 | // Clean up.
526 | delete Object.prototype[3];
527 | this.done(expected);
528 | });
529 |
530 | testSuite.shuffle();
531 |
532 | if (isLoader) {
533 | define(function () {
534 | return testSuite;
535 | });
536 | } else if (!isBrowser && (!isModule || (typeof module == "object" && module == require.main))) {
537 | testSuite.run();
538 | }
539 | })(this);
--------------------------------------------------------------------------------
/vendor/curl.js:
--------------------------------------------------------------------------------
1 | /** @license MIT License (c) copyright B Cavalier & J Hann */
2 |
3 | /**
4 | * curl (cujo resource loader)
5 | * An AMD-compliant javascript module and resource loader
6 | *
7 | * curl is part of the cujo.js family of libraries (http://cujojs.com/)
8 | *
9 | * Licensed under the MIT License at:
10 | * http://www.opensource.org/licenses/mit-license.php
11 | *
12 | * @version 0.6
13 | */
14 | (function (global) {
15 |
16 | /*
17 | * Basic operation:
18 | * When a dependency is encountered and it already exists, it's returned.
19 | * If it doesn't already exist, it is created and the dependency's script
20 | * is loaded. If there is a define call in the loaded script with a id,
21 | * it is resolved asap (i.e. as soon as the module's dependencies are
22 | * resolved). If there is a (single) define call with no id (anonymous),
23 | * the resource in the resNet is resolved after the script's onload fires.
24 | * IE requires a slightly different tactic. IE marks the readyState of the
25 | * currently executing script to 'interactive'. If we can find this script
26 | * while a define() is being called, we can match the define() to its id.
27 | * Opera marks scripts as 'interactive' but at inopportune times so we
28 | * have to handle it specifically.
29 | */
30 |
31 | var
32 | version = '0.6.0',
33 | userCfg = global['curl'],
34 | doc = global.document,
35 | head = doc && (doc['head'] || doc.getElementsByTagName('head')[0]),
36 | // constants / flags
37 | msgUsingExports = {},
38 | interactive = {},
39 | // this is the list of scripts that IE is loading. one of these will
40 | // be the "interactive" script. too bad IE doesn't send a readystatechange
41 | // event to tell us exactly which one.
42 | activeScripts = {},
43 | // these are always handy :)
44 | toString = ({}).toString,
45 | undef,
46 | // script ready states that signify it's loaded
47 | readyStates = { 'loaded': 1, 'interactive': interactive, 'complete': 1 },
48 | // local cache of resource definitions (lightweight promises)
49 | cache = {},
50 | // preload are files that must be loaded before any others
51 | preload = false,
52 | // net to catch anonymous define calls' arguments (non-IE browsers)
53 | argsNet,
54 | // RegExp's used later, "cached" here
55 | dontAddExtRx = /\?/,
56 | absUrlRx = /^\/|^[^:]+:\/\//,
57 | findLeadingDotsRx = /(?:^|\/)(\.)(\.?)\/?/g,
58 | removeCommentsRx = /\/\*[\s\S]*?\*\/|(?:[^\\])\/\/.*?[\n\r]/g,
59 | findRValueRequiresRx = /require\s*\(\s*["']([^"']+)["']\s*\)|(?:[^\\]?)(["'])/g,
60 | cjsGetters,
61 | core;
62 |
63 | function noop () {}
64 |
65 | function isType (obj, type) {
66 | return toString.call(obj).indexOf('[object ' + type) == 0;
67 | }
68 |
69 | function normalizePkgDescriptor (descriptor) {
70 | var path, main;
71 |
72 | path = descriptor.path = removeEndSlash(descriptor['path'] || descriptor['location'] || '');
73 | main = descriptor['main'] || 'main';
74 | descriptor.config = descriptor['config'];
75 | descriptor.main = main.charAt(0) == '.' ?
76 | removeEndSlash(reduceLeadingDots(main, path)) :
77 | joinPath(path, main);
78 |
79 | return descriptor;
80 | }
81 |
82 | function joinPath (path, file) {
83 | return removeEndSlash(path) + '/' + file;
84 | }
85 |
86 | function removeEndSlash (path) {
87 | return path && path.charAt(path.length - 1) == '/' ? path.substr(0, path.length - 1) : path;
88 | //return endsWithSlash(path) ? path.substr(0, path.length - 1) : path;
89 | }
90 |
91 | function reduceLeadingDots (childId, baseId) {
92 | // this algorithm is similar to dojo's compactPath, which interprets
93 | // module ids of "." and ".." as meaning "grab the module whose name is
94 | // the same as my folder or parent folder". These special module ids
95 | // are not included in the AMD spec.
96 | var levels, removeLevels, isRelative;
97 | removeLevels = 1;
98 | childId = childId.replace(findLeadingDotsRx, function (m, dot, doubleDot) {
99 | if (doubleDot) removeLevels++;
100 | isRelative = true;
101 | return '';
102 | });
103 | // TODO: throw if removeLevels > baseId levels
104 | if (isRelative) {
105 | levels = baseId.split('/');
106 | levels.splice(levels.length - removeLevels, removeLevels);
107 | // childId || [] is a trick to not concat if no childId
108 | return levels.concat(childId || []).join('/');
109 | }
110 | else {
111 | return childId;
112 | }
113 | }
114 |
115 | function pluginParts (id) {
116 | var delPos = id.indexOf('!');
117 | return {
118 | resourceId: id.substr(delPos + 1),
119 | // resourceId can be zero length
120 | pluginId: delPos >= 0 && id.substr(0, delPos)
121 | };
122 | }
123 |
124 | function Begetter () {}
125 |
126 | function beget (parent) {
127 | Begetter.prototype = parent;
128 | var child = new Begetter();
129 | Begetter.prototype = undef;
130 | return child;
131 | }
132 |
133 | function Promise () {
134 |
135 | var self, thens, complete;
136 |
137 | self = this;
138 | thens = [];
139 |
140 | function then (resolved, rejected, progressed) {
141 | // capture calls to callbacks
142 | thens.push([resolved, rejected, progressed]);
143 | }
144 |
145 | function notify (which, arg) {
146 | // complete all callbacks
147 | var aThen, cb, i = 0;
148 | while ((aThen = thens[i++])) {
149 | cb = aThen[which];
150 | if (cb) cb(arg);
151 | }
152 | }
153 |
154 | complete = function promiseComplete (success, arg) {
155 | // switch over to sync then()
156 | then = success ?
157 | function (resolved, rejected) { resolved && resolved(arg); } :
158 | function (resolved, rejected) { rejected && rejected(arg); };
159 | // we no longer throw during multiple calls to resolve or reject
160 | // since we don't really provide useful information anyways.
161 | complete = noop;
162 | // complete all callbacks
163 | notify(success ? 0 : 1, arg);
164 | };
165 |
166 | this.then = function (resolved, rejected, progressed) {
167 | then(resolved, rejected, progressed);
168 | return self;
169 | };
170 | this.resolve = function (val) {
171 | self.resolved = val;
172 | complete(true, val);
173 | };
174 | this.reject = function (ex) {
175 | self.rejected = ex;
176 | complete(false, ex);
177 | };
178 | this.progress = function (msg) {
179 | notify(2, msg);
180 | }
181 |
182 | }
183 |
184 | var ResourceDef = Promise; // subclassing isn't worth the extra bytes
185 |
186 | function isPromise (o) {
187 | return o instanceof Promise;
188 | }
189 |
190 | function when (promiseOrValue, callback, errback, progback) {
191 | // we can't just sniff for then(). if we do, resources that have a
192 | // then() method will make dependencies wait!
193 | if (isPromise(promiseOrValue)) {
194 | promiseOrValue.then(callback, errback, progback);
195 | }
196 | else {
197 | callback(promiseOrValue);
198 | }
199 | }
200 |
201 | core = {
202 |
203 | createResourceDef: function (id, cfg, isPreload, optCtxId) {
204 | var def, ctxId;
205 |
206 | ctxId = optCtxId == undef ? id : optCtxId;
207 |
208 | def = new ResourceDef();
209 | def.id = id;
210 | def.isPreload = isPreload;
211 |
212 | // replace cache with resolved value (overwrites self in cache)
213 | def.then(function (res) { cache[id] = res; });
214 |
215 | // functions that dependencies will use:
216 |
217 | function toAbsId (childId) {
218 | return reduceLeadingDots(childId, ctxId);
219 | }
220 |
221 | function toUrl (n) {
222 | // even though internally, we don't seem to need to do
223 | // toAbsId, the AMD spec says we need to do this for plugins.
224 | // also, thesec states that we should not append an extension
225 | // in this function.
226 | return core.resolvePathInfo(toAbsId(n), cfg).url;
227 | }
228 |
229 | function localRequire (ids, callback) {
230 | var cb, rvid, childDef, earlyExport;
231 |
232 | // this is a public function, so remove ability for callback
233 | // fixes issue #41
234 | cb = callback && function () { callback.apply(undef, arguments[0]); };
235 |
236 | // RValue require (CommonJS)
237 | if (isType(ids, 'String')) {
238 | // return resource
239 | rvid = toAbsId(ids);
240 | childDef = cache[rvid];
241 | earlyExport = isPromise(childDef) && childDef.exports;
242 | if (!(rvid in cache) || (isPromise(childDef) && !earlyExport)) {
243 | throw new Error('Module not resolved: ' + rvid);
244 | }
245 | if (cb) {
246 | throw new Error('require(id, callback) not allowed');
247 | }
248 | return earlyExport || childDef;
249 | }
250 |
251 | // pass the callback, so the main def won't get resolved!
252 | core.getDeps(def, ids, cb);
253 |
254 | }
255 |
256 | def.require = localRequire;
257 | localRequire['toUrl'] = toUrl;
258 | def.toAbsId = toAbsId;
259 |
260 | return def;
261 | },
262 |
263 | getCjsRequire: function (def) {
264 | return def.require;
265 | },
266 |
267 | getCjsExports: function (def) {
268 | return def.exports || (def.exports = {});
269 | },
270 |
271 | getCjsModule: function (def) {
272 | var module = def.module;
273 | if (!module) {
274 | module = def.module = {
275 | 'id': def.id,
276 | 'uri': core.getDefUrl(def),
277 | 'exports': core.getCjsExports(def)
278 | };
279 | module.exports = module['exports'];
280 | }
281 | return module;
282 | },
283 |
284 | getDefUrl: function (def) {
285 | // note: don't look up an anon module's id from it's own toUrl cuz
286 | // the parent's config was used to find this module
287 | // the toUrl fallback is for named modules in built files
288 | // which must have absolute ids.
289 | return def.url || (def.url = core.checkToAddJsExt(def.require['toUrl'](def.id)));
290 | },
291 |
292 | extractCfg: function (cfg) {
293 | var pluginCfgs;
294 |
295 | // set defaults and convert from closure-safe names
296 | cfg.baseUrl = cfg['baseUrl'] || '';
297 | cfg.pluginPath = 'pluginPath' in cfg ? cfg['pluginPath'] : 'curl/plugin';
298 |
299 | // create object to hold path map.
300 | // each plugin and package will have its own pathMap, too.
301 | cfg.pathMap = {};
302 | pluginCfgs = cfg.plugins = cfg['plugins'] || {};
303 |
304 | // temporary arrays of paths. this will be converted to
305 | // a regexp for fast path parsing.
306 | cfg.pathList = [];
307 |
308 | // normalizes path/package info and places info on either
309 | // the global cfg.pathMap or on a plugin-specific altCfg.pathMap.
310 | // also populates a pathList on cfg or plugin configs.
311 | function fixAndPushPaths (coll, isPkg) {
312 | var id, pluginId, data, parts, pluginCfg, info;
313 | for (var name in coll) {
314 | data = coll[name];
315 | pluginCfg = cfg;
316 | // grab the package id, if specified. default to
317 | // property name.
318 | parts = pluginParts(removeEndSlash(data['id'] || data['name'] || name));
319 | id = parts.resourceId;
320 | pluginId = parts.pluginId;
321 | if (pluginId) {
322 | // plugin-specific path
323 | pluginCfg = pluginCfgs[pluginId];
324 | if (!pluginCfg) {
325 | pluginCfg = pluginCfgs[pluginId] = beget(cfg);
326 | pluginCfg.pathMap = beget(cfg.pathMap);
327 | pluginCfg.pathList = [];
328 | }
329 | // remove plugin-specific path from coll
330 | delete coll[name];
331 | }
332 | if (isPkg) {
333 | info = normalizePkgDescriptor(data);
334 | }
335 | else {
336 | info = { path: removeEndSlash(data) };
337 | }
338 | info.specificity = id.split('/').length;
339 | // info.specificity = (id.match(findSlashRx) || []).length;
340 | if (id) {
341 | pluginCfg.pathMap[id] = info;
342 | pluginCfg.pathList.push(id);
343 | }
344 | else {
345 | // naked plugin name signifies baseUrl for plugin
346 | // resources. baseUrl could be relative to global
347 | // baseUrl.
348 | pluginCfg.baseUrl = core.resolveUrl(data, cfg);
349 | }
350 | }
351 | }
352 |
353 | // adds the path matching regexp onto the cfg or plugin cfgs.
354 | function convertPathMatcher (cfg) {
355 | var pathMap = cfg.pathMap;
356 | cfg.pathRx = new RegExp('^(' +
357 | cfg.pathList.sort(function (a, b) { return pathMap[a].specificity < pathMap[b].specificity; } )
358 | .join('|')
359 | .replace(/\//g, '\\/') +
360 | ')(?=\\/|$)'
361 | );
362 | delete cfg.pathList;
363 | }
364 |
365 | // fix all paths and packages
366 | fixAndPushPaths(cfg['paths'], false);
367 | fixAndPushPaths(cfg['packages'], true);
368 |
369 | // create search regex for each path map
370 | for (var p in pluginCfgs) {
371 | var pathList = pluginCfgs[p].pathList;
372 | if (pathList) {
373 | pluginCfgs[p].pathList = pathList.concat(cfg.pathList);
374 | convertPathMatcher(pluginCfgs[p]);
375 | }
376 | }
377 | convertPathMatcher(cfg);
378 |
379 | return cfg;
380 |
381 | },
382 |
383 | checkPreloads: function (cfg) {
384 | var preloads = cfg['preloads'];
385 | if (preloads && preloads.length > 0){
386 | // chain from previous preload, if any (revisit when
387 | // doing package-specific configs).
388 | when(preload, function () {
389 | preload = core.createResourceDef(undef, cfg, true);
390 | core.getDeps(preload, preloads);
391 | });
392 | }
393 |
394 | },
395 |
396 | resolvePathInfo: function (id, cfg, forPlugin) {
397 | // searches through the configured path mappings and packages
398 | var pathMap, pathInfo, path, pkgCfg, found;
399 |
400 | pathMap = cfg.pathMap;
401 |
402 | if (forPlugin && cfg.pluginPath && id.indexOf('/') < 0 && !(id in pathMap)) {
403 | // prepend plugin folder path, if it's missing and path isn't in pathMap
404 | // Note: this munges the concepts of ids and paths for plugins,
405 | // but is generally safe since it's only for non-namespaced
406 | // plugins (plugins without path or package info).
407 | id = joinPath(cfg.pluginPath, id);
408 | }
409 |
410 | if (!absUrlRx.test(id)) {
411 | path = id.replace(cfg.pathRx, function (match) {
412 |
413 | pathInfo = pathMap[match] || {};
414 | found = true;
415 | pkgCfg = pathInfo.config;
416 |
417 | // if pathInfo.main and match == id, this is a main module
418 | if (pathInfo.main && match == id) {
419 | return pathInfo.main;
420 | }
421 | // if pathInfo.path return pathInfo.path
422 | else {
423 | return pathInfo.path || '';
424 | }
425 |
426 | });
427 | }
428 | else {
429 | path = id;
430 | }
431 |
432 | return {
433 | path: path,
434 | config: pkgCfg || userCfg,
435 | url: core.resolveUrl(path, cfg)
436 | };
437 | },
438 |
439 | resolveUrl: function (path, cfg) {
440 | var baseUrl = cfg.baseUrl;
441 | return baseUrl && !absUrlRx.test(path) ? joinPath(baseUrl, path) : path;
442 | },
443 |
444 | checkToAddJsExt: function (url) {
445 | // don't add extension if a ? is found in the url (query params)
446 | // i'd like to move this feature to a moduleLoader
447 | return url + (dontAddExtRx.test(url) ? '' : '.js');
448 | },
449 |
450 | loadScript: function (def, success, failure) {
451 | // script processing rules learned from RequireJS
452 |
453 | // insert script
454 | var el = doc.createElement('script');
455 |
456 | // initial script processing
457 | function process (ev) {
458 | ev = ev || global.event;
459 | // detect when it's done loading
460 | if (ev.type == 'load' || readyStates[el.readyState]) {
461 | delete activeScripts[def.id];
462 | // release event listeners
463 | el.onload = el.onreadystatechange = el.onerror = ''; // ie cries if we use undefined
464 | success();
465 | }
466 | }
467 |
468 | function fail (e) {
469 | // some browsers send an event, others send a string,
470 | // but none of them send anything useful, so just say we failed:
471 | failure(new Error('Syntax or http error: ' + def.url));
472 | }
473 |
474 | // set type first since setting other properties could
475 | // prevent us from setting this later
476 | // actually, we don't even need to set this at all
477 | //el.type = 'text/javascript';
478 | // using dom0 event handlers instead of wordy w3c/ms
479 | el.onload = el.onreadystatechange = process;
480 | el.onerror = fail;
481 | // TODO: support other charsets?
482 | el.charset = 'utf-8';
483 | el.async = true;
484 | el.src = def.url;
485 |
486 | // loading will start when the script is inserted into the dom.
487 | // IE will load the script sync if it's in the cache, so
488 | // indicate the current resource definition if this happens.
489 | activeScripts[def.id] = el;
490 | // use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!)
491 | head.insertBefore(el, head.firstChild);
492 |
493 | },
494 |
495 | extractCjsDeps: function (defFunc) {
496 | // Note: ignores require() inside strings and comments
497 | var source, ids = [], currQuote;
498 | // prefer toSource (FF) since it strips comments
499 | source = typeof defFunc == 'string' ?
500 | defFunc :
501 | defFunc.toSource ? defFunc.toSource() : defFunc.toString();
502 | // remove comments, then look for require() or quotes
503 | source.replace(removeCommentsRx, '').replace(findRValueRequiresRx, function (m, id, qq) {
504 | // if we encounter a quote
505 | if (qq) {
506 | currQuote = currQuote == qq ? undef : currQuote;
507 | }
508 | // if we're not inside a quoted string
509 | else if (!currQuote) {
510 | ids.push(id);
511 | }
512 | return m; // uses least RAM/CPU
513 | });
514 | return ids;
515 | },
516 |
517 | fixArgs: function (args) {
518 | // resolve args
519 | // valid combinations for define:
520 | // (string, array, object|function) sax|saf
521 | // (array, object|function) ax|af
522 | // (string, object|function) sx|sf
523 | // (object|function) x|f
524 |
525 | var id, deps, defFunc, isDefFunc, len, cjs;
526 |
527 | len = args.length;
528 |
529 | defFunc = args[len - 1];
530 | isDefFunc = isType(defFunc, 'Function');
531 |
532 | if (len == 2) {
533 | if (isType(args[0], 'Array')) {
534 | deps = args[0];
535 | }
536 | else {
537 | id = args[0];
538 | }
539 | }
540 | else if (len == 3) {
541 | id = args[0];
542 | deps = args[1];
543 | }
544 |
545 | // Hybrid format: assume that a definition function with zero
546 | // dependencies and non-zero arity is a wrapped CommonJS module
547 | if (!deps && isDefFunc && defFunc.length > 0) {
548 | cjs = true;
549 | deps = ['require', 'exports', 'module'].concat(core.extractCjsDeps(defFunc));
550 | }
551 |
552 | return {
553 | id: id,
554 | deps: deps || [],
555 | res: isDefFunc ? defFunc : function () { return defFunc; },
556 | cjs: cjs
557 | };
558 | },
559 |
560 | executeDefFunc: function (def) {
561 | var resource, moduleThis;
562 | // the force of AMD is strong so anything returned
563 | // overrides exports.
564 | // node.js assumes `this` === `exports` so we do that
565 | // for all cjs-wrapped modules, just in case.
566 | // also, use module.exports if that was set
567 | // (node.js convention).
568 | // note: if .module exists, .exports exists.
569 | moduleThis = def.cjs ? def.exports : undef;
570 | resource = def.res.apply(moduleThis, def.deps);
571 | if (resource === undef && def.exports) {
572 | // note: exports will equal module.exports unless
573 | // module.exports was reassigned inside module.
574 | resource = def.module ? def.module.exports : def.exports;
575 | }
576 | return resource;
577 | },
578 |
579 | resolveResDef: function (def, args) {
580 |
581 | def.cjs = args.cjs;
582 |
583 | // get the dependencies and then resolve/reject
584 | core.getDeps(def, args.deps,
585 | function (deps) {
586 | var resource;
587 | def.deps = deps;
588 | def.res = args.res;
589 | try {
590 | resource = core.executeDefFunc(def);
591 | }
592 | catch (ex) {
593 | def.reject(ex);
594 | }
595 | def.resolve(resource);
596 | }
597 | );
598 |
599 | },
600 |
601 | fetchResDef: function (def) {
602 |
603 | // ensure url is computed
604 | core.getDefUrl(def);
605 |
606 | core.loadScript(def,
607 |
608 | function () {
609 | var args = argsNet;
610 | argsNet = undef; // reset it before we get deps
611 |
612 | // if our resource was not explicitly defined with an id (anonymous)
613 | // Note: if it did have an id, it will be resolved in the define()
614 | if (def.useNet !== false) {
615 |
616 | // if !args, nothing was added to the argsNet
617 | if (!args || args.ex) {
618 | def.reject(new Error(((args && args.ex) || 'define() missing or duplicated: url').replace('url', def.url)));
619 | }
620 | else {
621 | core.resolveResDef(def, args);
622 | }
623 | }
624 |
625 | },
626 |
627 | def.reject
628 |
629 | );
630 |
631 | return def;
632 |
633 | },
634 |
635 | fetchDep: function (depName, parentDef) {
636 | var toAbsId, isPreload, parts, mainId, loaderId, pluginId,
637 | resId, pathInfo, def, tempDef, resCfg;
638 |
639 | toAbsId = parentDef.toAbsId;
640 | isPreload = parentDef.isPreload;
641 |
642 | // check for plugin loaderId
643 | parts = pluginParts(depName);
644 | // resId is not normalized since the plugin may need to do it
645 | resId = parts.resourceId;
646 |
647 | // get id of first resource to load (which could be a plugin)
648 | mainId = toAbsId(parts.pluginId || resId);
649 | pathInfo = core.resolvePathInfo(mainId, userCfg, !!parts.pluginId);
650 |
651 | // get custom module loader from package config if not a plugin
652 | // TODO: figure out how to make module loaders work with plugins
653 | if (parts.pluginId) {
654 | loaderId = mainId;
655 | }
656 | else {
657 | loaderId = pathInfo.config['moduleLoader'];
658 | if (loaderId) {
659 | // since we're not using toAbsId, transformers must be absolute
660 | resId = mainId;
661 | mainId = loaderId;
662 | pathInfo = core.resolvePathInfo(loaderId, userCfg);
663 | }
664 | }
665 |
666 | // find resource definition
667 | def = cache[mainId];
668 | if (!def) {
669 | def = cache[mainId] = core.createResourceDef(mainId, pathInfo.config, isPreload, pathInfo.path);
670 | def.url = core.checkToAddJsExt(pathInfo.url);
671 | core.fetchResDef(def);
672 | }
673 |
674 | // plugin or transformer
675 | if (mainId == loaderId) {
676 |
677 | // we need to use depName until plugin tells us normalized id.
678 | // if the plugin changes the id, we need to consolidate
679 | // def promises below. Note: exports objects will be different
680 | // between pre-normalized and post-normalized defs! does this matter?
681 | // don't put this resource def in the cache because if the
682 | // resId doesn't change, the check if this is a new
683 | // normalizedDef (below) will think it's already being loaded.
684 | tempDef = /*cache[depName] =*/ core.createResourceDef(depName);
685 |
686 | // note: this means moduleLoaders can store config info in the
687 | // plugins config, too.
688 | resCfg = userCfg.plugins[loaderId] || userCfg;
689 |
690 | // wait for plugin resource def
691 | when(def, function(plugin) {
692 | var normalizedDef, fullId;
693 |
694 | // check if plugin supports the normalize method
695 | if ('normalize' in plugin) {
696 | // dojo/has may return falsey values (0, actually)
697 | resId = plugin['normalize'](resId, toAbsId, resCfg) || '';
698 | }
699 | else {
700 | resId = toAbsId(resId);
701 | }
702 |
703 | // use the full id (loaderId + id) to id plugin resources
704 | // so multiple plugins may each process the same resource
705 | // resId could be blank if the plugin doesn't require any (e.g. "domReady!")
706 | fullId = loaderId + '!' + resId;
707 | normalizedDef = cache[fullId];
708 |
709 | // if this is our first time fetching this (normalized) def
710 | if (!normalizedDef) {
711 |
712 | // because we're using resId, plugins, such as wire!,
713 | // can use paths relative to the resource
714 | normalizedDef = core.createResourceDef(fullId, resCfg, isPreload, resId);
715 |
716 | // don't cache non-determinate "dynamic" resources (or non-existent resources)
717 | if (!plugin['dynamic']) {
718 | cache[fullId] = normalizedDef;
719 | }
720 |
721 | // curl's plugins prefer to receive a deferred,
722 | // but to be compatible with AMD spec, we have to
723 | // piggy-back on the callback function parameter:
724 | var loaded = function (res) {
725 | normalizedDef.resolve(res);
726 | if (plugin['dynamic']) delete cache[fullId];
727 | };
728 | loaded['resolve'] = loaded;
729 | loaded['reject'] = normalizedDef.reject;
730 |
731 | // load the resource!
732 | plugin.load(resId, normalizedDef.require, loaded, resCfg);
733 |
734 | }
735 |
736 | // chain defs (resolve when plugin.load executes)
737 | if (tempDef != normalizedDef) {
738 | when(normalizedDef, tempDef.resolve, tempDef.reject);
739 | }
740 |
741 | }, tempDef.reject);
742 |
743 | }
744 |
745 | // return tempDef if this is a plugin-based resource
746 | return tempDef || def;
747 | },
748 |
749 | getDeps: function (parentDef, names, overrideCallback) {
750 | var deps, count, len, i, name, completed, callback;
751 |
752 | deps = [];
753 | count = len = names.length;
754 | completed = false;
755 | callback = overrideCallback || parentDef.resolve;
756 |
757 | function checkDone () {
758 | if (--count == 0) {
759 | // Note: IE may have obtained the dependencies sync, thus the completed flag
760 | completed = true;
761 | callback(deps);
762 | }
763 | }
764 |
765 | function getDep (index, depName) {
766 | var childDef, doOnce;
767 |
768 | childDef = core.fetchDep(depName, parentDef);
769 |
770 | doOnce = function (dep) {
771 | deps[index] = dep; // got it!
772 | checkDone();
773 | // only run once for this dep (in case of early exports)
774 | doOnce = noop;
775 | };
776 |
777 | function doSuccess (dep) {
778 | doOnce(dep);
779 | }
780 |
781 | function doFailure (ex) {
782 | completed = true;
783 | parentDef.reject(ex);
784 | }
785 |
786 | function doProgress (msg) {
787 | // only early-export to modules that also export since
788 | // pure AMD modules don't expect to get an early export
789 | // Note: this logic makes dojo 1.7 work, too.
790 | if (msg == msgUsingExports && parentDef.exports) {
791 | doOnce(childDef.exports);
792 | }
793 | }
794 |
795 | // hook into promise callbacks.
796 | when(childDef, doSuccess, doFailure, doProgress);
797 |
798 | }
799 |
800 | // wait for preload before fetching any other modules
801 | when(parentDef.isPreload || preload, function () {
802 |
803 | for (i = 0; i < len && !completed; i++) {
804 | name = names[i];
805 | if (name in cjsGetters) {
806 | // is this "require", "exports", or "module"?
807 | deps[i] = cjsGetters[name](parentDef);
808 | checkDone();
809 | }
810 | // check for blanks. fixes #32.
811 | // this helps support yepnope.js, has.js, and the has! plugin
812 | else if (names[i]) {
813 | getDep(i, names[i]);
814 | }
815 | else {
816 | checkDone();
817 | }
818 | }
819 |
820 | if (parentDef.exports) {
821 | // announce
822 | parentDef.progress(msgUsingExports);
823 | }
824 |
825 | if (count == 0 && !completed) {
826 | // there were none to fetch
827 | callback(deps);
828 | }
829 |
830 | });
831 |
832 | },
833 |
834 | getCurrentDefName: function () {
835 | // IE marks the currently executing thread as "interactive"
836 | // Note: Opera lies about which scripts are "interactive", so we
837 | // just have to test for it. Opera provides a true browser test, not
838 | // a UA sniff, thankfully.
839 | // learned this trick from James Burke's RequireJS
840 | var def;
841 | if (!isType(global.opera, 'Opera')) {
842 | for (var d in activeScripts) {
843 | if (readyStates[activeScripts[d].readyState] == interactive) {
844 | def = d;
845 | break;
846 | }
847 | }
848 | }
849 | return def;
850 | }
851 |
852 | };
853 |
854 | // hook-up cjs free variable getters
855 | cjsGetters = {'require': core.getCjsRequire, 'exports': core.getCjsExports, 'module': core.getCjsModule};
856 |
857 | function _curl (/* various */) {
858 |
859 | var args = [].slice.call(arguments), ids;
860 |
861 | // extract config, if it's specified
862 | if (isType(args[0], 'Object')) {
863 | userCfg = core.extractCfg(args.shift());
864 | core.checkPreloads(userCfg);
865 | }
866 |
867 | // thanks to Joop Ringelberg for helping troubleshoot the API
868 | function CurlApi (ids, callback, waitFor) {
869 | var then, def;
870 | def = core.createResourceDef(undef, userCfg);
871 | this['then'] = then = function (resolved, rejected) {
872 | when(def,
873 | // return the dependencies as arguments, not an array
874 | function (deps) { if (resolved) resolved.apply(undef, deps); },
875 | // just throw if the dev didn't specify an error handler
876 | function (ex) { if (rejected) rejected(ex); else throw ex; }
877 | );
878 | return this;
879 | };
880 | this['next'] = function (ids, cb) {
881 | // chain api
882 | return new CurlApi(ids, cb, def);
883 | };
884 | if (callback) then(callback);
885 | when(waitFor, function () {
886 | core.getDeps(def, [].concat(ids));
887 | });
888 | }
889 |
890 | ids = [].concat(args[0]); // force to array TODO: create unit test
891 | return new CurlApi(ids, args[1]);
892 |
893 | }
894 |
895 | function _define (args) {
896 |
897 | var id = args.id;
898 |
899 | if (id == undef) {
900 | if (argsNet !== undef) {
901 | argsNet = {ex: 'Multiple anonymous defines in url'};
902 | }
903 | else if (!(id = core.getCurrentDefName())/* intentional assignment */) {
904 | // anonymous define(), defer processing until after script loads
905 | argsNet = args;
906 | }
907 | }
908 | if (id != undef) {
909 | // named define(), it is in the cache if we are loading a dependency
910 | // (could also be a secondary define() appearing in a built file, etc.)
911 | var def = cache[id];
912 | if (!def) {
913 | // id is an absolute id in this case, so we can get the config.
914 | // there's no way to allow a named define to fetch dependencies
915 | // in the preload phase since we can't cascade the parent def.
916 | var cfg = core.resolvePathInfo(id, userCfg).config;
917 | def = cache[id] = core.createResourceDef(id, cfg);
918 | }
919 | // check if this resource has already been resolved (can happen if
920 | // a module was defined inside a built file and outside of it and
921 | // dev didn't coordinate it explicitly)
922 | if (isPromise(def)) {
923 | def.useNet = false;
924 | core.resolveResDef(def, args);
925 | }
926 | }
927 |
928 | }
929 |
930 | /***** grab any global configuration info *****/
931 |
932 | // if userCfg is a function, assume curl() exists already
933 | if (isType(userCfg, 'Function')) return;
934 |
935 | userCfg = core.extractCfg(userCfg || {});
936 | core.checkPreloads(userCfg);
937 |
938 | /***** define public API *****/
939 |
940 | var apiName, apiContext, define;
941 |
942 | // allow curl to be renamed and added to a specified context
943 | apiName = userCfg['apiName'] || 'curl';
944 | apiContext = userCfg['apiContext'] || global;
945 | apiContext[apiName] = _curl;
946 |
947 | // allow curl to be a dependency
948 | cache['curl'] = _curl;
949 |
950 | // wrap inner _define so it can be replaced without losing define.amd
951 | define = global['define'] = function () {
952 | var args = core.fixArgs(arguments);
953 | _define(args);
954 | };
955 | _curl['version'] = version;
956 |
957 | // indicate our capabilities:
958 | define['amd'] = { 'plugins': true, 'jQuery': true, 'curl': version };
959 |
960 | // expose curl core for special plugins and modules
961 | // Note: core overrides will only work in either of two scenarios:
962 | // 1. the files are running un-compressed (Google Closure or Uglify)
963 | // 2. the overriding module was compressed with curl.js
964 | // Compiling curl and the overriding module separately won't work.
965 | cache['curl/_privileged'] = {
966 | 'core': core,
967 | 'cache': cache,
968 | 'cfg': userCfg,
969 | '_define': _define,
970 | '_curl': _curl,
971 | 'ResourceDef': ResourceDef
972 | };
973 |
974 | }(this));
--------------------------------------------------------------------------------
/lib/json3.js:
--------------------------------------------------------------------------------
1 | /*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */
2 | ;(function () {
3 | // Convenience aliases.
4 | var getClass = {}.toString, isProperty, forEach, undef;
5 |
6 | // Detect the `define` function exposed by asynchronous module loaders. The
7 | // strict `define` check is necessary for compatibility with `r.js`.
8 | var isLoader = typeof define === "function" && define.amd, JSON3 = !isLoader && typeof exports == "object" && exports;
9 |
10 | if (JSON3 || isLoader) {
11 | if (typeof JSON == "object" && JSON) {
12 | // Delegate to the native `stringify` and `parse` implementations in
13 | // asynchronous module loaders and CommonJS environments.
14 | if (isLoader) {
15 | JSON3 = JSON;
16 | } else {
17 | JSON3.stringify = JSON.stringify;
18 | JSON3.parse = JSON.parse;
19 | }
20 | } else if (isLoader) {
21 | JSON3 = this.JSON = {};
22 | }
23 | } else {
24 | // Export for web browsers and JavaScript engines.
25 | JSON3 = this.JSON || (this.JSON = {});
26 | }
27 |
28 | // Local variables.
29 | var Escapes, toPaddedString, quote, serialize;
30 | var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source;
31 |
32 | // Test the `Date#getUTC*` methods. Based on work by @Yaffle.
33 | var isExtended = new Date(-3509827334573292), floor, Months, getDay;
34 |
35 | try {
36 | // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
37 | // results for certain dates in Opera >= 10.53.
38 | isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() == 1 &&
39 | // Safari < 2.0.2 stores the internal millisecond time value correctly,
40 | // but clips the values returned by the date methods to the range of
41 | // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
42 | isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
43 | } catch (exception) {}
44 |
45 | // Internal: Determines whether the native `JSON.stringify` and `parse`
46 | // implementations are spec-compliant. Based on work by Ken Snyder.
47 | function has(name) {
48 | var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json";
49 | if (all || name == "json-stringify" || name == "json-parse") {
50 | // Test `JSON.stringify`.
51 | if (name == "json-stringify" || all) {
52 | if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) {
53 | // A test function object with a custom `toJSON` method.
54 | (value = function () {
55 | return 1;
56 | }).toJSON = value;
57 | try {
58 | stringifySupported =
59 | // Firefox 3.1b1 and b2 serialize string, number, and boolean
60 | // primitives as object literals.
61 | JSON3.stringify(0) === "0" &&
62 | // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
63 | // literals.
64 | JSON3.stringify(new Number()) === "0" &&
65 | JSON3.stringify(new String()) == '""' &&
66 | // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
67 | // does not define a canonical JSON representation (this applies to
68 | // objects with `toJSON` properties as well, *unless* they are nested
69 | // within an object or array).
70 | JSON3.stringify(getClass) === undef &&
71 | // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
72 | // FF 3.1b3 pass this test.
73 | JSON3.stringify(undef) === undef &&
74 | // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
75 | // respectively, if the value is omitted entirely.
76 | JSON3.stringify() === undef &&
77 | // FF 3.1b1, 2 throw an error if the given value is not a number,
78 | // string, array, object, Boolean, or `null` literal. This applies to
79 | // objects with custom `toJSON` methods as well, unless they are nested
80 | // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
81 | // methods entirely.
82 | JSON3.stringify(value) === "1" &&
83 | JSON3.stringify([value]) == "[1]" &&
84 | // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
85 | // `"[null]"`.
86 | JSON3.stringify([undef]) == "[null]" &&
87 | // YUI 3.0.0b1 fails to serialize `null` literals.
88 | JSON3.stringify(null) == "null" &&
89 | // FF 3.1b1, 2 halts serialization if an array contains a function:
90 | // `[1, true, getClass, 1]` serializes as "[1,true,],". These versions
91 | // of Firefox also allow trailing commas in JSON objects and arrays.
92 | // FF 3.1b3 elides non-JSON values from objects and arrays, unless they
93 | // define custom `toJSON` methods.
94 | JSON3.stringify([undef, getClass, null]) == "[null,null,null]" &&
95 | // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
96 | // where character escape codes are expected (e.g., `\b` => `\u0008`).
97 | JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized &&
98 | // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
99 | JSON3.stringify(null, value) === "1" &&
100 | JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
101 | // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
102 | // serialize extended years.
103 | JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
104 | // The milliseconds are optional in ES 5, but required in 5.1.
105 | JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
106 | // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
107 | // four-digit years instead of six-digit years. Credits: @Yaffle.
108 | JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
109 | // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
110 | // values less than 1000. Credits: @Yaffle.
111 | JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
112 | } catch (exception) {
113 | stringifySupported = false;
114 | }
115 | }
116 | if (!all) {
117 | return stringifySupported;
118 | }
119 | }
120 | // Test `JSON.parse`.
121 | if (name == "json-parse" || all) {
122 | if (typeof JSON3.parse == "function") {
123 | try {
124 | // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
125 | // Conforming implementations should also coerce the initial argument to
126 | // a string prior to parsing.
127 | if (JSON3.parse("0") === 0 && !JSON3.parse(false)) {
128 | // Simple parsing test.
129 | value = JSON3.parse(serialized);
130 | if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) {
131 | try {
132 | // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
133 | parseSupported = !JSON3.parse('"\t"');
134 | } catch (exception) {}
135 | if (parseSupported) {
136 | try {
137 | // FF 4.0 and 4.0.1 allow leading `+` signs, and leading and
138 | // trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also
139 | // allow certain octal literals.
140 | parseSupported = JSON3.parse("01") != 1;
141 | } catch (exception) {}
142 | }
143 | }
144 | }
145 | } catch (exception) {
146 | parseSupported = false;
147 | }
148 | }
149 | if (!all) {
150 | return parseSupported;
151 | }
152 | }
153 | return stringifySupported && parseSupported;
154 | }
155 | }
156 |
157 | if (!has("json")) {
158 | // Define additional utility methods if the `Date` methods are buggy.
159 | if (!isExtended) {
160 | floor = Math.floor;
161 | // A mapping between the months of the year and the number of days between
162 | // January 1st and the first of the respective month.
163 | Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
164 | // Internal: Calculates the number of days between the Unix epoch and the
165 | // first day of the given month.
166 | getDay = function (year, month) {
167 | return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
168 | };
169 | }
170 |
171 | // Internal: Determines if a property is a direct property of the given
172 | // object. Delegates to the native `Object#hasOwnProperty` method.
173 | if (!(isProperty = {}.hasOwnProperty)) {
174 | isProperty = function (property) {
175 | var members = {}, constructor;
176 | if ((members.__proto__ = null, members.__proto__ = {
177 | // The *proto* property cannot be set multiple times in recent
178 | // versions of Firefox and SeaMonkey.
179 | "toString": 1
180 | }, members).toString != getClass) {
181 | // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
182 | // supports the mutable *proto* property.
183 | isProperty = function (property) {
184 | // Capture and break the object's prototype chain (see section 8.6.2
185 | // of the ES 5.1 spec). The parenthesized expression prevents an
186 | // unsafe transformation by the Closure Compiler.
187 | var original = this.__proto__, result = property in (this.__proto__ = null, this);
188 | // Restore the original prototype chain.
189 | this.__proto__ = original;
190 | return result;
191 | };
192 | } else {
193 | // Capture a reference to the top-level `Object` constructor.
194 | constructor = members.constructor;
195 | // Use the `constructor` property to simulate `Object#hasOwnProperty` in
196 | // other environments.
197 | isProperty = function (property) {
198 | var parent = (this.constructor || constructor).prototype;
199 | return property in this && !(property in parent && this[property] === parent[property]);
200 | };
201 | }
202 | members = null;
203 | return isProperty.call(this, property);
204 | };
205 | }
206 |
207 | // Internal: Normalizes the `for...in` iteration algorithm across
208 | // environments. Each enumerated key is yielded to a `callback` function.
209 | forEach = function (object, callback) {
210 | var size = 0, Properties, members, property, forEach;
211 |
212 | // Tests for bugs in the current environment's `for...in` algorithm. The
213 | // `valueOf` property inherits the non-enumerable flag from
214 | // `Object.prototype` in older versions of IE, Netscape, and Mozilla.
215 | (Properties = function () {
216 | this.valueOf = 0;
217 | }).prototype.valueOf = 0;
218 |
219 | // Iterate over a new instance of the `Properties` class.
220 | members = new Properties();
221 | for (property in members) {
222 | // Ignore all properties inherited from `Object.prototype`.
223 | if (isProperty.call(members, property)) {
224 | size++;
225 | }
226 | }
227 | Properties = members = null;
228 |
229 | // Normalize the iteration algorithm.
230 | if (!size) {
231 | // A list of non-enumerable properties inherited from `Object.prototype`.
232 | members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
233 | // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
234 | // properties.
235 | forEach = function (object, callback) {
236 | var isFunction = getClass.call(object) == "[object Function]", property, length;
237 | for (property in object) {
238 | // Gecko <= 1.0 enumerates the `prototype` property of functions under
239 | // certain conditions; IE does not.
240 | if (!(isFunction && property == "prototype") && isProperty.call(object, property)) {
241 | callback(property);
242 | }
243 | }
244 | // Manually invoke the callback for each non-enumerable property.
245 | for (length = members.length; property = members[--length]; isProperty.call(object, property) && callback(property));
246 | };
247 | } else if (size == 2) {
248 | // Safari <= 2.0.4 enumerates shadowed properties twice.
249 | forEach = function (object, callback) {
250 | // Create a set of iterated properties.
251 | var members = {}, isFunction = getClass.call(object) == "[object Function]", property;
252 | for (property in object) {
253 | // Store each property name to prevent double enumeration. The
254 | // `prototype` property of functions is not enumerated due to cross-
255 | // environment inconsistencies.
256 | if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
257 | callback(property);
258 | }
259 | }
260 | };
261 | } else {
262 | // No bugs detected; use the standard `for...in` algorithm.
263 | forEach = function (object, callback) {
264 | var isFunction = getClass.call(object) == "[object Function]", property, isConstructor;
265 | for (property in object) {
266 | if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
267 | callback(property);
268 | }
269 | }
270 | // Manually invoke the callback for the `constructor` property due to
271 | // cross-environment inconsistencies.
272 | if (isConstructor || isProperty.call(object, (property = "constructor"))) {
273 | callback(property);
274 | }
275 | };
276 | }
277 | return forEach(object, callback);
278 | };
279 |
280 | // Public: Serializes a JavaScript `value` as a JSON string. The optional
281 | // `filter` argument may specify either a function that alters how object and
282 | // array members are serialized, or an array of strings and numbers that
283 | // indicates which properties should be serialized. The optional `width`
284 | // argument may be either a string or number that specifies the indentation
285 | // level of the output.
286 | if (!has("json-stringify")) {
287 | // Internal: A map of control characters and their escaped equivalents.
288 | Escapes = {
289 | "\\": "\\\\",
290 | '"': '\\"',
291 | "\b": "\\b",
292 | "\f": "\\f",
293 | "\n": "\\n",
294 | "\r": "\\r",
295 | "\t": "\\t"
296 | };
297 |
298 | // Internal: Converts `value` into a zero-padded string such that its
299 | // length is at least equal to `width`. The `width` must be <= 6.
300 | toPaddedString = function (width, value) {
301 | // The `|| 0` expression is necessary to work around a bug in
302 | // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
303 | return ("000000" + (value || 0)).slice(-width);
304 | };
305 |
306 | // Internal: Double-quotes a string `value`, replacing all ASCII control
307 | // characters (characters with code unit values between 0 and 31) with
308 | // their escaped equivalents. This is an implementation of the
309 | // `Quote(value)` operation defined in ES 5.1 section 15.12.3.
310 | quote = function (value) {
311 | var result = '"', index = 0, symbol;
312 | for (; symbol = value.charAt(index); index++) {
313 | // Escape the reverse solidus, double quote, backspace, form feed, line
314 | // feed, carriage return, and tab characters.
315 | result += '\\"\b\f\n\r\t'.indexOf(symbol) > -1 ? Escapes[symbol] :
316 | // If the character is a control character, append its Unicode escape
317 | // sequence; otherwise, append the character as-is.
318 | (Escapes[symbol] = symbol < " " ? "\\u00" + toPaddedString(2, symbol.charCodeAt(0).toString(16)) : symbol);
319 | }
320 | return result + '"';
321 | };
322 |
323 | // Internal: Recursively serializes an object. Implements the
324 | // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
325 | serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
326 | var value = object[property], className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, any, result;
327 | if (typeof value == "object" && value) {
328 | className = getClass.call(value);
329 | if (className == "[object Date]" && !isProperty.call(value, "toJSON")) {
330 | if (value > -1 / 0 && value < 1 / 0) {
331 | // Dates are serialized according to the `Date#toJSON` method
332 | // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
333 | // for the ISO 8601 date time string format.
334 | if (getDay) {
335 | // Manually compute the year, month, date, hours, minutes,
336 | // seconds, and milliseconds if the `getUTC*` methods are
337 | // buggy. Adapted from @Yaffle's `date-shim` project.
338 | date = floor(value / 864e5);
339 | for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
340 | for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
341 | date = 1 + date - getDay(year, month);
342 | // The `time` value specifies the time within the day (see ES
343 | // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
344 | // to compute `A modulo B`, as the `%` operator does not
345 | // correspond to the `modulo` operation for negative numbers.
346 | time = (value % 864e5 + 864e5) % 864e5;
347 | // The hours, minutes, seconds, and milliseconds are obtained by
348 | // decomposing the time within the day. See section 15.9.1.10.
349 | hours = floor(time / 36e5) % 24;
350 | minutes = floor(time / 6e4) % 60;
351 | seconds = floor(time / 1e3) % 60;
352 | milliseconds = time % 1e3;
353 | } else {
354 | year = value.getUTCFullYear();
355 | month = value.getUTCMonth();
356 | date = value.getUTCDate();
357 | hours = value.getUTCHours();
358 | minutes = value.getUTCMinutes();
359 | seconds = value.getUTCSeconds();
360 | milliseconds = value.getUTCMilliseconds();
361 | }
362 | // Serialize extended years correctly.
363 | value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
364 | "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
365 | // Months, dates, hours, minutes, and seconds should have two
366 | // digits; milliseconds should have three.
367 | "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
368 | // Milliseconds are optional in ES 5.0, but required in 5.1.
369 | "." + toPaddedString(3, milliseconds) + "Z";
370 | } else {
371 | value = null;
372 | }
373 | } else if (typeof value.toJSON == "function" && ((className != "[object Number]" && className != "[object String]" && className != "[object Array]") || isProperty.call(value, "toJSON"))) {
374 | // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
375 | // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
376 | // ignores all `toJSON` methods on these objects unless they are
377 | // defined directly on an instance.
378 | value = value.toJSON(property);
379 | }
380 | }
381 | if (callback) {
382 | // If a replacement function was provided, call it to obtain the value
383 | // for serialization.
384 | value = callback.call(object, property, value);
385 | }
386 | if (value === null) {
387 | return "null";
388 | }
389 | className = getClass.call(value);
390 | if (className == "[object Boolean]") {
391 | // Booleans are represented literally.
392 | return "" + value;
393 | } else if (className == "[object Number]") {
394 | // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
395 | // `"null"`.
396 | return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
397 | } else if (className == "[object String]") {
398 | // Strings are double-quoted and escaped.
399 | return quote(value);
400 | }
401 | // Recursively serialize objects and arrays.
402 | if (typeof value == "object") {
403 | // Check for cyclic structures. This is a linear search; performance
404 | // is inversely proportional to the number of unique nested objects.
405 | for (length = stack.length; length--;) {
406 | if (stack[length] === value) {
407 | // Cyclic structures cannot be serialized by `JSON.stringify`.
408 | throw TypeError();
409 | }
410 | }
411 | // Add the object to the stack of traversed objects.
412 | stack.push(value);
413 | results = [];
414 | // Save the current indentation level and indent one additional level.
415 | prefix = indentation;
416 | indentation += whitespace;
417 | if (className == "[object Array]") {
418 | // Recursively serialize array elements.
419 | for (index = 0, length = value.length; index < length; any || (any = true), index++) {
420 | element = serialize(index, value, callback, properties, whitespace, indentation, stack);
421 | results.push(element === undef ? "null" : element);
422 | }
423 | result = any ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
424 | } else {
425 | // Recursively serialize object members. Members are selected from
426 | // either a user-specified list of property names, or the object
427 | // itself.
428 | forEach(properties || value, function (property) {
429 | var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
430 | if (element !== undef) {
431 | // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
432 | // is not the empty string, let `member` {quote(property) + ":"}
433 | // be the concatenation of `member` and the `space` character."
434 | // The "`space` character" refers to the literal space
435 | // character, not the `space` {width} argument provided to
436 | // `JSON.stringify`.
437 | results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
438 | }
439 | any || (any = true);
440 | });
441 | result = any ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
442 | }
443 | // Remove the object from the traversed object stack.
444 | stack.pop();
445 | return result;
446 | }
447 | };
448 |
449 | // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
450 | JSON3.stringify = function (source, filter, width) {
451 | var whitespace, callback, properties, index, length, value;
452 | if (typeof filter == "function" || typeof filter == "object" && filter) {
453 | if (getClass.call(filter) == "[object Function]") {
454 | callback = filter;
455 | } else if (getClass.call(filter) == "[object Array]") {
456 | // Convert the property names array into a makeshift set.
457 | properties = {};
458 | for (index = 0, length = filter.length; index < length; value = filter[index++], ((getClass.call(value) == "[object String]" || getClass.call(value) == "[object Number]") && (properties[value] = 1)));
459 | }
460 | }
461 | if (width) {
462 | if (getClass.call(width) == "[object Number]") {
463 | // Convert the `width` to an integer and create a string containing
464 | // `width` number of space characters.
465 | if ((width -= width % 1) > 0) {
466 | for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
467 | }
468 | } else if (getClass.call(width) == "[object String]") {
469 | whitespace = width.length <= 10 ? width : width.slice(0, 10);
470 | }
471 | }
472 | // Opera <= 7.54u2 discards the values associated with empty string keys
473 | // (`""`) only if they are used directly within an object member list
474 | // (e.g., `!("" in { "": 1})`).
475 | return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
476 | };
477 | }
478 |
479 | // Public: Parses a JSON source string.
480 | if (!has("json-parse")) {
481 | fromCharCode = String.fromCharCode;
482 | // Internal: A map of escaped control characters and their unescaped
483 | // equivalents.
484 | Unescapes = {
485 | "\\": "\\",
486 | '"': '"',
487 | "/": "/",
488 | "b": "\b",
489 | "t": "\t",
490 | "n": "\n",
491 | "f": "\f",
492 | "r": "\r"
493 | };
494 |
495 | // Internal: Resets the parser state and throws a `SyntaxError`.
496 | abort = function() {
497 | Index = Source = null;
498 | throw SyntaxError();
499 | };
500 |
501 | // Internal: Returns the next token, or `"$"` if the parser has reached
502 | // the end of the source string. A token may be a string, number, `null`
503 | // literal, or Boolean literal.
504 | lex = function () {
505 | var source = Source, length = source.length, symbol, value, begin, position, sign;
506 | while (Index < length) {
507 | symbol = source.charAt(Index);
508 | if ("\t\r\n ".indexOf(symbol) > -1) {
509 | // Skip whitespace tokens, including tabs, carriage returns, line
510 | // feeds, and space characters.
511 | Index++;
512 | } else if ("{}[]:,".indexOf(symbol) > -1) {
513 | // Parse a punctuator token at the current position.
514 | Index++;
515 | return symbol;
516 | } else if (symbol == '"') {
517 | // Advance to the next character and parse a JSON string at the
518 | // current position. String tokens are prefixed with the sentinel
519 | // `@` character to distinguish them from punctuators.
520 | for (value = "@", Index++; Index < length;) {
521 | symbol = source.charAt(Index);
522 | if (symbol < " ") {
523 | // Unescaped ASCII control characters are not permitted.
524 | abort();
525 | } else if (symbol == "\\") {
526 | // Parse escaped JSON control characters, `"`, `\`, `/`, and
527 | // Unicode escape sequences.
528 | symbol = source.charAt(++Index);
529 | if ('\\"/btnfr'.indexOf(symbol) > -1) {
530 | // Revive escaped control characters.
531 | value += Unescapes[symbol];
532 | Index++;
533 | } else if (symbol == "u") {
534 | // Advance to the first character of the escape sequence.
535 | begin = ++Index;
536 | // Validate the Unicode escape sequence.
537 | for (position = Index + 4; Index < position; Index++) {
538 | symbol = source.charAt(Index);
539 | // A valid sequence comprises four hexdigits that form a
540 | // single hexadecimal value.
541 | if (!(symbol >= "0" && symbol <= "9" || symbol >= "a" && symbol <= "f" || symbol >= "A" && symbol <= "F")) {
542 | // Invalid Unicode escape sequence.
543 | abort();
544 | }
545 | }
546 | // Revive the escaped character.
547 | value += fromCharCode("0x" + source.slice(begin, Index));
548 | } else {
549 | // Invalid escape sequence.
550 | abort();
551 | }
552 | } else {
553 | if (symbol == '"') {
554 | // An unescaped double-quote character marks the end of the
555 | // string.
556 | break;
557 | }
558 | // Append the original character as-is.
559 | value += symbol;
560 | Index++;
561 | }
562 | }
563 | if (source.charAt(Index) == '"') {
564 | Index++;
565 | // Return the revived string.
566 | return value;
567 | }
568 | // Unterminated string.
569 | abort();
570 | } else {
571 | // Parse numbers and literals.
572 | begin = Index;
573 | // Advance the scanner's position past the sign, if one is
574 | // specified.
575 | if (symbol == "-") {
576 | sign = true;
577 | symbol = source.charAt(++Index);
578 | }
579 | // Parse an integer or floating-point value.
580 | if (symbol >= "0" && symbol <= "9") {
581 | // Leading zeroes are interpreted as octal literals.
582 | if (symbol == "0" && (symbol = source.charAt(Index + 1), symbol >= "0" && symbol <= "9")) {
583 | // Illegal octal literal.
584 | abort();
585 | }
586 | sign = false;
587 | // Parse the integer component.
588 | for (; Index < length && (symbol = source.charAt(Index), symbol >= "0" && symbol <= "9"); Index++);
589 | // Floats cannot contain a leading decimal point; however, this
590 | // case is already accounted for by the parser.
591 | if (source.charAt(Index) == ".") {
592 | position = ++Index;
593 | // Parse the decimal component.
594 | for (; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
595 | if (position == Index) {
596 | // Illegal trailing decimal.
597 | abort();
598 | }
599 | Index = position;
600 | }
601 | // Parse exponents.
602 | symbol = source.charAt(Index);
603 | if (symbol == "e" || symbol == "E") {
604 | // Skip past the sign following the exponent, if one is
605 | // specified.
606 | symbol = source.charAt(++Index);
607 | if (symbol == "+" || symbol == "-") {
608 | Index++;
609 | }
610 | // Parse the exponential component.
611 | for (position = Index; position < length && (symbol = source.charAt(position), symbol >= "0" && symbol <= "9"); position++);
612 | if (position == Index) {
613 | // Illegal empty exponent.
614 | abort();
615 | }
616 | Index = position;
617 | }
618 | // Coerce the parsed value to a JavaScript number.
619 | return +source.slice(begin, Index);
620 | }
621 | // A negative sign may only precede numbers.
622 | if (sign) {
623 | abort();
624 | }
625 | // `true`, `false`, and `null` literals.
626 | if (source.slice(Index, Index + 4) == "true") {
627 | Index += 4;
628 | return true;
629 | } else if (source.slice(Index, Index + 5) == "false") {
630 | Index += 5;
631 | return false;
632 | } else if (source.slice(Index, Index + 4) == "null") {
633 | Index += 4;
634 | return null;
635 | }
636 | // Unrecognized token.
637 | abort();
638 | }
639 | }
640 | // Return the sentinel `$` character if the parser has reached the end
641 | // of the source string.
642 | return "$";
643 | };
644 |
645 | // Internal: Parses a JSON `value` token.
646 | get = function (value) {
647 | var results, any, key;
648 | if (value == "$") {
649 | // Unexpected end of input.
650 | abort();
651 | }
652 | if (typeof value == "string") {
653 | if (value.charAt(0) == "@") {
654 | // Remove the sentinel `@` character.
655 | return value.slice(1);
656 | }
657 | // Parse object and array literals.
658 | if (value == "[") {
659 | // Parses a JSON array, returning a new JavaScript array.
660 | results = [];
661 | for (;; any || (any = true)) {
662 | value = lex();
663 | // A closing square bracket marks the end of the array literal.
664 | if (value == "]") {
665 | break;
666 | }
667 | // If the array literal contains elements, the current token
668 | // should be a comma separating the previous element from the
669 | // next.
670 | if (any) {
671 | if (value == ",") {
672 | value = lex();
673 | if (value == "]") {
674 | // Unexpected trailing `,` in array literal.
675 | abort();
676 | }
677 | } else {
678 | // A `,` must separate each array element.
679 | abort();
680 | }
681 | }
682 | // Elisions and leading commas are not permitted.
683 | if (value == ",") {
684 | abort();
685 | }
686 | results.push(get(value));
687 | }
688 | return results;
689 | } else if (value == "{") {
690 | // Parses a JSON object, returning a new JavaScript object.
691 | results = {};
692 | for (;; any || (any = true)) {
693 | value = lex();
694 | // A closing curly brace marks the end of the object literal.
695 | if (value == "}") {
696 | break;
697 | }
698 | // If the object literal contains members, the current token
699 | // should be a comma separator.
700 | if (any) {
701 | if (value == ",") {
702 | value = lex();
703 | if (value == "}") {
704 | // Unexpected trailing `,` in object literal.
705 | abort();
706 | }
707 | } else {
708 | // A `,` must separate each object member.
709 | abort();
710 | }
711 | }
712 | // Leading commas are not permitted, object property names must be
713 | // double-quoted strings, and a `:` must separate each property
714 | // name and value.
715 | if (value == "," || typeof value != "string" || value.charAt(0) != "@" || lex() != ":") {
716 | abort();
717 | }
718 | results[value.slice(1)] = get(lex());
719 | }
720 | return results;
721 | }
722 | // Unexpected token encountered.
723 | abort();
724 | }
725 | return value;
726 | };
727 |
728 | // Internal: Updates a traversed object member.
729 | update = function(source, property, callback) {
730 | var element = walk(source, property, callback);
731 | if (element === undef) {
732 | delete source[property];
733 | } else {
734 | source[property] = element;
735 | }
736 | };
737 |
738 | // Internal: Recursively traverses a parsed JSON object, invoking the
739 | // `callback` function for each value. This is an implementation of the
740 | // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
741 | walk = function (source, property, callback) {
742 | var value = source[property], length;
743 | if (typeof value == "object" && value) {
744 | if (getClass.call(value) == "[object Array]") {
745 | for (length = value.length; length--;) {
746 | update(value, length, callback);
747 | }
748 | } else {
749 | // `forEach` can't be used to traverse an array in Opera <= 8.54,
750 | // as `Object#hasOwnProperty` returns `false` for array indices
751 | // (e.g., `![1, 2, 3].hasOwnProperty("0")`).
752 | forEach(value, function (property) {
753 | update(value, property, callback);
754 | });
755 | }
756 | }
757 | return callback.call(source, property, value);
758 | };
759 |
760 | // Public: `JSON.parse`. See ES 5.1 section 15.12.2.
761 | JSON3.parse = function (source, callback) {
762 | var result, value;
763 | Index = 0;
764 | Source = source;
765 | result = get(lex());
766 | // If a JSON string contains multiple tokens, it is invalid.
767 | if (lex() != "$") {
768 | abort();
769 | }
770 | // Reset the parser state.
771 | Index = Source = null;
772 | return callback && getClass.call(callback) == "[object Function]" ? walk((value = {}, value[""] = result, value), "", callback) : result;
773 | };
774 | }
775 | }
776 |
777 | // Export for asynchronous module loaders.
778 | if (isLoader) {
779 | define(function () {
780 | return JSON3;
781 | });
782 | }
783 | }).call(this);
--------------------------------------------------------------------------------
/vendor/require.js:
--------------------------------------------------------------------------------
1 | /** vim: et:ts=4:sw=4:sts=4
2 | * @license RequireJS 0.25.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
3 | * Available via the MIT or new BSD license.
4 | * see: http://github.com/jrburke/requirejs for details
5 | */
6 | /*jslint strict: false, plusplus: false */
7 | /*global window: false, navigator: false, document: false, importScripts: false,
8 | jQuery: false, clearInterval: false, setInterval: false, self: false,
9 | setTimeout: false, opera: false */
10 |
11 | var requirejs, require, define;
12 | (function () {
13 | //Change this version number for each release.
14 | var version = "0.25.0",
15 | commentRegExp = /(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg,
16 | cjsRequireRegExp = /require\(["']([^'"\s]+)["']\)/g,
17 | currDirRegExp = /^\.\//,
18 | jsSuffixRegExp = /\.js$/,
19 | ostring = Object.prototype.toString,
20 | ap = Array.prototype,
21 | aps = ap.slice,
22 | apsp = ap.splice,
23 | isBrowser = !!(typeof window !== "undefined" && navigator && document),
24 | isWebWorker = !isBrowser && typeof importScripts !== "undefined",
25 | //PS3 indicates loaded and complete, but need to wait for complete
26 | //specifically. Sequence is "loading", "loaded", execution,
27 | // then "complete". The UA check is unfortunate, but not sure how
28 | //to feature test w/o causing perf issues.
29 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
30 | /^complete$/ : /^(complete|loaded)$/,
31 | defContextName = "_",
32 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
33 | isOpera = typeof opera !== "undefined" && opera.toString() === "[object Opera]",
34 | reqWaitIdPrefix = "_r@@",
35 | empty = {},
36 | contexts = {},
37 | globalDefQueue = [],
38 | interactiveScript = null,
39 | isDone = false,
40 | checkLoadedDepth = 0,
41 | useInteractive = false,
42 | req, cfg = {}, currentlyAddingScript, s, head, baseElement, scripts, script,
43 | src, subPath, mainScript, dataMain, i, scrollIntervalId, setReadyState, ctx,
44 | jQueryCheck, checkLoadedTimeoutId;
45 |
46 | function isFunction(it) {
47 | return ostring.call(it) === "[object Function]";
48 | }
49 |
50 | function isArray(it) {
51 | return ostring.call(it) === "[object Array]";
52 | }
53 |
54 | /**
55 | * Simple function to mix in properties from source into target,
56 | * but only if target does not already have a property of the same name.
57 | * This is not robust in IE for transferring methods that match
58 | * Object.prototype names, but the uses of mixin here seem unlikely to
59 | * trigger a problem related to that.
60 | */
61 | function mixin(target, source, force) {
62 | for (var prop in source) {
63 | if (!(prop in empty) && (!(prop in target) || force)) {
64 | target[prop] = source[prop];
65 | }
66 | }
67 | return req;
68 | }
69 |
70 | /**
71 | * Constructs an error with a pointer to an URL with more information.
72 | * @param {String} id the error ID that maps to an ID on a web page.
73 | * @param {String} message human readable error.
74 | * @param {Error} [err] the original error, if there is one.
75 | *
76 | * @returns {Error}
77 | */
78 | function makeError(id, msg, err) {
79 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
80 | if (err) {
81 | e.originalError = err;
82 | }
83 | return e;
84 | }
85 |
86 | /**
87 | * Used to set up package paths from a packagePaths or packages config object.
88 | * @param {Object} pkgs the object to store the new package config
89 | * @param {Array} currentPackages an array of packages to configure
90 | * @param {String} [dir] a prefix dir to use.
91 | */
92 | function configurePackageDir(pkgs, currentPackages, dir) {
93 | var i, location, pkgObj;
94 |
95 | for (i = 0; (pkgObj = currentPackages[i]); i++) {
96 | pkgObj = typeof pkgObj === "string" ? { name: pkgObj } : pkgObj;
97 | location = pkgObj.location;
98 |
99 | //Add dir to the path, but avoid paths that start with a slash
100 | //or have a colon (indicates a protocol)
101 | if (dir && (!location || (location.indexOf("/") !== 0 && location.indexOf(":") === -1))) {
102 | location = dir + "/" + (location || pkgObj.name);
103 | }
104 |
105 | //Create a brand new object on pkgs, since currentPackages can
106 | //be passed in again, and config.pkgs is the internal transformed
107 | //state for all package configs.
108 | pkgs[pkgObj.name] = {
109 | name: pkgObj.name,
110 | location: location || pkgObj.name,
111 | //Remove leading dot in main, so main paths are normalized,
112 | //and remove any trailing .js, since different package
113 | //envs have different conventions: some use a module name,
114 | //some use a file name.
115 | main: (pkgObj.main || "main")
116 | .replace(currDirRegExp, '')
117 | .replace(jsSuffixRegExp, '')
118 | };
119 | }
120 | }
121 |
122 | /**
123 | * jQuery 1.4.3-1.5.x use a readyWait/ready() pairing to hold DOM
124 | * ready callbacks, but jQuery 1.6 supports a holdReady() API instead.
125 | * At some point remove the readyWait/ready() support and just stick
126 | * with using holdReady.
127 | */
128 | function jQueryHoldReady($, shouldHold) {
129 | if ($.holdReady) {
130 | $.holdReady(shouldHold);
131 | } else if (shouldHold) {
132 | $.readyWait += 1;
133 | } else {
134 | $.ready(true);
135 | }
136 | }
137 |
138 | if (typeof define !== "undefined") {
139 | //If a define is already in play via another AMD loader,
140 | //do not overwrite.
141 | return;
142 | }
143 |
144 | if (typeof requirejs !== "undefined") {
145 | if (isFunction(requirejs)) {
146 | //Do not overwrite and existing requirejs instance.
147 | return;
148 | } else {
149 | cfg = requirejs;
150 | requirejs = undefined;
151 | }
152 | }
153 |
154 | //Allow for a require config object
155 | if (typeof require !== "undefined" && !isFunction(require)) {
156 | //assume it is a config object.
157 | cfg = require;
158 | require = undefined;
159 | }
160 |
161 | /**
162 | * Creates a new context for use in require and define calls.
163 | * Handle most of the heavy lifting. Do not want to use an object
164 | * with prototype here to avoid using "this" in require, in case it
165 | * needs to be used in more super secure envs that do not want this.
166 | * Also there should not be that many contexts in the page. Usually just
167 | * one for the default context, but could be extra for multiversion cases
168 | * or if a package needs a special context for a dependency that conflicts
169 | * with the standard context.
170 | */
171 | function newContext(contextName) {
172 | var context, resume,
173 | config = {
174 | waitSeconds: 7,
175 | baseUrl: s.baseUrl || "./",
176 | paths: {},
177 | pkgs: {}
178 | },
179 | defQueue = [],
180 | specified = {
181 | "require": true,
182 | "exports": true,
183 | "module": true
184 | },
185 | urlMap = {},
186 | defined = {},
187 | loaded = {},
188 | waiting = {},
189 | waitAry = [],
190 | waitIdCounter = 0,
191 | managerCallbacks = {},
192 | plugins = {},
193 | pluginsQueue = {},
194 | resumeDepth = 0,
195 | normalizedWaiting = {};
196 |
197 | /**
198 | * Trims the . and .. from an array of path segments.
199 | * It will keep a leading path segment if a .. will become
200 | * the first path segment, to help with module name lookups,
201 | * which act like paths, but can be remapped. But the end result,
202 | * all paths that use this function should look normalized.
203 | * NOTE: this method MODIFIES the input array.
204 | * @param {Array} ary the array of path segments.
205 | */
206 | function trimDots(ary) {
207 | var i, part;
208 | for (i = 0; (part = ary[i]); i++) {
209 | if (part === ".") {
210 | ary.splice(i, 1);
211 | i -= 1;
212 | } else if (part === "..") {
213 | if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
214 | //End of the line. Keep at least one non-dot
215 | //path segment at the front so it can be mapped
216 | //correctly to disk. Otherwise, there is likely
217 | //no path mapping for a path starting with '..'.
218 | //This can still fail, but catches the most reasonable
219 | //uses of ..
220 | break;
221 | } else if (i > 0) {
222 | ary.splice(i - 1, 2);
223 | i -= 2;
224 | }
225 | }
226 | }
227 | }
228 |
229 | /**
230 | * Given a relative module name, like ./something, normalize it to
231 | * a real name that can be mapped to a path.
232 | * @param {String} name the relative name
233 | * @param {String} baseName a real name that the name arg is relative
234 | * to.
235 | * @returns {String} normalized name
236 | */
237 | function normalize(name, baseName) {
238 | var pkgName, pkgConfig;
239 |
240 | //Adjust any relative paths.
241 | if (name.charAt(0) === ".") {
242 | //If have a base name, try to normalize against it,
243 | //otherwise, assume it is a top-level require that will
244 | //be relative to baseUrl in the end.
245 | if (baseName) {
246 | if (config.pkgs[baseName]) {
247 | //If the baseName is a package name, then just treat it as one
248 | //name to concat the name with.
249 | baseName = [baseName];
250 | } else {
251 | //Convert baseName to array, and lop off the last part,
252 | //so that . matches that "directory" and not name of the baseName's
253 | //module. For instance, baseName of "one/two/three", maps to
254 | //"one/two/three.js", but we want the directory, "one/two" for
255 | //this normalization.
256 | baseName = baseName.split("/");
257 | baseName = baseName.slice(0, baseName.length - 1);
258 | }
259 |
260 | name = baseName.concat(name.split("/"));
261 | trimDots(name);
262 |
263 | //Some use of packages may use a . path to reference the
264 | //"main" module name, so normalize for that.
265 | pkgConfig = config.pkgs[(pkgName = name[0])];
266 | name = name.join("/");
267 | if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
268 | name = pkgName;
269 | }
270 | }
271 | }
272 | return name;
273 | }
274 |
275 | /**
276 | * Creates a module mapping that includes plugin prefix, module
277 | * name, and path. If parentModuleMap is provided it will
278 | * also normalize the name via require.normalize()
279 | *
280 | * @param {String} name the module name
281 | * @param {String} [parentModuleMap] parent module map
282 | * for the module name, used to resolve relative names.
283 | *
284 | * @returns {Object}
285 | */
286 | function makeModuleMap(name, parentModuleMap) {
287 | var index = name ? name.indexOf("!") : -1,
288 | prefix = null,
289 | parentName = parentModuleMap ? parentModuleMap.name : null,
290 | originalName = name,
291 | normalizedName, url, pluginModule;
292 |
293 | if (index !== -1) {
294 | prefix = name.substring(0, index);
295 | name = name.substring(index + 1, name.length);
296 | }
297 |
298 | if (prefix) {
299 | prefix = normalize(prefix, parentName);
300 | }
301 |
302 | //Account for relative paths if there is a base name.
303 | if (name) {
304 | if (prefix) {
305 | pluginModule = defined[prefix];
306 | if (pluginModule) {
307 | //Plugin is loaded, use its normalize method, otherwise,
308 | //normalize name as usual.
309 | if (pluginModule.normalize) {
310 | normalizedName = pluginModule.normalize(name, function (name) {
311 | return normalize(name, parentName);
312 | });
313 | } else {
314 | normalizedName = normalize(name, parentName);
315 | }
316 | } else {
317 | //Plugin is not loaded yet, so do not normalize
318 | //the name, wait for plugin to load to see if
319 | //it has a normalize method. To avoid possible
320 | //ambiguity with relative names loaded from another
321 | //plugin, use the parent's name as part of this name.
322 | normalizedName = '__$p' + parentName + '@' + name;
323 | }
324 | } else {
325 | normalizedName = normalize(name, parentName);
326 | }
327 |
328 | url = urlMap[normalizedName];
329 | if (!url) {
330 | //Calculate url for the module, if it has a name.
331 | if (req.toModuleUrl) {
332 | //Special logic required for a particular engine,
333 | //like Node.
334 | url = req.toModuleUrl(context, normalizedName, parentModuleMap);
335 | } else {
336 | url = context.nameToUrl(normalizedName, null, parentModuleMap);
337 | }
338 |
339 | //Store the URL mapping for later.
340 | urlMap[normalizedName] = url;
341 | }
342 | }
343 |
344 | return {
345 | prefix: prefix,
346 | name: normalizedName,
347 | parentMap: parentModuleMap,
348 | url: url,
349 | originalName: originalName,
350 | fullName: prefix ? prefix + "!" + normalizedName : normalizedName
351 | };
352 | }
353 |
354 | /**
355 | * Determine if priority loading is done. If so clear the priorityWait
356 | */
357 | function isPriorityDone() {
358 | var priorityDone = true,
359 | priorityWait = config.priorityWait,
360 | priorityName, i;
361 | if (priorityWait) {
362 | for (i = 0; (priorityName = priorityWait[i]); i++) {
363 | if (!loaded[priorityName]) {
364 | priorityDone = false;
365 | break;
366 | }
367 | }
368 | if (priorityDone) {
369 | delete config.priorityWait;
370 | }
371 | }
372 | return priorityDone;
373 | }
374 |
375 | /**
376 | * Helper function that creates a setExports function for a "module"
377 | * CommonJS dependency. Do this here to avoid creating a closure that
378 | * is part of a loop.
379 | */
380 | function makeSetExports(moduleObj) {
381 | return function (exports) {
382 | moduleObj.exports = exports;
383 | };
384 | }
385 |
386 | function makeContextModuleFunc(func, relModuleMap, enableBuildCallback) {
387 | return function () {
388 | //A version of a require function that passes a moduleName
389 | //value for items that may need to
390 | //look up paths relative to the moduleName
391 | var args = [].concat(aps.call(arguments, 0)), lastArg;
392 | if (enableBuildCallback &&
393 | isFunction((lastArg = args[args.length - 1]))) {
394 | lastArg.__requireJsBuild = true;
395 | }
396 | args.push(relModuleMap);
397 | return func.apply(null, args);
398 | };
399 | }
400 |
401 | /**
402 | * Helper function that creates a require function object to give to
403 | * modules that ask for it as a dependency. It needs to be specific
404 | * per module because of the implication of path mappings that may
405 | * need to be relative to the module name.
406 | */
407 | function makeRequire(relModuleMap, enableBuildCallback) {
408 | var modRequire = makeContextModuleFunc(context.require, relModuleMap, enableBuildCallback);
409 |
410 | mixin(modRequire, {
411 | nameToUrl: makeContextModuleFunc(context.nameToUrl, relModuleMap),
412 | toUrl: makeContextModuleFunc(context.toUrl, relModuleMap),
413 | defined: makeContextModuleFunc(context.requireDefined, relModuleMap),
414 | specified: makeContextModuleFunc(context.requireSpecified, relModuleMap),
415 | ready: req.ready,
416 | isBrowser: req.isBrowser
417 | });
418 | //Something used by node.
419 | if (req.paths) {
420 | modRequire.paths = req.paths;
421 | }
422 | return modRequire;
423 | }
424 |
425 | /**
426 | * Used to update the normalized name for plugin-based dependencies
427 | * after a plugin loads, since it can have its own normalization structure.
428 | * @param {String} pluginName the normalized plugin module name.
429 | */
430 | function updateNormalizedNames(pluginName) {
431 |
432 | var oldFullName, oldModuleMap, moduleMap, fullName, callbacks,
433 | i, j, k, depArray, existingCallbacks,
434 | maps = normalizedWaiting[pluginName];
435 |
436 | if (maps) {
437 | for (i = 0; (oldModuleMap = maps[i]); i++) {
438 | oldFullName = oldModuleMap.fullName;
439 | moduleMap = makeModuleMap(oldModuleMap.originalName, oldModuleMap.parentMap);
440 | fullName = moduleMap.fullName;
441 | //Callbacks could be undefined if the same plugin!name was
442 | //required twice in a row, so use empty array in that case.
443 | callbacks = managerCallbacks[oldFullName] || [];
444 | existingCallbacks = managerCallbacks[fullName];
445 |
446 | if (fullName !== oldFullName) {
447 | //Update the specified object, but only if it is already
448 | //in there. In sync environments, it may not be yet.
449 | if (oldFullName in specified) {
450 | delete specified[oldFullName];
451 | specified[fullName] = true;
452 | }
453 |
454 | //Update managerCallbacks to use the correct normalized name.
455 | //If there are already callbacks for the normalized name,
456 | //just add to them.
457 | if (existingCallbacks) {
458 | managerCallbacks[fullName] = existingCallbacks.concat(callbacks);
459 | } else {
460 | managerCallbacks[fullName] = callbacks;
461 | }
462 | delete managerCallbacks[oldFullName];
463 |
464 | //In each manager callback, update the normalized name in the depArray.
465 | for (j = 0; j < callbacks.length; j++) {
466 | depArray = callbacks[j].depArray;
467 | for (k = 0; k < depArray.length; k++) {
468 | if (depArray[k] === oldFullName) {
469 | depArray[k] = fullName;
470 | }
471 | }
472 | }
473 | }
474 | }
475 | }
476 |
477 | delete normalizedWaiting[pluginName];
478 | }
479 |
480 | /*
481 | * Queues a dependency for checking after the loader is out of a
482 | * "paused" state, for example while a script file is being loaded
483 | * in the browser, where it may have many modules defined in it.
484 | *
485 | * depName will be fully qualified, no relative . or .. path.
486 | */
487 | function queueDependency(dep) {
488 | //Make sure to load any plugin and associate the dependency
489 | //with that plugin.
490 | var prefix = dep.prefix,
491 | fullName = dep.fullName;
492 |
493 | //Do not bother if the depName is already in transit
494 | if (specified[fullName] || fullName in defined) {
495 | return;
496 | }
497 |
498 | if (prefix && !plugins[prefix]) {
499 | //Queue up loading of the dependency, track it
500 | //via context.plugins. Mark it as a plugin so
501 | //that the build system will know to treat it
502 | //special.
503 | plugins[prefix] = undefined;
504 |
505 | //Remember this dep that needs to have normaliztion done
506 | //after the plugin loads.
507 | (normalizedWaiting[prefix] || (normalizedWaiting[prefix] = []))
508 | .push(dep);
509 |
510 | //Register an action to do once the plugin loads, to update
511 | //all managerCallbacks to use a properly normalized module
512 | //name.
513 | (managerCallbacks[prefix] ||
514 | (managerCallbacks[prefix] = [])).push({
515 | onDep: function (name, value) {
516 | if (name === prefix) {
517 | updateNormalizedNames(prefix);
518 | }
519 | }
520 | });
521 |
522 | queueDependency(makeModuleMap(prefix));
523 | }
524 |
525 | context.paused.push(dep);
526 | }
527 |
528 | function execManager(manager) {
529 | var i, ret, waitingCallbacks, err,
530 | cb = manager.callback,
531 | fullName = manager.fullName,
532 | args = [],
533 | ary = manager.depArray;
534 |
535 | //Call the callback to define the module, if necessary.
536 | if (cb && isFunction(cb)) {
537 | //Pull out the defined dependencies and pass the ordered
538 | //values to the callback.
539 | if (ary) {
540 | for (i = 0; i < ary.length; i++) {
541 | args.push(manager.deps[ary[i]]);
542 | }
543 | }
544 |
545 | try {
546 | ret = req.execCb(fullName, manager.callback, args, defined[fullName]);
547 | } catch (e) {
548 | err = e;
549 | }
550 |
551 | if (fullName) {
552 | //If setting exports via "module" is in play,
553 | //favor that over return value and exports. After that,
554 | //favor a non-undefined return value over exports use.
555 | if (manager.cjsModule && manager.cjsModule.exports !== undefined) {
556 | ret = defined[fullName] = manager.cjsModule.exports;
557 | } else if (ret === undefined && manager.usingExports) {
558 | //exports already set the defined value.
559 | ret = defined[fullName];
560 | } else {
561 | //Use the return value from the function.
562 | defined[fullName] = ret;
563 | }
564 | }
565 | } else if (fullName) {
566 | //May just be an object definition for the module. Only
567 | //worry about defining if have a module name.
568 | ret = defined[fullName] = cb;
569 | }
570 |
571 | //Clean up waiting. Do this before error calls, and before
572 | //calling back waitingCallbacks, so that bookkeeping is correct
573 | //in the event of an error and error is reported in correct order,
574 | //since the waitingCallbacks will likely have errors if the
575 | //onError function does not throw.
576 | if (waiting[manager.waitId]) {
577 | delete waiting[manager.waitId];
578 | manager.isDone = true;
579 | context.waitCount -= 1;
580 | if (context.waitCount === 0) {
581 | //Clear the wait array used for cycles.
582 | waitAry = [];
583 | }
584 | }
585 |
586 | if (err) {
587 | err = makeError('defineerror', 'Error evaluating ' +
588 | 'module "' + fullName + '" at location "' +
589 | (fullName ? makeModuleMap(fullName).url : '') + '":\n' +
590 | err + '\nfileName:' + (err.fileName || err.sourceURL) +
591 | '\nlineNumber: ' + (err.lineNumber || err.line), err);
592 | err.moduleName = fullName;
593 | return req.onError(err);
594 | }
595 |
596 | if (fullName) {
597 | //If anything was waiting for this module to be defined,
598 | //notify them now.
599 | waitingCallbacks = managerCallbacks[fullName];
600 | if (waitingCallbacks) {
601 | for (i = 0; i < waitingCallbacks.length; i++) {
602 | waitingCallbacks[i].onDep(fullName, ret);
603 | }
604 | delete managerCallbacks[fullName];
605 | }
606 | }
607 |
608 | return undefined;
609 | }
610 |
611 | function main(inName, depArray, callback, relModuleMap) {
612 | var moduleMap = makeModuleMap(inName, relModuleMap),
613 | name = moduleMap.name,
614 | fullName = moduleMap.fullName,
615 | uniques = {},
616 | manager = {
617 | //Use a wait ID because some entries are anon
618 | //async require calls.
619 | waitId: name || reqWaitIdPrefix + (waitIdCounter++),
620 | depCount: 0,
621 | depMax: 0,
622 | prefix: moduleMap.prefix,
623 | name: name,
624 | fullName: fullName,
625 | deps: {},
626 | depArray: depArray,
627 | callback: callback,
628 | onDep: function (depName, value) {
629 | if (!(depName in manager.deps)) {
630 | manager.deps[depName] = value;
631 | manager.depCount += 1;
632 | if (manager.depCount === manager.depMax) {
633 | //All done, execute!
634 | execManager(manager);
635 | }
636 | }
637 | }
638 | },
639 | i, depArg, depName, cjsMod;
640 |
641 | if (fullName) {
642 | //If module already defined for context, or already loaded,
643 | //then leave. Also leave if jQuery is registering but it does
644 | //not match the desired version number in the config.
645 | if (fullName in defined || loaded[fullName] === true ||
646 | (fullName === "jquery" && config.jQuery &&
647 | config.jQuery !== callback().fn.jquery)) {
648 | return;
649 | }
650 |
651 | //Set specified/loaded here for modules that are also loaded
652 | //as part of a layer, where onScriptLoad is not fired
653 | //for those cases. Do this after the inline define and
654 | //dependency tracing is done.
655 | specified[fullName] = true;
656 | loaded[fullName] = true;
657 |
658 | //If module is jQuery set up delaying its dom ready listeners.
659 | if (fullName === "jquery" && callback) {
660 | jQueryCheck(callback());
661 | }
662 | }
663 |
664 | //Add the dependencies to the deps field, and register for callbacks
665 | //on the dependencies.
666 | for (i = 0; i < depArray.length; i++) {
667 | depArg = depArray[i];
668 | //There could be cases like in IE, where a trailing comma will
669 | //introduce a null dependency, so only treat a real dependency
670 | //value as a dependency.
671 | if (depArg) {
672 | //Split the dependency name into plugin and name parts
673 | depArg = makeModuleMap(depArg, (name ? moduleMap : relModuleMap));
674 | depName = depArg.fullName;
675 |
676 | //Fix the name in depArray to be just the name, since
677 | //that is how it will be called back later.
678 | depArray[i] = depName;
679 |
680 | //Fast path CommonJS standard dependencies.
681 | if (depName === "require") {
682 | manager.deps[depName] = makeRequire(moduleMap);
683 | } else if (depName === "exports") {
684 | //CommonJS module spec 1.1
685 | manager.deps[depName] = defined[fullName] = {};
686 | manager.usingExports = true;
687 | } else if (depName === "module") {
688 | //CommonJS module spec 1.1
689 | manager.cjsModule = cjsMod = manager.deps[depName] = {
690 | id: name,
691 | uri: name ? context.nameToUrl(name, null, relModuleMap) : undefined,
692 | exports: defined[fullName]
693 | };
694 | cjsMod.setExports = makeSetExports(cjsMod);
695 | } else if (depName in defined && !(depName in waiting)) {
696 | //Module already defined, no need to wait for it.
697 | manager.deps[depName] = defined[depName];
698 | } else if (!uniques[depName]) {
699 |
700 | //A dynamic dependency.
701 | manager.depMax += 1;
702 |
703 | queueDependency(depArg);
704 |
705 | //Register to get notification when dependency loads.
706 | (managerCallbacks[depName] ||
707 | (managerCallbacks[depName] = [])).push(manager);
708 |
709 | uniques[depName] = true;
710 | }
711 | }
712 | }
713 |
714 | //Do not bother tracking the manager if it is all done.
715 | if (manager.depCount === manager.depMax) {
716 | //All done, execute!
717 | execManager(manager);
718 | } else {
719 | waiting[manager.waitId] = manager;
720 | waitAry.push(manager);
721 | context.waitCount += 1;
722 | }
723 | }
724 |
725 | /**
726 | * Convenience method to call main for a define call that was put on
727 | * hold in the defQueue.
728 | */
729 | function callDefMain(args) {
730 | main.apply(null, args);
731 | //Mark the module loaded. Must do it here in addition
732 | //to doing it in define in case a script does
733 | //not call define
734 | loaded[args[0]] = true;
735 | }
736 |
737 | /**
738 | * jQuery 1.4.3+ supports ways to hold off calling
739 | * calling jQuery ready callbacks until all scripts are loaded. Be sure
740 | * to track it if the capability exists.. Also, since jQuery 1.4.3 does
741 | * not register as a module, need to do some global inference checking.
742 | * Even if it does register as a module, not guaranteed to be the precise
743 | * name of the global. If a jQuery is tracked for this context, then go
744 | * ahead and register it as a module too, if not already in process.
745 | */
746 | jQueryCheck = function (jqCandidate) {
747 | if (!context.jQuery) {
748 | var $ = jqCandidate || (typeof jQuery !== "undefined" ? jQuery : null);
749 |
750 | if ($) {
751 | //If a specific version of jQuery is wanted, make sure to only
752 | //use this jQuery if it matches.
753 | if (config.jQuery && $.fn.jquery !== config.jQuery) {
754 | return;
755 | }
756 |
757 | if ("holdReady" in $ || "readyWait" in $) {
758 | context.jQuery = $;
759 |
760 | //Manually create a "jquery" module entry if not one already
761 | //or in process. Note this could trigger an attempt at
762 | //a second jQuery registration, but does no harm since
763 | //the first one wins, and it is the same value anyway.
764 | callDefMain(["jquery", [], function () {
765 | return jQuery;
766 | }]);
767 |
768 | //Ask jQuery to hold DOM ready callbacks.
769 | if (context.scriptCount) {
770 | jQueryHoldReady($, true);
771 | context.jQueryIncremented = true;
772 | }
773 | }
774 | }
775 | }
776 | };
777 |
778 | function forceExec(manager, traced) {
779 | if (manager.isDone) {
780 | return undefined;
781 | }
782 |
783 | var fullName = manager.fullName,
784 | depArray = manager.depArray,
785 | depName, i;
786 | if (fullName) {
787 | if (traced[fullName]) {
788 | return defined[fullName];
789 | }
790 |
791 | traced[fullName] = true;
792 | }
793 |
794 | //forceExec all of its dependencies.
795 | for (i = 0; i < depArray.length; i++) {
796 | //Some array members may be null, like if a trailing comma
797 | //IE, so do the explicit [i] access and check if it has a value.
798 | depName = depArray[i];
799 | if (depName) {
800 | if (!manager.deps[depName] && waiting[depName]) {
801 | manager.onDep(depName, forceExec(waiting[depName], traced));
802 | }
803 | }
804 | }
805 |
806 | return fullName ? defined[fullName] : undefined;
807 | }
808 |
809 | /**
810 | * Checks if all modules for a context are loaded, and if so, evaluates the
811 | * new ones in right dependency order.
812 | *
813 | * @private
814 | */
815 | function checkLoaded() {
816 | var waitInterval = config.waitSeconds * 1000,
817 | //It is possible to disable the wait interval by using waitSeconds of 0.
818 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
819 | noLoads = "", hasLoadedProp = false, stillLoading = false, prop,
820 | err, manager;
821 |
822 | //If there are items still in the paused queue processing wait.
823 | //This is particularly important in the sync case where each paused
824 | //item is processed right away but there may be more waiting.
825 | if (context.pausedCount > 0) {
826 | return undefined;
827 | }
828 |
829 | //Determine if priority loading is done. If so clear the priority. If
830 | //not, then do not check
831 | if (config.priorityWait) {
832 | if (isPriorityDone()) {
833 | //Call resume, since it could have
834 | //some waiting dependencies to trace.
835 | resume();
836 | } else {
837 | return undefined;
838 | }
839 | }
840 |
841 | //See if anything is still in flight.
842 | for (prop in loaded) {
843 | if (!(prop in empty)) {
844 | hasLoadedProp = true;
845 | if (!loaded[prop]) {
846 | if (expired) {
847 | noLoads += prop + " ";
848 | } else {
849 | stillLoading = true;
850 | break;
851 | }
852 | }
853 | }
854 | }
855 |
856 | //Check for exit conditions.
857 | if (!hasLoadedProp && !context.waitCount) {
858 | //If the loaded object had no items, then the rest of
859 | //the work below does not need to be done.
860 | return undefined;
861 | }
862 | if (expired && noLoads) {
863 | //If wait time expired, throw error of unloaded modules.
864 | err = makeError("timeout", "Load timeout for modules: " + noLoads);
865 | err.requireType = "timeout";
866 | err.requireModules = noLoads;
867 | return req.onError(err);
868 | }
869 | if (stillLoading || context.scriptCount) {
870 | //Something is still waiting to load. Wait for it, but only
871 | //if a timeout is not already in effect.
872 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
873 | checkLoadedTimeoutId = setTimeout(function () {
874 | checkLoadedTimeoutId = 0;
875 | checkLoaded();
876 | }, 50);
877 | }
878 | return undefined;
879 | }
880 |
881 | //If still have items in the waiting cue, but all modules have
882 | //been loaded, then it means there are some circular dependencies
883 | //that need to be broken.
884 | //However, as a waiting thing is fired, then it can add items to
885 | //the waiting cue, and those items should not be fired yet, so
886 | //make sure to redo the checkLoaded call after breaking a single
887 | //cycle, if nothing else loaded then this logic will pick it up
888 | //again.
889 | if (context.waitCount) {
890 | //Cycle through the waitAry, and call items in sequence.
891 | for (i = 0; (manager = waitAry[i]); i++) {
892 | forceExec(manager, {});
893 | }
894 |
895 | //Only allow this recursion to a certain depth. Only
896 | //triggered by errors in calling a module in which its
897 | //modules waiting on it cannot finish loading, or some circular
898 | //dependencies that then may add more dependencies.
899 | //The value of 5 is a bit arbitrary. Hopefully just one extra
900 | //pass, or two for the case of circular dependencies generating
901 | //more work that gets resolved in the sync node case.
902 | if (checkLoadedDepth < 5) {
903 | checkLoadedDepth += 1;
904 | checkLoaded();
905 | }
906 | }
907 |
908 | checkLoadedDepth = 0;
909 |
910 | //Check for DOM ready, and nothing is waiting across contexts.
911 | req.checkReadyState();
912 |
913 | return undefined;
914 | }
915 |
916 | function callPlugin(pluginName, dep) {
917 | var name = dep.name,
918 | fullName = dep.fullName,
919 | load;
920 |
921 | //Do not bother if plugin is already defined or being loaded.
922 | if (fullName in defined || fullName in loaded) {
923 | return;
924 | }
925 |
926 | if (!plugins[pluginName]) {
927 | plugins[pluginName] = defined[pluginName];
928 | }
929 |
930 | //Only set loaded to false for tracking if it has not already been set.
931 | if (!loaded[fullName]) {
932 | loaded[fullName] = false;
933 | }
934 |
935 | load = function (ret) {
936 | //Allow the build process to register plugin-loaded dependencies.
937 | if (req.onPluginLoad) {
938 | req.onPluginLoad(context, pluginName, name, ret);
939 | }
940 |
941 | execManager({
942 | prefix: dep.prefix,
943 | name: dep.name,
944 | fullName: dep.fullName,
945 | callback: function () {
946 | return ret;
947 | }
948 | });
949 | loaded[fullName] = true;
950 | };
951 |
952 | //Allow plugins to load other code without having to know the
953 | //context or how to "complete" the load.
954 | load.fromText = function (moduleName, text) {
955 | /*jslint evil: true */
956 | var hasInteractive = useInteractive;
957 |
958 | //Indicate a the module is in process of loading.
959 | context.loaded[moduleName] = false;
960 | context.scriptCount += 1;
961 |
962 | //Turn off interactive script matching for IE for any define
963 | //calls in the text, then turn it back on at the end.
964 | if (hasInteractive) {
965 | useInteractive = false;
966 | }
967 |
968 | req.exec(text);
969 |
970 | if (hasInteractive) {
971 | useInteractive = true;
972 | }
973 |
974 | //Support anonymous modules.
975 | context.completeLoad(moduleName);
976 | };
977 |
978 | //Use parentName here since the plugin's name is not reliable,
979 | //could be some weird string with no path that actually wants to
980 | //reference the parentName's path.
981 | plugins[pluginName].load(name, makeRequire(dep.parentMap, true), load, config);
982 | }
983 |
984 | function loadPaused(dep) {
985 | //Renormalize dependency if its name was waiting on a plugin
986 | //to load, which as since loaded.
987 | if (dep.prefix && dep.name.indexOf('__$p') === 0 && defined[dep.prefix]) {
988 | dep = makeModuleMap(dep.originalName, dep.parentMap);
989 | }
990 |
991 | var pluginName = dep.prefix,
992 | fullName = dep.fullName,
993 | urlFetched = context.urlFetched;
994 |
995 | //Do not bother if the dependency has already been specified.
996 | if (specified[fullName] || loaded[fullName]) {
997 | return;
998 | } else {
999 | specified[fullName] = true;
1000 | }
1001 |
1002 | if (pluginName) {
1003 | //If plugin not loaded, wait for it.
1004 | //set up callback list. if no list, then register
1005 | //managerCallback for that plugin.
1006 | if (defined[pluginName]) {
1007 | callPlugin(pluginName, dep);
1008 | } else {
1009 | if (!pluginsQueue[pluginName]) {
1010 | pluginsQueue[pluginName] = [];
1011 | (managerCallbacks[pluginName] ||
1012 | (managerCallbacks[pluginName] = [])).push({
1013 | onDep: function (name, value) {
1014 | if (name === pluginName) {
1015 | var i, oldModuleMap, ary = pluginsQueue[pluginName];
1016 |
1017 | //Now update all queued plugin actions.
1018 | for (i = 0; i < ary.length; i++) {
1019 | oldModuleMap = ary[i];
1020 | //Update the moduleMap since the
1021 | //module name may be normalized
1022 | //differently now.
1023 | callPlugin(pluginName,
1024 | makeModuleMap(oldModuleMap.originalName, oldModuleMap.parentMap));
1025 | }
1026 | delete pluginsQueue[pluginName];
1027 | }
1028 | }
1029 | });
1030 | }
1031 | pluginsQueue[pluginName].push(dep);
1032 | }
1033 | } else {
1034 | if (!urlFetched[dep.url]) {
1035 | req.load(context, fullName, dep.url);
1036 | urlFetched[dep.url] = true;
1037 | }
1038 | }
1039 | }
1040 |
1041 | /**
1042 | * Resumes tracing of dependencies and then checks if everything is loaded.
1043 | */
1044 | resume = function () {
1045 | var args, i, p;
1046 |
1047 | resumeDepth += 1;
1048 |
1049 | if (context.scriptCount <= 0) {
1050 | //Synchronous envs will push the number below zero with the
1051 | //decrement above, be sure to set it back to zero for good measure.
1052 | //require() calls that also do not end up loading scripts could
1053 | //push the number negative too.
1054 | context.scriptCount = 0;
1055 | }
1056 |
1057 | //Make sure any remaining defQueue items get properly processed.
1058 | while (defQueue.length) {
1059 | args = defQueue.shift();
1060 | if (args[0] === null) {
1061 | return req.onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
1062 | } else {
1063 | callDefMain(args);
1064 | }
1065 | }
1066 |
1067 | //Skip the resume of paused dependencies
1068 | //if current context is in priority wait.
1069 | if (!config.priorityWait || isPriorityDone()) {
1070 | while (context.paused.length) {
1071 | p = context.paused;
1072 | context.pausedCount += p.length;
1073 | //Reset paused list
1074 | context.paused = [];
1075 |
1076 | for (i = 0; (args = p[i]); i++) {
1077 | loadPaused(args);
1078 | }
1079 | //Move the start time for timeout forward.
1080 | context.startTime = (new Date()).getTime();
1081 | context.pausedCount -= p.length;
1082 | }
1083 | }
1084 |
1085 | //Only check if loaded when resume depth is 1. It is likely that
1086 | //it is only greater than 1 in sync environments where a factory
1087 | //function also then calls the callback-style require. In those
1088 | //cases, the checkLoaded should not occur until the resume
1089 | //depth is back at the top level.
1090 | if (resumeDepth === 1) {
1091 | checkLoaded();
1092 | }
1093 |
1094 | resumeDepth -= 1;
1095 |
1096 | return undefined;
1097 | };
1098 |
1099 | //Define the context object. Many of these fields are on here
1100 | //just to make debugging easier.
1101 | context = {
1102 | contextName: contextName,
1103 | config: config,
1104 | defQueue: defQueue,
1105 | waiting: waiting,
1106 | waitCount: 0,
1107 | specified: specified,
1108 | loaded: loaded,
1109 | urlMap: urlMap,
1110 | scriptCount: 0,
1111 | urlFetched: {},
1112 | defined: defined,
1113 | paused: [],
1114 | pausedCount: 0,
1115 | plugins: plugins,
1116 | managerCallbacks: managerCallbacks,
1117 | makeModuleMap: makeModuleMap,
1118 | normalize: normalize,
1119 | /**
1120 | * Set a configuration for the context.
1121 | * @param {Object} cfg config object to integrate.
1122 | */
1123 | configure: function (cfg) {
1124 | var paths, prop, packages, pkgs, packagePaths, requireWait;
1125 |
1126 | //Make sure the baseUrl ends in a slash.
1127 | if (cfg.baseUrl) {
1128 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== "/") {
1129 | cfg.baseUrl += "/";
1130 | }
1131 | }
1132 |
1133 | //Save off the paths and packages since they require special processing,
1134 | //they are additive.
1135 | paths = config.paths;
1136 | packages = config.packages;
1137 | pkgs = config.pkgs;
1138 |
1139 | //Mix in the config values, favoring the new values over
1140 | //existing ones in context.config.
1141 | mixin(config, cfg, true);
1142 |
1143 | //Adjust paths if necessary.
1144 | if (cfg.paths) {
1145 | for (prop in cfg.paths) {
1146 | if (!(prop in empty)) {
1147 | paths[prop] = cfg.paths[prop];
1148 | }
1149 | }
1150 | config.paths = paths;
1151 | }
1152 |
1153 | packagePaths = cfg.packagePaths;
1154 | if (packagePaths || cfg.packages) {
1155 | //Convert packagePaths into a packages config.
1156 | if (packagePaths) {
1157 | for (prop in packagePaths) {
1158 | if (!(prop in empty)) {
1159 | configurePackageDir(pkgs, packagePaths[prop], prop);
1160 | }
1161 | }
1162 | }
1163 |
1164 | //Adjust packages if necessary.
1165 | if (cfg.packages) {
1166 | configurePackageDir(pkgs, cfg.packages);
1167 | }
1168 |
1169 | //Done with modifications, assing packages back to context config
1170 | config.pkgs = pkgs;
1171 | }
1172 |
1173 | //If priority loading is in effect, trigger the loads now
1174 | if (cfg.priority) {
1175 | //Hold on to requireWait value, and reset it after done
1176 | requireWait = context.requireWait;
1177 |
1178 | //Allow tracing some require calls to allow the fetching
1179 | //of the priority config.
1180 | context.requireWait = false;
1181 |
1182 | //But first, call resume to register any defined modules that may
1183 | //be in a data-main built file before the priority config
1184 | //call. Also grab any waiting define calls for this context.
1185 | context.takeGlobalQueue();
1186 | resume();
1187 |
1188 | context.require(cfg.priority);
1189 |
1190 | //Trigger a resume right away, for the case when
1191 | //the script with the priority load is done as part
1192 | //of a data-main call. In that case the normal resume
1193 | //call will not happen because the scriptCount will be
1194 | //at 1, since the script for data-main is being processed.
1195 | resume();
1196 |
1197 | //Restore previous state.
1198 | context.requireWait = requireWait;
1199 | config.priorityWait = cfg.priority;
1200 | }
1201 |
1202 | //If a deps array or a config callback is specified, then call
1203 | //require with those args. This is useful when require is defined as a
1204 | //config object before require.js is loaded.
1205 | if (cfg.deps || cfg.callback) {
1206 | context.require(cfg.deps || [], cfg.callback);
1207 | }
1208 |
1209 | //Set up ready callback, if asked. Useful when require is defined as a
1210 | //config object before require.js is loaded.
1211 | if (cfg.ready) {
1212 | req.ready(cfg.ready);
1213 | }
1214 | },
1215 |
1216 | requireDefined: function (moduleName, relModuleMap) {
1217 | return makeModuleMap(moduleName, relModuleMap).fullName in defined;
1218 | },
1219 |
1220 | requireSpecified: function (moduleName, relModuleMap) {
1221 | return makeModuleMap(moduleName, relModuleMap).fullName in specified;
1222 | },
1223 |
1224 | require: function (deps, callback, relModuleMap) {
1225 | var moduleName, fullName, moduleMap;
1226 | if (typeof deps === "string") {
1227 | //Synchronous access to one module. If require.get is
1228 | //available (as in the Node adapter), prefer that.
1229 | //In this case deps is the moduleName and callback is
1230 | //the relModuleMap
1231 | if (req.get) {
1232 | return req.get(context, deps, callback);
1233 | }
1234 |
1235 | //Just return the module wanted. In this scenario, the
1236 | //second arg (if passed) is just the relModuleMap.
1237 | moduleName = deps;
1238 | relModuleMap = callback;
1239 |
1240 | //Normalize module name, if it contains . or ..
1241 | moduleMap = makeModuleMap(moduleName, relModuleMap);
1242 | fullName = moduleMap.fullName;
1243 |
1244 | if (!(fullName in defined)) {
1245 | return req.onError(makeError("notloaded", "Module name '" +
1246 | moduleMap.fullName +
1247 | "' has not been loaded yet for context: " +
1248 | contextName));
1249 | }
1250 | return defined[fullName];
1251 | }
1252 |
1253 | main(null, deps, callback, relModuleMap);
1254 |
1255 | //If the require call does not trigger anything new to load,
1256 | //then resume the dependency processing.
1257 | if (!context.requireWait) {
1258 | while (!context.scriptCount && context.paused.length) {
1259 | //For built layers, there can be some defined
1260 | //modules waiting for intake into the context,
1261 | //in particular module plugins. Take them.
1262 | context.takeGlobalQueue();
1263 | resume();
1264 | }
1265 | }
1266 | return undefined;
1267 | },
1268 |
1269 | /**
1270 | * Internal method to transfer globalQueue items to this context's
1271 | * defQueue.
1272 | */
1273 | takeGlobalQueue: function () {
1274 | //Push all the globalDefQueue items into the context's defQueue
1275 | if (globalDefQueue.length) {
1276 | //Array splice in the values since the context code has a
1277 | //local var ref to defQueue, so cannot just reassign the one
1278 | //on context.
1279 | apsp.apply(context.defQueue,
1280 | [context.defQueue.length - 1, 0].concat(globalDefQueue));
1281 | globalDefQueue = [];
1282 | }
1283 | },
1284 |
1285 | /**
1286 | * Internal method used by environment adapters to complete a load event.
1287 | * A load event could be a script load or just a load pass from a synchronous
1288 | * load call.
1289 | * @param {String} moduleName the name of the module to potentially complete.
1290 | */
1291 | completeLoad: function (moduleName) {
1292 | var args;
1293 |
1294 | context.takeGlobalQueue();
1295 |
1296 | while (defQueue.length) {
1297 | args = defQueue.shift();
1298 |
1299 | if (args[0] === null) {
1300 | args[0] = moduleName;
1301 | break;
1302 | } else if (args[0] === moduleName) {
1303 | //Found matching define call for this script!
1304 | break;
1305 | } else {
1306 | //Some other named define call, most likely the result
1307 | //of a build layer that included many define calls.
1308 | callDefMain(args);
1309 | args = null;
1310 | }
1311 | }
1312 | if (args) {
1313 | callDefMain(args);
1314 | } else {
1315 | //A script that does not call define(), so just simulate
1316 | //the call for it. Special exception for jQuery dynamic load.
1317 | callDefMain([moduleName, [],
1318 | moduleName === "jquery" && typeof jQuery !== "undefined" ?
1319 | function () {
1320 | return jQuery;
1321 | } : null]);
1322 | }
1323 |
1324 | //Mark the script as loaded. Note that this can be different from a
1325 | //moduleName that maps to a define call. This line is important
1326 | //for traditional browser scripts.
1327 | loaded[moduleName] = true;
1328 |
1329 | //If a global jQuery is defined, check for it. Need to do it here
1330 | //instead of main() since stock jQuery does not register as
1331 | //a module via define.
1332 | jQueryCheck();
1333 |
1334 | //Doing this scriptCount decrement branching because sync envs
1335 | //need to decrement after resume, otherwise it looks like
1336 | //loading is complete after the first dependency is fetched.
1337 | //For browsers, it works fine to decrement after, but it means
1338 | //the checkLoaded setTimeout 50 ms cost is taken. To avoid
1339 | //that cost, decrement beforehand.
1340 | if (req.isAsync) {
1341 | context.scriptCount -= 1;
1342 | }
1343 | resume();
1344 | if (!req.isAsync) {
1345 | context.scriptCount -= 1;
1346 | }
1347 | },
1348 |
1349 | /**
1350 | * Converts a module name + .extension into an URL path.
1351 | * *Requires* the use of a module name. It does not support using
1352 | * plain URLs like nameToUrl.
1353 | */
1354 | toUrl: function (moduleNamePlusExt, relModuleMap) {
1355 | var index = moduleNamePlusExt.lastIndexOf("."),
1356 | ext = null;
1357 |
1358 | if (index !== -1) {
1359 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
1360 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
1361 | }
1362 |
1363 | return context.nameToUrl(moduleNamePlusExt, ext, relModuleMap);
1364 | },
1365 |
1366 | /**
1367 | * Converts a module name to a file path. Supports cases where
1368 | * moduleName may actually be just an URL.
1369 | */
1370 | nameToUrl: function (moduleName, ext, relModuleMap) {
1371 | var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
1372 | config = context.config;
1373 |
1374 | //Normalize module name if have a base relative module name to work from.
1375 | moduleName = normalize(moduleName, relModuleMap && relModuleMap.fullName);
1376 |
1377 | //If a colon is in the URL, it indicates a protocol is used and it is just
1378 | //an URL to a file, or if it starts with a slash or ends with .js, it is just a plain file.
1379 | //The slash is important for protocol-less URLs as well as full paths.
1380 | if (req.jsExtRegExp.test(moduleName)) {
1381 | //Just a plain path, not module name lookup, so just return it.
1382 | //Add extension if it is included. This is a bit wonky, only non-.js things pass
1383 | //an extension, this method probably needs to be reworked.
1384 | url = moduleName + (ext ? ext : "");
1385 | } else {
1386 | //A module that needs to be converted to a path.
1387 | paths = config.paths;
1388 | pkgs = config.pkgs;
1389 |
1390 | syms = moduleName.split("/");
1391 | //For each module name segment, see if there is a path
1392 | //registered for it. Start with most specific name
1393 | //and work up from it.
1394 | for (i = syms.length; i > 0; i--) {
1395 | parentModule = syms.slice(0, i).join("/");
1396 | if (paths[parentModule]) {
1397 | syms.splice(0, i, paths[parentModule]);
1398 | break;
1399 | } else if ((pkg = pkgs[parentModule])) {
1400 | //If module name is just the package name, then looking
1401 | //for the main module.
1402 | if (moduleName === pkg.name) {
1403 | pkgPath = pkg.location + '/' + pkg.main;
1404 | } else {
1405 | pkgPath = pkg.location;
1406 | }
1407 | syms.splice(0, i, pkgPath);
1408 | break;
1409 | }
1410 | }
1411 |
1412 | //Join the path parts together, then figure out if baseUrl is needed.
1413 | url = syms.join("/") + (ext || ".js");
1414 | url = (url.charAt(0) === '/' || url.match(/^\w+:/) ? "" : config.baseUrl) + url;
1415 | }
1416 |
1417 | return config.urlArgs ? url +
1418 | ((url.indexOf('?') === -1 ? '?' : '&') +
1419 | config.urlArgs) : url;
1420 | }
1421 | };
1422 |
1423 | //Make these visible on the context so can be called at the very
1424 | //end of the file to bootstrap
1425 | context.jQueryCheck = jQueryCheck;
1426 | context.resume = resume;
1427 |
1428 | return context;
1429 | }
1430 |
1431 | /**
1432 | * Main entry point.
1433 | *
1434 | * If the only argument to require is a string, then the module that
1435 | * is represented by that string is fetched for the appropriate context.
1436 | *
1437 | * If the first argument is an array, then it will be treated as an array
1438 | * of dependency string names to fetch. An optional function callback can
1439 | * be specified to execute when all of those dependencies are available.
1440 | *
1441 | * Make a local req variable to help Caja compliance (it assumes things
1442 | * on a require that are not standardized), and to give a short
1443 | * name for minification/local scope use.
1444 | */
1445 | req = requirejs = function (deps, callback) {
1446 |
1447 | //Find the right context, use default
1448 | var contextName = defContextName,
1449 | context, config;
1450 |
1451 | // Determine if have config object in the call.
1452 | if (!isArray(deps) && typeof deps !== "string") {
1453 | // deps is a config object
1454 | config = deps;
1455 | if (isArray(callback)) {
1456 | // Adjust args if there are dependencies
1457 | deps = callback;
1458 | callback = arguments[2];
1459 | } else {
1460 | deps = [];
1461 | }
1462 | }
1463 |
1464 | if (config && config.context) {
1465 | contextName = config.context;
1466 | }
1467 |
1468 | context = contexts[contextName] ||
1469 | (contexts[contextName] = newContext(contextName));
1470 |
1471 | if (config) {
1472 | context.configure(config);
1473 | }
1474 |
1475 | return context.require(deps, callback);
1476 | };
1477 |
1478 | /**
1479 | * Export require as a global, but only if it does not already exist.
1480 | */
1481 | if (typeof require === "undefined") {
1482 | require = req;
1483 | }
1484 |
1485 | /**
1486 | * Global require.toUrl(), to match global require, mostly useful
1487 | * for debugging/work in the global space.
1488 | */
1489 | req.toUrl = function (moduleNamePlusExt) {
1490 | return contexts[defContextName].toUrl(moduleNamePlusExt);
1491 | };
1492 |
1493 | req.version = version;
1494 | req.isArray = isArray;
1495 | req.isFunction = isFunction;
1496 | req.mixin = mixin;
1497 | //Used to filter out dependencies that are already paths.
1498 | req.jsExtRegExp = /^\/|:|\?|\.js$/;
1499 | s = req.s = {
1500 | contexts: contexts,
1501 | //Stores a list of URLs that should not get async script tag treatment.
1502 | skipAsync: {},
1503 | isPageLoaded: !isBrowser,
1504 | readyCalls: []
1505 | };
1506 |
1507 | req.isAsync = req.isBrowser = isBrowser;
1508 | if (isBrowser) {
1509 | head = s.head = document.getElementsByTagName("head")[0];
1510 | //If BASE tag is in play, using appendChild is a problem for IE6.
1511 | //When that browser dies, this can be removed. Details in this jQuery bug:
1512 | //http://dev.jquery.com/ticket/2709
1513 | baseElement = document.getElementsByTagName("base")[0];
1514 | if (baseElement) {
1515 | head = s.head = baseElement.parentNode;
1516 | }
1517 | }
1518 |
1519 | /**
1520 | * Any errors that require explicitly generates will be passed to this
1521 | * function. Intercept/override it if you want custom error handling.
1522 | * @param {Error} err the error object.
1523 | */
1524 | req.onError = function (err) {
1525 | throw err;
1526 | };
1527 |
1528 | /**
1529 | * Does the request to load a module for the browser case.
1530 | * Make this a separate function to allow other environments
1531 | * to override it.
1532 | *
1533 | * @param {Object} context the require context to find state.
1534 | * @param {String} moduleName the name of the module.
1535 | * @param {Object} url the URL to the module.
1536 | */
1537 | req.load = function (context, moduleName, url) {
1538 | var loaded = context.loaded;
1539 |
1540 | isDone = false;
1541 |
1542 | //Only set loaded to false for tracking if it has not already been set.
1543 | if (!loaded[moduleName]) {
1544 | loaded[moduleName] = false;
1545 | }
1546 |
1547 | context.scriptCount += 1;
1548 | req.attach(url, context, moduleName);
1549 |
1550 | //If tracking a jQuery, then make sure its ready callbacks
1551 | //are put on hold to prevent its ready callbacks from
1552 | //triggering too soon.
1553 | if (context.jQuery && !context.jQueryIncremented) {
1554 | jQueryHoldReady(context.jQuery, true);
1555 | context.jQueryIncremented = true;
1556 | }
1557 | };
1558 |
1559 | function getInteractiveScript() {
1560 | var scripts, i, script;
1561 | if (interactiveScript && interactiveScript.readyState === 'interactive') {
1562 | return interactiveScript;
1563 | }
1564 |
1565 | scripts = document.getElementsByTagName('script');
1566 | for (i = scripts.length - 1; i > -1 && (script = scripts[i]); i--) {
1567 | if (script.readyState === 'interactive') {
1568 | return (interactiveScript = script);
1569 | }
1570 | }
1571 |
1572 | return null;
1573 | }
1574 |
1575 | /**
1576 | * The function that handles definitions of modules. Differs from
1577 | * require() in that a string for the module should be the first argument,
1578 | * and the function to execute after dependencies are loaded should
1579 | * return a value to define the module corresponding to the first argument's
1580 | * name.
1581 | */
1582 | define = req.def = function (name, deps, callback) {
1583 | var node, context;
1584 |
1585 | //Allow for anonymous functions
1586 | if (typeof name !== 'string') {
1587 | //Adjust args appropriately
1588 | callback = deps;
1589 | deps = name;
1590 | name = null;
1591 | }
1592 |
1593 | //This module may not have dependencies
1594 | if (!req.isArray(deps)) {
1595 | callback = deps;
1596 | deps = [];
1597 | }
1598 |
1599 | //If no name, and callback is a function, then figure out if it a
1600 | //CommonJS thing with dependencies.
1601 | if (!name && !deps.length && req.isFunction(callback)) {
1602 | //Remove comments from the callback string,
1603 | //look for require calls, and pull them into the dependencies,
1604 | //but only if there are function args.
1605 | if (callback.length) {
1606 | callback
1607 | .toString()
1608 | .replace(commentRegExp, "")
1609 | .replace(cjsRequireRegExp, function (match, dep) {
1610 | deps.push(dep);
1611 | });
1612 |
1613 | //May be a CommonJS thing even without require calls, but still
1614 | //could use exports, and module. Avoid doing exports and module
1615 | //work though if it just needs require.
1616 | //REQUIRES the function to expect the CommonJS variables in the
1617 | //order listed below.
1618 | deps = (callback.length === 1 ? ["require"] : ["require", "exports", "module"]).concat(deps);
1619 | }
1620 | }
1621 |
1622 | //If in IE 6-8 and hit an anonymous define() call, do the interactive
1623 | //work.
1624 | if (useInteractive) {
1625 | node = currentlyAddingScript || getInteractiveScript();
1626 | if (!node) {
1627 | return req.onError(makeError("interactive", "No matching script interactive for " + callback));
1628 | }
1629 | if (!name) {
1630 | name = node.getAttribute("data-requiremodule");
1631 | }
1632 | context = contexts[node.getAttribute("data-requirecontext")];
1633 | }
1634 |
1635 | //Always save off evaluating the def call until the script onload handler.
1636 | //This allows multiple modules to be in a file without prematurely
1637 | //tracing dependencies, and allows for anonymous module support,
1638 | //where the module name is not known until the script onload event
1639 | //occurs. If no context, use the global queue, and get it processed
1640 | //in the onscript load callback.
1641 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
1642 |
1643 | return undefined;
1644 | };
1645 |
1646 | define.amd = {
1647 | multiversion: true,
1648 | plugins: true,
1649 | jQuery: true
1650 | };
1651 |
1652 | /**
1653 | * Executes the text. Normally just uses eval, but can be modified
1654 | * to use a more environment specific call.
1655 | * @param {String} text the text to execute/evaluate.
1656 | */
1657 | req.exec = function (text) {
1658 | return eval(text);
1659 | };
1660 |
1661 | /**
1662 | * Executes a module callack function. Broken out as a separate function
1663 | * solely to allow the build system to sequence the files in the built
1664 | * layer in the right sequence.
1665 | *
1666 | * @private
1667 | */
1668 | req.execCb = function (name, callback, args, exports) {
1669 | return callback.apply(exports, args);
1670 | };
1671 |
1672 | /**
1673 | * callback for script loads, used to check status of loading.
1674 | *
1675 | * @param {Event} evt the event from the browser for the script
1676 | * that was loaded.
1677 | *
1678 | * @private
1679 | */
1680 | req.onScriptLoad = function (evt) {
1681 | //Using currentTarget instead of target for Firefox 2.0's sake. Not
1682 | //all old browsers will be supported, but this one was easy enough
1683 | //to support and still makes sense.
1684 | var node = evt.currentTarget || evt.srcElement, contextName, moduleName,
1685 | context;
1686 |
1687 | if (evt.type === "load" || readyRegExp.test(node.readyState)) {
1688 | //Reset interactive script so a script node is not held onto for
1689 | //to long.
1690 | interactiveScript = null;
1691 |
1692 | //Pull out the name of the module and the context.
1693 | contextName = node.getAttribute("data-requirecontext");
1694 | moduleName = node.getAttribute("data-requiremodule");
1695 | context = contexts[contextName];
1696 |
1697 | contexts[contextName].completeLoad(moduleName);
1698 |
1699 | //Clean up script binding. Favor detachEvent because of IE9
1700 | //issue, see attachEvent/addEventListener comment elsewhere
1701 | //in this file.
1702 | if (node.detachEvent && !isOpera) {
1703 | //Probably IE. If not it will throw an error, which will be
1704 | //useful to know.
1705 | node.detachEvent("onreadystatechange", req.onScriptLoad);
1706 | } else {
1707 | node.removeEventListener("load", req.onScriptLoad, false);
1708 | }
1709 | }
1710 | };
1711 |
1712 | /**
1713 | * Attaches the script represented by the URL to the current
1714 | * environment. Right now only supports browser loading,
1715 | * but can be redefined in other environments to do the right thing.
1716 | * @param {String} url the url of the script to attach.
1717 | * @param {Object} context the context that wants the script.
1718 | * @param {moduleName} the name of the module that is associated with the script.
1719 | * @param {Function} [callback] optional callback, defaults to require.onScriptLoad
1720 | * @param {String} [type] optional type, defaults to text/javascript
1721 | */
1722 | req.attach = function (url, context, moduleName, callback, type) {
1723 | var node, loaded;
1724 | if (isBrowser) {
1725 | //In the browser so use a script tag
1726 | callback = callback || req.onScriptLoad;
1727 | node = context && context.config && context.config.xhtml ?
1728 | document.createElementNS("http://www.w3.org/1999/xhtml", "html:script") :
1729 | document.createElement("script");
1730 | node.type = type || "text/javascript";
1731 | node.charset = "utf-8";
1732 | //Use async so Gecko does not block on executing the script if something
1733 | //like a long-polling comet tag is being run first. Gecko likes
1734 | //to evaluate scripts in DOM order, even for dynamic scripts.
1735 | //It will fetch them async, but only evaluate the contents in DOM
1736 | //order, so a long-polling script tag can delay execution of scripts
1737 | //after it. But telling Gecko we expect async gets us the behavior
1738 | //we want -- execute it whenever it is finished downloading. Only
1739 | //Helps Firefox 3.6+
1740 | //Allow some URLs to not be fetched async. Mostly helps the order!
1741 | //plugin
1742 | node.async = !s.skipAsync[url];
1743 |
1744 | if (context) {
1745 | node.setAttribute("data-requirecontext", context.contextName);
1746 | }
1747 | node.setAttribute("data-requiremodule", moduleName);
1748 |
1749 | //Set up load listener. Test attachEvent first because IE9 has
1750 | //a subtle issue in its addEventListener and script onload firings
1751 | //that do not match the behavior of all other browsers with
1752 | //addEventListener support, which fire the onload event for a
1753 | //script right after the script execution. See:
1754 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
1755 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script
1756 | //script execution mode.
1757 | if (node.attachEvent && !isOpera) {
1758 | //Probably IE. IE (at least 6-8) do not fire
1759 | //script onload right after executing the script, so
1760 | //we cannot tie the anonymous define call to a name.
1761 | //However, IE reports the script as being in "interactive"
1762 | //readyState at the time of the define call.
1763 | useInteractive = true;
1764 | node.attachEvent("onreadystatechange", callback);
1765 | } else {
1766 | node.addEventListener("load", callback, false);
1767 | }
1768 | node.src = url;
1769 |
1770 | //For some cache cases in IE 6-8, the script executes before the end
1771 | //of the appendChild execution, so to tie an anonymous define
1772 | //call to the module name (which is stored on the node), hold on
1773 | //to a reference to this node, but clear after the DOM insertion.
1774 | currentlyAddingScript = node;
1775 | if (baseElement) {
1776 | head.insertBefore(node, baseElement);
1777 | } else {
1778 | head.appendChild(node);
1779 | }
1780 | currentlyAddingScript = null;
1781 | return node;
1782 | } else if (isWebWorker) {
1783 | //In a web worker, use importScripts. This is not a very
1784 | //efficient use of importScripts, importScripts will block until
1785 | //its script is downloaded and evaluated. However, if web workers
1786 | //are in play, the expectation that a build has been done so that
1787 | //only one script needs to be loaded anyway. This may need to be
1788 | //reevaluated if other use cases become common.
1789 | loaded = context.loaded;
1790 | loaded[moduleName] = false;
1791 |
1792 | importScripts(url);
1793 |
1794 | //Account for anonymous modules
1795 | context.completeLoad(moduleName);
1796 | }
1797 | return null;
1798 | };
1799 |
1800 | //Look for a data-main script attribute, which could also adjust the baseUrl.
1801 | if (isBrowser) {
1802 | //Figure out baseUrl. Get it from the script tag with require.js in it.
1803 | scripts = document.getElementsByTagName("script");
1804 |
1805 | for (i = scripts.length - 1; i > -1 && (script = scripts[i]); i--) {
1806 | //Set the "head" where we can append children by
1807 | //using the script's parent.
1808 | if (!head) {
1809 | head = script.parentNode;
1810 | }
1811 |
1812 | //Look for a data-main attribute to set main script for the page
1813 | //to load. If it is there, the path to data main becomes the
1814 | //baseUrl, if it is not already set.
1815 | if ((dataMain = script.getAttribute('data-main'))) {
1816 | if (!cfg.baseUrl) {
1817 | //Pull off the directory of data-main for use as the
1818 | //baseUrl.
1819 | src = dataMain.split('/');
1820 | mainScript = src.pop();
1821 | subPath = src.length ? src.join('/') + '/' : './';
1822 |
1823 | //Set final config.
1824 | cfg.baseUrl = subPath;
1825 | //Strip off any trailing .js since dataMain is now
1826 | //like a module name.
1827 | dataMain = mainScript.replace(jsSuffixRegExp, '');
1828 | }
1829 |
1830 | //Put the data-main script in the files to load.
1831 | cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
1832 |
1833 | break;
1834 | }
1835 | }
1836 | }
1837 |
1838 | //Set baseUrl based on config.
1839 | s.baseUrl = cfg.baseUrl;
1840 |
1841 | //****** START page load functionality ****************
1842 | /**
1843 | * Sets the page as loaded and triggers check for all modules loaded.
1844 | */
1845 | req.pageLoaded = function () {
1846 | if (!s.isPageLoaded) {
1847 | s.isPageLoaded = true;
1848 | if (scrollIntervalId) {
1849 | clearInterval(scrollIntervalId);
1850 | }
1851 |
1852 | //Part of a fix for FF < 3.6 where readyState was not set to
1853 | //complete so libraries like jQuery that check for readyState
1854 | //after page load where not getting initialized correctly.
1855 | //Original approach suggested by Andrea Giammarchi:
1856 | //http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
1857 | //see other setReadyState reference for the rest of the fix.
1858 | if (setReadyState) {
1859 | document.readyState = "complete";
1860 | }
1861 |
1862 | req.callReady();
1863 | }
1864 | };
1865 |
1866 | //See if there is nothing waiting across contexts, and if not, trigger
1867 | //callReady.
1868 | req.checkReadyState = function () {
1869 | var contexts = s.contexts, prop;
1870 | for (prop in contexts) {
1871 | if (!(prop in empty)) {
1872 | if (contexts[prop].waitCount) {
1873 | return;
1874 | }
1875 | }
1876 | }
1877 | s.isDone = true;
1878 | req.callReady();
1879 | };
1880 |
1881 | /**
1882 | * Internal function that calls back any ready functions. If you are
1883 | * integrating RequireJS with another library without require.ready support,
1884 | * you can define this method to call your page ready code instead.
1885 | */
1886 | req.callReady = function () {
1887 | var callbacks = s.readyCalls, i, callback, contexts, context, prop;
1888 |
1889 | if (s.isPageLoaded && s.isDone) {
1890 | if (callbacks.length) {
1891 | s.readyCalls = [];
1892 | for (i = 0; (callback = callbacks[i]); i++) {
1893 | callback();
1894 | }
1895 | }
1896 |
1897 | //If jQuery with DOM ready delayed, release it now.
1898 | contexts = s.contexts;
1899 | for (prop in contexts) {
1900 | if (!(prop in empty)) {
1901 | context = contexts[prop];
1902 | if (context.jQueryIncremented) {
1903 | jQueryHoldReady(context.jQuery, false);
1904 | context.jQueryIncremented = false;
1905 | }
1906 | }
1907 | }
1908 | }
1909 | };
1910 |
1911 | /**
1912 | * Registers functions to call when the page is loaded
1913 | */
1914 | req.ready = function (callback) {
1915 | if (s.isPageLoaded && s.isDone) {
1916 | callback();
1917 | } else {
1918 | s.readyCalls.push(callback);
1919 | }
1920 | return req;
1921 | };
1922 |
1923 | if (isBrowser) {
1924 | if (document.addEventListener) {
1925 | //Standards. Hooray! Assumption here that if standards based,
1926 | //it knows about DOMContentLoaded.
1927 | document.addEventListener("DOMContentLoaded", req.pageLoaded, false);
1928 | window.addEventListener("load", req.pageLoaded, false);
1929 | //Part of FF < 3.6 readystate fix (see setReadyState refs for more info)
1930 | if (!document.readyState) {
1931 | setReadyState = true;
1932 | document.readyState = "loading";
1933 | }
1934 | } else if (window.attachEvent) {
1935 | window.attachEvent("onload", req.pageLoaded);
1936 |
1937 | //DOMContentLoaded approximation, as found by Diego Perini:
1938 | //http://javascript.nwbox.com/IEContentLoaded/
1939 | if (self === self.top) {
1940 | scrollIntervalId = setInterval(function () {
1941 | try {
1942 | //From this ticket:
1943 | //http://bugs.dojotoolkit.org/ticket/11106,
1944 | //In IE HTML Application (HTA), such as in a selenium test,
1945 | //javascript in the iframe can't see anything outside
1946 | //of it, so self===self.top is true, but the iframe is
1947 | //not the top window and doScroll will be available
1948 | //before document.body is set. Test document.body
1949 | //before trying the doScroll trick.
1950 | if (document.body) {
1951 | document.documentElement.doScroll("left");
1952 | req.pageLoaded();
1953 | }
1954 | } catch (e) {}
1955 | }, 30);
1956 | }
1957 | }
1958 |
1959 | //Check if document already complete, and if so, just trigger page load
1960 | //listeners. NOTE: does not work with Firefox before 3.6. To support
1961 | //those browsers, manually call require.pageLoaded().
1962 | if (document.readyState === "complete") {
1963 | req.pageLoaded();
1964 | }
1965 | }
1966 | //****** END page load functionality ****************
1967 |
1968 | //Set up default context. If require was a configuration object, use that as base config.
1969 | req(cfg);
1970 |
1971 | //If modules are built into require.js, then need to make sure dependencies are
1972 | //traced. Use a setTimeout in the browser world, to allow all the modules to register
1973 | //themselves. In a non-browser env, assume that modules are not built into require.js,
1974 | //which seems odd to do on the server.
1975 | if (req.isAsync && typeof setTimeout !== "undefined") {
1976 | ctx = s.contexts[(cfg.context || defContextName)];
1977 | //Indicate that the script that includes require() is still loading,
1978 | //so that require()'d dependencies are not traced until the end of the
1979 | //file is parsed (approximated via the setTimeout call).
1980 | ctx.requireWait = true;
1981 | setTimeout(function () {
1982 | ctx.requireWait = false;
1983 |
1984 | //Any modules included with the require.js file will be in the
1985 | //global queue, assign them to this context.
1986 | ctx.takeGlobalQueue();
1987 |
1988 | //Allow for jQuery to be loaded/already in the page, and if jQuery 1.4.3,
1989 | //make sure to hold onto it for readyWait triggering.
1990 | ctx.jQueryCheck();
1991 |
1992 | if (!ctx.scriptCount) {
1993 | ctx.resume();
1994 | }
1995 | req.checkReadyState();
1996 | }, 0);
1997 | }
1998 | }());
--------------------------------------------------------------------------------