├── .npmignore ├── page ├── logo.png ├── background.png ├── page.html └── style.css ├── vendor ├── closure-compiler.jar ├── curl.js └── require.js ├── .gitmodules ├── package.json ├── LICENSE ├── test ├── test_browser.html └── test_json3.js ├── jsl.conf ├── lib ├── json3.min.js └── json3.js ├── README.md └── index.html /.npmignore: -------------------------------------------------------------------------------- 1 | build.js 2 | index.html 3 | jsl.conf 4 | 5 | page 6 | test 7 | vendor -------------------------------------------------------------------------------- /page/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/json3/gh-pages/page/logo.png -------------------------------------------------------------------------------- /page/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/json3/gh-pages/page/background.png -------------------------------------------------------------------------------- /vendor/closure-compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/json3/gh-pages/vendor/closure-compiler.jar -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/spec"] 2 | path = vendor/spec 3 | url = git://github.com/kitcambridge/spec.git 4 | [submodule "vendor/marked"] 5 | path = vendor/marked 6 | url = git://github.com/chjj/marked.git 7 | -------------------------------------------------------------------------------- /page/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSON 3 6 | 7 | 8 | 9 | 12 |
13 | <%= source %> 14 |
15 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json3", 3 | "version": "3.2.4", 4 | "description": "A modern JSON implementation compatible with nearly all JavaScript platforms.", 5 | "homepage": "http://bestiejs.github.com/json3", 6 | "main": "./lib/json3", 7 | "keywords": ["json", "spec", "ecma", "es5", "lexer", "parser", "stringify"], 8 | "licenses": [{ 9 | "type": "MIT", 10 | "url": "http://kit.mit-license.org/" 11 | }], 12 | "author": { 13 | "name": "Kit Cambridge", 14 | "web": "http://kitcambridge.github.com" 15 | }, 16 | "maintainers": [{ 17 | "name": "Kit Cambridge", 18 | "web": "http://kitcambridge.github.com" 19 | }], 20 | "bugs": { 21 | "url": "http://github.com/bestiejs/json3/issues" 22 | }, 23 | "scripts": { 24 | "test": "node test/test_*.js" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/bestiejs/json3.git" 29 | } 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Kit Cambridge. 2 | http://kitcambridge.github.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. -------------------------------------------------------------------------------- /test/test_browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSON 3 Unit Tests 6 | 7 | 8 | 9 | 28 | 56 | 57 | 58 |
59 | 60 | -------------------------------------------------------------------------------- /page/style.css: -------------------------------------------------------------------------------- 1 | /* Earl Grey | http://kitcambridge.github.com/earlgrey | Copyright 2011-2012, Kit Cambridge | http://creativecommons.org/licenses/by/3.0 */ 2 | html { 3 | font: 12px/24px "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif; 4 | background: url(background.png); 5 | } 6 | 7 | body { 8 | width: 80%; 9 | margin: 20px auto; 10 | border: 3px solid #eee; 11 | background: #fff; 12 | } 13 | 14 | #content { 15 | padding: 25px; 16 | text-align: justify; 17 | } 18 | 19 | p, ul, ol { 20 | padding: 5px 0; 21 | margin: 0; 22 | } 23 | 24 | ul, ol { 25 | color: #333; 26 | margin: auto auto auto 25px; 27 | } 28 | 29 | a { 30 | padding: 1px; 31 | color: inherit; 32 | text-decoration: underline; 33 | } 34 | 35 | a:hover, a:focus { 36 | text-decoration: none; 37 | } 38 | 39 | #content a:hover, #content a:focus { 40 | background: #000; 41 | color: #fff; 42 | } 43 | 44 | #footer a:hover, #footer a:focus { 45 | background: #fff; 46 | color: #000; 47 | } 48 | 49 | h1, h2, h3 { 50 | padding: 5px 0; 51 | margin: 0; 52 | } 53 | 54 | h1 { 55 | font: 24px Palatino, Georgia, Times, "Times New Roman", serif; 56 | color: #900; 57 | } 58 | 59 | h2, h3 { 60 | text-transform: uppercase; 61 | color: #666; 62 | letter-spacing: 1px; 63 | font-weight: bold; 64 | } 65 | 66 | h2 { 67 | font-size: 14px; 68 | } 69 | 70 | h3 { 71 | font-size: 12px; 72 | } 73 | 74 | table { 75 | border-collapse: collapse; 76 | border: solid #ccc; 77 | border-width: 1px 0 0 1px; 78 | margin: 15px 0; 79 | } 80 | 81 | td, th { 82 | border: solid #ccc; 83 | border-width: 0 1px 1px 0; 84 | padding: 5px; 85 | width: 20%; 86 | text-align: center; 87 | } 88 | 89 | th { 90 | font-weight: bold; 91 | color: #333; 92 | } 93 | 94 | #navigation { 95 | list-style: none; 96 | float: left; 97 | text-align: center; 98 | width: 100%; 99 | background: #333; 100 | padding: 0; 101 | margin: 0 0 10px 0; 102 | } 103 | 104 | #navigation li { 105 | display: inline; 106 | } 107 | 108 | #navigation a { 109 | display: inline-block; 110 | color: #fff; 111 | padding: 5px 5% 5px; 112 | height: 20px; 113 | text-decoration: none; 114 | text-align: center; 115 | } 116 | 117 | #navigation a:hover, #navigation a:focus, #navigation a.active { 118 | background: #eee; 119 | color: #900; 120 | } 121 | 122 | #footer { 123 | background: #333; 124 | color: #fff; 125 | text-align: center; 126 | } 127 | 128 | pre, code, tt { 129 | font: normal 12px/24px Menlo, Monaco, Consolas, "Lucida Console", monospace; 130 | } 131 | 132 | pre { 133 | background-color: #f8f8ff; 134 | padding: 10px; 135 | border: 1px solid #eee; 136 | word-wrap: break-word; 137 | white-space: pre-wrap; 138 | } 139 | 140 | img { 141 | padding: 0 0 30px 30px; 142 | float: right; 143 | } -------------------------------------------------------------------------------- /jsl.conf: -------------------------------------------------------------------------------- 1 | # JavaScript Lint configuration file for JSON 3. 2 | 3 | +process lib/json3.js 4 | +process test/test_json3.js 5 | 6 | +output-format Problem at line __LINE__: __ERROR__ 7 | +context 8 | 9 | # Predefine global variables. 10 | +define define 11 | +define exports 12 | +define require 13 | +define console 14 | +define print 15 | +define module 16 | +define JSON 17 | 18 | # Function does not explicitly return a value. 19 | -no_return_value 20 | -anon_no_return_value 21 | 22 | # Duplicate argument name. 23 | +duplicate_formal 24 | 25 | # Assignment expression used in a conditional. 26 | -equal_as_assign 27 | 28 | # Declared variable shadows an argument. 29 | +var_hides_arg 30 | 31 | # Redeclared variable. 32 | +redeclared_var 33 | 34 | # Missing semicolon. 35 | +missing_semicolon 36 | +lambda_assign_requires_semicolon 37 | 38 | # Meaningless block; curly braces are not required. 39 | +meaningless_block 40 | 41 | # Comma-separated statements; use semicolons instead. 42 | -comma_separated_stmts 43 | 44 | # Unreachable code. 45 | +unreachable_code 46 | 47 | # Missing `break` statement. 48 | +missing_break 49 | -missing_break_for_last_case 50 | 51 | # The `default` case in a `switch` statement is either missing or not at the 52 | # end of the statement. 53 | -missing_default_case 54 | +default_not_at_end 55 | 56 | # `switch` statement contains a duplicate `case`. 57 | +duplicate_case_in_switch 58 | 59 | # Loose comparison against `null`, 0, `true`, `false`, or "". 60 | -comparison_type_conv 61 | 62 | # Increment or decrement operator used as part of an expression. 63 | -inc_dec_within_stmt 64 | 65 | # Ambiguous increment/decrement operator (x+++y or x---y). 66 | +multiple_plus_minus 67 | 68 | # Unnecessary use of the `void` operator. 69 | -useless_void 70 | 71 | # Unnecessary label. 72 | +use_of_label 73 | 74 | # Block statements should be surrounded by curly braces. 75 | -block_without_braces 76 | 77 | # Ambiguous leading or trailing decimal point (denotes either a number or an 78 | # object member). 79 | -leading_decimal_point 80 | -trailing_decimal_point 81 | 82 | # Leading zeros denote octal numbers. 83 | +octal_number 84 | 85 | # Nested comment. 86 | +nested_comment 87 | 88 | # A regular expression should be preceded by either a left parenthesis, 89 | # assignment, colon, or comma. 90 | +misplaced_regex 91 | 92 | # Line breaking error; unclear whether the lines are part of the same 93 | # statement. 94 | +ambiguous_newline 95 | 96 | # Empty statement or unnecessary semicolon. 97 | -empty_statement 98 | 99 | # Check for undeclared identifiers by enabling the "option explicit" setting. 100 | +always_use_option_explicit 101 | 102 | # Missing, incorrect, or duplicate "option explicit" control comment. 103 | -missing_option_explicit 104 | +partial_option_explicit 105 | +dup_option_explicit 106 | 107 | # Unnecessary assignment expression. 108 | +useless_assign 109 | 110 | # Nested block statements and `else` statements should be disambiguated by 111 | # curly braces. 112 | -ambiguous_nested_stmt 113 | +ambiguous_else_stmt 114 | 115 | # Legacy or malformed JSL control comment. 116 | -legacy_control_comments 117 | +legacy_cc_not_understood 118 | +jsl_cc_not_understood 119 | 120 | # JScript-specific qualified function name (see "JScript Deviations from ES3," 121 | # section 4.4). 122 | -jscript_function_extensions 123 | 124 | # Useless comparison (comparing identical expressions). 125 | -useless_comparison 126 | 127 | # Use a temporary variable instead of a `with` statement. 128 | +with_statement 129 | 130 | # Extra comma in array literal. 131 | +trailing_comma_in_array 132 | 133 | # Assignment to a function call. 134 | +assign_to_function_call 135 | 136 | # Missing radix parameter for `parseInt`. 137 | +parseint_missing_radix -------------------------------------------------------------------------------- /lib/json3.min.js: -------------------------------------------------------------------------------- 1 | /*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */ 2 | ;(function(){var e=void 0,i=!0,k=null,l={}.toString,m,n,p="function"===typeof define&&define.c,q=!p&&"object"==typeof exports&&exports;q||p?"object"==typeof JSON&&JSON?p?q=JSON:(q.stringify=JSON.stringify,q.parse=JSON.parse):p&&(q=this.JSON={}):q=this.JSON||(this.JSON={});var r,t,u,x,z,B,C,D,E,F,G,H,I,J=new Date(-3509827334573292),K,O,P;try{J=-109252==J.getUTCFullYear()&&0===J.getUTCMonth()&&1==J.getUTCDate()&&10==J.getUTCHours()&&37==J.getUTCMinutes()&&6==J.getUTCSeconds()&&708==J.getUTCMilliseconds()}catch(Q){} 3 | function R(b){var c,a,d,j=b=="json";if(j||b=="json-stringify"||b=="json-parse"){if(b=="json-stringify"||j){if(c=typeof q.stringify=="function"&&J){(d=function(){return 1}).toJSON=d;try{c=q.stringify(0)==="0"&&q.stringify(new Number)==="0"&&q.stringify(new String)=='""'&&q.stringify(l)===e&&q.stringify(e)===e&&q.stringify()===e&&q.stringify(d)==="1"&&q.stringify([d])=="[1]"&&q.stringify([e])=="[null]"&&q.stringify(k)=="null"&&q.stringify([e,l,k])=="[null,null,null]"&&q.stringify({A:[d,i,false,k,"\x00\u0008\n\u000c\r\t"]})== 4 | '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'&&q.stringify(k,d)==="1"&&q.stringify([1,2],k,1)=="[\n 1,\n 2\n]"&&q.stringify(new Date(-864E13))=='"-271821-04-20T00:00:00.000Z"'&&q.stringify(new Date(864E13))=='"+275760-09-13T00:00:00.000Z"'&&q.stringify(new Date(-621987552E5))=='"-000001-01-01T00:00:00.000Z"'&&q.stringify(new Date(-1))=='"1969-12-31T23:59:59.999Z"'}catch(f){c=false}}if(!j)return c}if(b=="json-parse"||j){if(typeof q.parse=="function")try{if(q.parse("0")===0&&!q.parse(false)){d= 5 | q.parse('{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}');if(a=d.a.length==5&&d.a[0]==1){try{a=!q.parse('"\t"')}catch(o){}if(a)try{a=q.parse("01")!=1}catch(g){}}}}catch(h){a=false}if(!j)return a}return c&&a}} 6 | if(!R("json")){J||(K=Math.floor,O=[0,31,59,90,120,151,181,212,243,273,304,334],P=function(b,c){return O[c]+365*(b-1970)+K((b-1969+(c=+(c>1)))/4)-K((b-1901+c)/100)+K((b-1601+c)/400)});if(!(m={}.hasOwnProperty))m=function(b){var c={},a;if((c.__proto__=k,c.__proto__={toString:1},c).toString!=l)m=function(a){var b=this.__proto__,a=a in(this.__proto__=k,this);this.__proto__=b;return a};else{a=c.constructor;m=function(b){var c=(this.constructor||a).prototype;return b in this&&!(b in c&&this[b]===c[b])}}c= 7 | k;return m.call(this,b)};n=function(b,c){var a=0,d,j,f;(d=function(){this.valueOf=0}).prototype.valueOf=0;j=new d;for(f in j)m.call(j,f)&&a++;d=j=k;if(a)a=a==2?function(a,b){var c={},d=l.call(a)=="[object Function]",f;for(f in a)!(d&&f=="prototype")&&!m.call(c,f)&&(c[f]=1)&&m.call(a,f)&&b(f)}:function(a,b){var c=l.call(a)=="[object Function]",d,f;for(d in a)!(c&&d=="prototype")&&m.call(a,d)&&!(f=d==="constructor")&&b(d);(f||m.call(a,d="constructor"))&&b(d)};else{j=["valueOf","toString","toLocaleString", 8 | "propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];a=function(a,b){var c=l.call(a)=="[object Function]",d;for(d in a)!(c&&d=="prototype")&&m.call(a,d)&&b(d);for(c=j.length;d=j[--c];m.call(a,d)&&b(d));}}a(b,c)};R("json-stringify")||(r={"\\":"\\\\",'"':'\\"',"\u0008":"\\b","\u000c":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},t=function(b,c){return("000000"+(c||0)).slice(-b)},u=function(b){for(var c='"',a=0,d;d=b.charAt(a);a++)c=c+('\\"\u0008\u000c\n\r\t'.indexOf(d)>-1?r[d]:r[d]=d<" "? 9 | "\\u00"+t(2,d.charCodeAt(0).toString(16)):d);return c+'"'},x=function(b,c,a,d,j,f,o){var g=c[b],h,s,v,w,L,M,N,y,A;if(typeof g=="object"&&g){h=l.call(g);if(h=="[object Date]"&&!m.call(g,"toJSON"))if(g>-1/0&&g<1/0){if(P){v=K(g/864E5);for(h=K(v/365.2425)+1970-1;P(h+1,0)<=v;h++);for(s=K((v-P(h,0))/30.42);P(h,s+1)<=v;s++);v=1+v-P(h,s);w=(g%864E5+864E5)%864E5;L=K(w/36E5)%24;M=K(w/6E4)%60;N=K(w/1E3)%60;w=w%1E3}else{h=g.getUTCFullYear();s=g.getUTCMonth();v=g.getUTCDate();L=g.getUTCHours();M=g.getUTCMinutes(); 10 | N=g.getUTCSeconds();w=g.getUTCMilliseconds()}g=(h<=0||h>=1E4?(h<0?"-":"+")+t(6,h<0?-h:h):t(4,h))+"-"+t(2,s+1)+"-"+t(2,v)+"T"+t(2,L)+":"+t(2,M)+":"+t(2,N)+"."+t(3,w)+"Z"}else g=k;else if(typeof g.toJSON=="function"&&(h!="[object Number]"&&h!="[object String]"&&h!="[object Array]"||m.call(g,"toJSON")))g=g.toJSON(b)}a&&(g=a.call(c,b,g));if(g===k)return"null";h=l.call(g);if(h=="[object Boolean]")return""+g;if(h=="[object Number]")return g>-1/0&&g<1/0?""+g:"null";if(h=="[object String]")return u(g);if(typeof g== 11 | "object"){for(b=o.length;b--;)if(o[b]===g)throw TypeError();o.push(g);y=[];c=f;f=f+j;if(h=="[object Array]"){s=0;for(b=g.length;s0){d="";for(a>10&&(a=10);d.length-1)H++;else{if("{}[]:,".indexOf(a)>-1){H++;return a}if(a=='"'){d="@";for(H++;H-1){d=d+B[a];H++}else if(a=="u"){j=++H;for(f=H+4;H="0"&&a<="9"||a>="a"&&a<="f"||a>="A"&&a<="F"||C()}d=d+z("0x"+b.slice(j,H))}else C()}else{if(a=='"')break; 14 | d=d+a;H++}}if(b.charAt(H)=='"'){H++;return d}}else{j=H;if(a=="-"){o=i;a=b.charAt(++H)}if(a>="0"&&a<="9"){for(a=="0"&&(a=b.charAt(H+1),a>="0"&&a<="9")&&C();H="0"&&a<="9");H++);if(b.charAt(H)=="."){for(f=++H;f="0"&&a<="9");f++);f==H&&C();H=f}a=b.charAt(H);if(a=="e"||a=="E"){a=b.charAt(++H);(a=="+"||a=="-")&&H++;for(f=H;f="0"&&a<="9");f++);f==H&&C();H=f}return+b.slice(j,H)}o&&C();if(b.slice(H,H+4)=="true"){H=H+4;return i}if(b.slice(H,H+5)== 15 | "false"){H=H+5;return false}if(b.slice(H,H+4)=="null"){H=H+4;return k}}C()}}return"$"},E=function(b){var c,a;b=="$"&&C();if(typeof b=="string"){if(b.charAt(0)=="@")return b.slice(1);if(b=="["){for(c=[];;a||(a=i)){b=D();if(b=="]")break;if(a)if(b==","){b=D();b=="]"&&C()}else C();b==","&&C();c.push(E(b))}return c}if(b=="{"){for(c={};;a||(a=i)){b=D();if(b=="}")break;if(a)if(b==","){b=D();b=="}"&&C()}else C();(b==","||typeof b!="string"||b.charAt(0)!="@"||D()!=":")&&C();c[b.slice(1)]=E(D())}return c}C()}return b}, 16 | G=function(b,c,a){a=F(b,c,a);a===e?delete b[c]:b[c]=a},F=function(b,c,a){var d=b[c],j;if(typeof d=="object"&&d)if(l.call(d)=="[object Array]")for(j=d.length;j--;)G(d,j,a);else n(d,function(b){G(d,b,a)});return a.call(b,c,d)},q.parse=function(b,c){var a,d;H=0;I=b;a=E(D());D()!="$"&&C();H=I=k;return c&&l.call(c)=="[object Function]"?F((d={},d[""]=a,d),"",c):a})}p&&define(function(){return q}); 17 | }()); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON 3 # 2 | 3 | ![JSON 3 Logo](http://bestiejs.github.com/json3/page/logo.png) 4 | 5 | **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**. 6 | 7 | - [Development Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.js) *(36.5 KB; uncompressed with comments)* 8 | - [Production Version](http://cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.min.js) *(3.0 KB; compressed and `gzip`-ped)* 9 | 10 | [JSON](http://json.org/) is a language-independent data interchange format based on a loose subset of the JavaScript grammar. Originally popularized by [Douglas Crockford](http://www.crockford.com/), the format was standardized in the [fifth edition](http://es5.github.com/) of the ECMAScript specification. The 5.1 edition, ratified in June 2011, incorporates several modifications to the grammar pertaining to the serialization of dates. 11 | 12 | JSON 3 exposes two functions: `stringify()` for [serializing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/stringify) a JavaScript value to JSON, and `parse()` for [producing](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON/parse) a JavaScript value from a JSON source string. It is a **drop-in replacement** for [JSON 2](http://json.org/js). The functions behave exactly as described in the ECMAScript spec, **except** for the date serialization discrepancy noted below. 13 | 14 | The JSON 3 parser does **not** use `eval` or regular expressions. This provides security and performance benefits in obsolete and mobile environments, where the margin is particularly significant. The complete [benchmark suite](http://jsperf.com/json3) is available on [jsPerf](http://jsperf.com/). 15 | 16 | The project is [hosted on GitHub](http://git.io/json3), along with the [unit tests](http://bestiejs.github.com/json3/test/test_browser.html). It is part of the [BestieJS](https://github.com/bestiejs) family, a collection of best-in-class JavaScript libraries that promote cross-platform support, specification precedents, unit testing, and plenty of documentation. 17 | 18 | # Changes from JSON 2 # 19 | 20 | JSON 3... 21 | 22 | * Correctly serializes primitive wrapper objects. 23 | * Throws a `TypeError` when serializing cyclic structures (JSON 2 recurses until the call stack overflows). 24 | * 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. 25 | 26 | **As of v3.2.3**, JSON 3 is compatible with [Prototype](http://prototypejs.org) 1.6.1 and older. 27 | 28 | In contrast to JSON 2, JSON 3 **does not**... 29 | 30 | * 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. 31 | * Add `toJSON()` or `toISOString()` methods to `Date.prototype`. See the note about date serialization below. 32 | 33 | ## Date Serialization 34 | 35 | **JSON 3 deviates from the specification in one important way**: it does not define `Date#toISOString()` or `Date#toJSON()`. This preserves CommonJS compatibility and avoids polluting native prototypes. Instead, date serialization is performed internally by the `stringify()` implementation: if a date object does not define a custom `toJSON()` method, it is serialized as a [simplified ISO 8601 date-time string](http://es5.github.com/#x15.9.1.15). 36 | 37 | **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. 38 | 39 | Portions of the date serialization code are adapted from the [`date-shim`](https://github.com/Yaffle/date-shim) project. 40 | 41 | # Usage # 42 | 43 | ## Web Browsers 44 | 45 | 46 | 57 | 58 | ## CommonJS Environments 59 | 60 | var JSON3 = require("./path/to/json3"); 61 | JSON3.parse("[1, 2, 3]"); 62 | // => [1, 2, 3] 63 | 64 | ## JavaScript Engines 65 | 66 | load("path/to/json3.js"); 67 | JSON.stringify({"Hello": 123, "Good-bye": 456}, ["Hello"], "\t"); 68 | // => '{\n\t"Hello": 123\n}' 69 | 70 | # Compatibility # 71 | 72 | JSON 3 has been **tested** with the following web browsers, CommonJS environments, and JavaScript engines. 73 | 74 | ## Web Browsers 75 | 76 | - Windows [Internet Explorer](http://www.microsoft.com/windows/internet-explorer), version 6.0 and higher 77 | - Mozilla [Firefox](http://www.mozilla.com/firefox), version 1.0 and higher 78 | - Apple [Safari](http://www.apple.com/safari), version 2.0 and higher 79 | - [Opera](http://www.opera.com) 7.02 and higher 80 | - [Mozilla](http://sillydog.org/narchive/gecko.php) 1.0, [Netscape](http://sillydog.org/narchive/) 6.2.3, and [SeaMonkey](http://www.seamonkey-project.org/) 1.0 and higher 81 | 82 | ## CommonJS Environments 83 | 84 | - [Node](http://nodejs.org/) 0.2.6 and higher 85 | - [RingoJS](http://ringojs.org/) 0.4 and higher 86 | - [Narwhal](http://narwhaljs.org/) 0.3.2 and higher 87 | 88 | ## JavaScript Engines 89 | 90 | - Mozilla [Rhino](http://www.mozilla.org/rhino) 1.5R5 and higher 91 | - WebKit [JSC](https://trac.webkit.org/wiki/JSC) 92 | - Google [V8](http://code.google.com/p/v8) 93 | 94 | ## Known Incompatibilities 95 | 96 | * 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))`. 97 | 98 | ## Required Native Methods 99 | 100 | JSON 3 assumes that the following methods exist and function as described in the ECMAScript specification: 101 | 102 | - The `Number`, `String`, `Array`, `Object`, `Date`, `SyntaxError`, and `TypeError` constructors. 103 | - `String.fromCharCode` 104 | - `Object#toString` 105 | - `Function#call` 106 | - `Math.floor` 107 | - `Number#toString` 108 | - `Date#valueOf` 109 | - `String.prototype`: `indexOf`, `charCodeAt`, `charAt`, `slice`. 110 | - `Array.prototype`: `push`, `pop`, `join`. 111 | 112 | # Contribute # 113 | 114 | Check out a working copy of the JSON 3 source code with [Git](http://git-scm.com/): 115 | 116 | $ git clone git://github.com/bestiejs/json3.git 117 | $ cd json3 118 | $ git submodule update --init 119 | 120 | If you'd like to contribute a feature or bug fix, you can [fork](http://help.github.com/fork-a-repo/) JSON 3, commit your changes, and [send a pull request](http://help.github.com/send-pull-requests/). Please make sure to update the unit tests in the `test` directory as well. 121 | 122 | Alternatively, you can use the [GitHub issue tracker](https://github.com/bestiejs/json3/issues) to submit bug reports, feature requests, and questions, or send tweets to [@kitcambridge](http://twitter.com/kitcambridge). 123 | 124 | JSON 3 is released under the [MIT License](http://kit.mit-license.org/). -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSON 3 6 | 7 | 8 | 9 | 17 |
18 |

JSON 3

19 |

JSON 3 Logo 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 | }()); --------------------------------------------------------------------------------