├── LICENSE ├── README.md ├── app.build.js ├── examples-build ├── as-globals.html ├── as-modules-only.html ├── as-modules.html ├── build-example.html ├── build.txt ├── js │ ├── build-example.js │ ├── cheese.js │ ├── config.js │ ├── lib │ │ ├── candy.js │ │ ├── donut.js │ │ ├── frosting.js │ │ ├── nachos.js │ │ ├── r.js │ │ ├── require.js │ │ ├── rolls.js │ │ ├── sugar.js │ │ ├── text.js │ │ └── wheat.js │ └── pizza.js └── use-path-option.html ├── examples ├── as-globals.html ├── as-modules-only.html ├── as-modules.html ├── build-example.html ├── js │ ├── build-example.js │ ├── cheese.js │ ├── config.js │ ├── lib │ │ ├── candy.js │ │ ├── donut.js │ │ ├── frosting.js │ │ ├── ketchup.js │ │ ├── nachos.js │ │ ├── r.js │ │ ├── require.js │ │ ├── rolls.js │ │ ├── sugar.js │ │ ├── tasty.hamburger.js │ │ ├── text.js │ │ └── wheat.js │ └── pizza.js └── use-path-option.html ├── spec ├── browser │ └── wrapjs-test.js ├── buster.js └── module │ ├── amd-test.js │ ├── amd-test2.js │ └── amd-test3.js └── wrap.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (c) 2012 Dave Geddes 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | ## This plugin's wildest dreams came true and its functionality was added to RequireJS 2.0
Please upgrade your RequireJS and use its new [shim config](http://requirejs.org/docs/api.html#config-shim) instead of wrap.js 3 | 4 | 5 | # wrap.js 6 | version 0.2.2 7 | A RequireJS plugin for wrapping regular scripts as AMD modules. 8 | This is useful for cases where regular scripts have dependencies, like jQuery plugins and Backbone. 9 | Depends on the [text plugin](http://requirejs.org/docs/api.html#text) 10 | 11 | ## Usage 12 | In your require.config use the wrapJS object to specify which scripts you want to wrap. 13 | Use the `deps` attribute to specify all the script's dependencies. 14 | Use the `attach` attribute to specify the global name of the library (think $, _, Backbone, etc) 15 | Use the `path` attribute to specify the location of the script to wrap. Alternatively use require's regular path config. 16 | The following example wraps backbone, declaring underscore and jQuery as dependencies: 17 | 18 | 19 | ```js 20 | require.config({ 21 | wrapJS: { 22 | "backbone": { 23 | deps: ["underscore", "jquery"], //an array of the script's dependencies 24 | attach: "Backbone" 25 | } 26 | }); 27 | 28 | ``` 29 | 30 | Now use the wrap plugin to load the backbone (and its dependencies) like so: 31 | 32 | ```js 33 | require('wrap!backbone', function(Backbone){ 34 | console.log(Backbone); //now returns Backbone, just as if Backbone were an AMD module 35 | } 36 | ``` 37 | 38 | NOTE: As of RequireJS 1.0.5 you can reuse your app's main.js config as part of your build config, by using the new mainConfigFile setting: `mainConfigFile:'path/to/main.js'` So you only have to specify the wrapJS stuff once. Here's a sample [app.build.js](https://github.com/geddesign/wrapjs/blob/master/app.build.js). 39 | 40 | ## How It Works 41 | 42 | During development, wrap.js basically does a nested `require()` for you, so that the script's dependencies are sure to load first. Then when you run the r.js build, an actual AMD module is generated based on your config. So say you were trying to wrap Backbone.js, and the Backbone.js script looks like this: 43 | 44 | ```js 45 | (function(){ 46 | //stuff 47 | }).call(this); 48 | ``` 49 | 50 | Now after the r.js build backbone looks like this: 51 | 52 | ```js 53 | define('wrap!backbone', ['jquery', 'underscore'], function(){ 54 | (function(){ 55 | //backbone stuff 56 | }).call(this); 57 | return (function () { 58 | var attach = Backbone; 59 | return (typeof attach === 'function') ? attach.apply(this) : attach; 60 | }()); 61 | }); 62 | ``` 63 | 64 | NOTE: the generated AMD module contains an immediate function that resolves to and returns Backbone in the example above. Pure JS magic. 65 | 66 | So there you have it. The wrap.js plugin turns any old script into an AMD module with just a little config. 67 | 68 | ## More Examples 69 | I've included several examples in the `examples` directory. 70 | 71 | ### Use as Globals 72 | By default the wrapped scripts are still available as globals. See [as-globals.html](https://github.com/geddesign/wrapjs/blob/master/examples/as-globals.html). 73 | 74 | ### Use as Modules 75 | The wrapped scripts are also returned to your callback just like AMD modules. See [as-modules.html](https://github.com/geddesign/wrapjs/blob/master/examples/as-modules.html). 76 | 77 | ### Remove Globals 78 | If you want to have the wrapped scripts no longer available as globals, you can remove the global—and do any other custom things—in a function you pass to the `attach` property. See [as-modules-only.html](https://github.com/geddesign/wrapjs/blob/master/examples/as-modules-only.html). 79 | 80 | ### Using the path option 81 | Using the `path` attribute can help keep your config small. See [use-path-option.html](https://github.com/geddesign/wrapjs/blob/master/examples/use-path-option.html) 82 | 83 | ### Post-build example 84 | To see an example of a generated module, see the build-example.js [before](https://github.com/geddesign/wrapjs/blob/master/examples/js/build-example.js) and [after](https://github.com/geddesign/wrapjs/blob/master/examples-build/js/build-example.js). 85 | -------------------------------------------------------------------------------- /app.build.js: -------------------------------------------------------------------------------- 1 | ({ 2 | appDir:"examples/", 3 | baseUrl: "js/", 4 | dir:"examples-build/", 5 | optimize: 'none', //for sake of example don't compress 6 | mainConfigFile:'examples/js/config.js', 7 | modules:[ 8 | { name:"build-example", 9 | //exluding config for the sake of testing. 10 | exclude: ['text', 'wrap', 'config'] 11 | } 12 | ], 13 | findNestedDependencies:true 14 | }) -------------------------------------------------------------------------------- /examples-build/as-globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | keep as globals 5 | 6 | 27 | 28 | -------------------------------------------------------------------------------- /examples-build/as-modules-only.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | turn into modules with no more globals 5 | 6 | 35 | 36 | -------------------------------------------------------------------------------- /examples-build/as-modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | turn scripts into modules 5 | 6 | 31 | 32 | -------------------------------------------------------------------------------- /examples-build/build-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | load the build-time generated modules 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples-build/build.txt: -------------------------------------------------------------------------------- 1 | 2 | js/build-example.js 3 | ---------------- 4 | wrap!frosting 5 | wrap!donut 6 | js/lib/frosting.js 7 | js/build-example.js 8 | -------------------------------------------------------------------------------- /examples-build/js/build-example.js: -------------------------------------------------------------------------------- 1 | 2 | /* script wrapped by the wrap! plugin */ 3 | define("wrap!frosting", [], function(){ 4 | frosting = { 5 | name:"frosting" 6 | }; 7 | return (function () { 8 | var attach = frosting; 9 | return (typeof attach === 'function') ? attach.apply(this) : attach; 10 | }()); 11 | }); 12 | 13 | /* script wrapped by the wrap! plugin */ 14 | define("wrap!donut", ["wrap!frosting"], function(){ 15 | donut = { 16 | name: "donut", 17 | ingredients: [frosting] //depends on the global sugar.js being available 18 | }; 19 | return (function () { 20 | var attach = function (){var donut=this.donut;delete this.donut;return donut}; 21 | return (typeof attach === 'function') ? attach.apply(this) : attach; 22 | }()); 23 | }); 24 | 25 | //NOTE: I commented this out for the sake of loading this file during the tests. 26 | 27 | // //see config.js for the wrapJS config being used here 28 | // require(['require', 'config'], function(require){ 29 | // // requiring donut will now make sure frosting is loaded first 30 | // require(['wrap!donut'], function (donut) { 31 | // console.log("donut: ", donut); 32 | // // notice that the scripts are available as parameters to this callback, as if it were an AMD module 33 | // console.log('mmm', donut.name); 34 | // console.log('mmm', donut.ingredients[0].name); 35 | 36 | // // also notice the donut variable is no longer available as a global 37 | // console.log('look no more global: ', window.donut); 38 | // }); 39 | // }); 40 | // define("build-example", function(){}); 41 | -------------------------------------------------------------------------------- /examples-build/js/cheese.js: -------------------------------------------------------------------------------- 1 | cheese = { 2 | name:"cheese" 3 | }; -------------------------------------------------------------------------------- /examples-build/js/config.js: -------------------------------------------------------------------------------- 1 | //wrap the scripts that have dependencies, specify what to return for the AMD module, and remove the script's global 2 | require.config({ 3 | paths:{ 'wrap':'../../wrap', 'text':'lib/text'}, 4 | wrapJS:{ 5 | 'donut':{ 6 | deps:['wrap!frosting'], 7 | attach:function () { 8 | var donut = this.donut; // this === window (global) 9 | delete this.donut; //kill the global 10 | return donut; //tell the module what to return 11 | }, 12 | path: 'lib/donut' 13 | } 14 | ,'frosting': { 15 | attach: 'frosting', 16 | path: 'lib/frosting' 17 | } 18 | } 19 | }); -------------------------------------------------------------------------------- /examples-build/js/lib/candy.js: -------------------------------------------------------------------------------- 1 | candy = { 2 | name: "candy", 3 | ingredients: [sugar] //depends on the global sugar.js being available 4 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/donut.js: -------------------------------------------------------------------------------- 1 | donut = { 2 | name: "donut", 3 | ingredients: [frosting] //depends on the global sugar.js being available 4 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/frosting.js: -------------------------------------------------------------------------------- 1 | frosting = { 2 | name:"frosting" 3 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/nachos.js: -------------------------------------------------------------------------------- 1 | var nachos = { 2 | name: 'nachos', 3 | ingredients: [cheese] //depends on the global cheese.js being available 4 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/require.js: -------------------------------------------------------------------------------- 1 | /** vim: et:ts=4:sw=4:sts=4 2 | * @license RequireJS 1.0.7 Copyright (c) 2010-2012, 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, sub: true */ 7 | /*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ 8 | var requirejs, require, define; 9 | (function () { 10 | //Change this version number for each release. 11 | var version = "1.0.7", 12 | commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, 13 | cjsRequireRegExp = /require\(\s*["']([^'"\s]+)["']\s*\)/g, 14 | currDirRegExp = /^\.\//, 15 | jsSuffixRegExp = /\.js$/, 16 | ostring = Object.prototype.toString, 17 | ap = Array.prototype, 18 | aps = ap.slice, 19 | apsp = ap.splice, 20 | isBrowser = !!(typeof window !== "undefined" && navigator && document), 21 | isWebWorker = !isBrowser && typeof importScripts !== "undefined", 22 | //PS3 indicates loaded and complete, but need to wait for complete 23 | //specifically. Sequence is "loading", "loaded", execution, 24 | // then "complete". The UA check is unfortunate, but not sure how 25 | //to feature test w/o causing perf issues. 26 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? 27 | /^complete$/ : /^(complete|loaded)$/, 28 | defContextName = "_", 29 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason. 30 | isOpera = typeof opera !== "undefined" && opera.toString() === "[object Opera]", 31 | empty = {}, 32 | contexts = {}, 33 | globalDefQueue = [], 34 | interactiveScript = null, 35 | checkLoadedDepth = 0, 36 | useInteractive = false, 37 | reservedDependencies = { 38 | require: true, 39 | module: true, 40 | exports: true 41 | }, 42 | req, cfg = {}, currentlyAddingScript, s, head, baseElement, scripts, script, 43 | src, subPath, mainScript, dataMain, globalI, ctx, jQueryCheck, checkLoadedTimeoutId; 44 | 45 | function isFunction(it) { 46 | return ostring.call(it) === "[object Function]"; 47 | } 48 | 49 | function isArray(it) { 50 | return ostring.call(it) === "[object Array]"; 51 | } 52 | 53 | /** 54 | * Simple function to mix in properties from source into target, 55 | * but only if target does not already have a property of the same name. 56 | * This is not robust in IE for transferring methods that match 57 | * Object.prototype names, but the uses of mixin here seem unlikely to 58 | * trigger a problem related to that. 59 | */ 60 | function mixin(target, source, force) { 61 | for (var prop in source) { 62 | if (!(prop in empty) && (!(prop in target) || force)) { 63 | target[prop] = source[prop]; 64 | } 65 | } 66 | return req; 67 | } 68 | 69 | /** 70 | * Constructs an error with a pointer to an URL with more information. 71 | * @param {String} id the error ID that maps to an ID on a web page. 72 | * @param {String} message human readable error. 73 | * @param {Error} [err] the original error, if there is one. 74 | * 75 | * @returns {Error} 76 | */ 77 | function makeError(id, msg, err) { 78 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); 79 | if (err) { 80 | e.originalError = err; 81 | } 82 | return e; 83 | } 84 | 85 | /** 86 | * Used to set up package paths from a packagePaths or packages config object. 87 | * @param {Object} pkgs the object to store the new package config 88 | * @param {Array} currentPackages an array of packages to configure 89 | * @param {String} [dir] a prefix dir to use. 90 | */ 91 | function configurePackageDir(pkgs, currentPackages, dir) { 92 | var i, location, pkgObj; 93 | 94 | for (i = 0; (pkgObj = currentPackages[i]); i++) { 95 | pkgObj = typeof pkgObj === "string" ? { name: pkgObj } : pkgObj; 96 | location = pkgObj.location; 97 | 98 | //Add dir to the path, but avoid paths that start with a slash 99 | //or have a colon (indicates a protocol) 100 | if (dir && (!location || (location.indexOf("/") !== 0 && location.indexOf(":") === -1))) { 101 | location = dir + "/" + (location || pkgObj.name); 102 | } 103 | 104 | //Create a brand new object on pkgs, since currentPackages can 105 | //be passed in again, and config.pkgs is the internal transformed 106 | //state for all package configs. 107 | pkgs[pkgObj.name] = { 108 | name: pkgObj.name, 109 | location: location || pkgObj.name, 110 | //Remove leading dot in main, so main paths are normalized, 111 | //and remove any trailing .js, since different package 112 | //envs have different conventions: some use a module name, 113 | //some use a file name. 114 | main: (pkgObj.main || "main") 115 | .replace(currDirRegExp, '') 116 | .replace(jsSuffixRegExp, '') 117 | }; 118 | } 119 | } 120 | 121 | /** 122 | * jQuery 1.4.3-1.5.x use a readyWait/ready() pairing to hold DOM 123 | * ready callbacks, but jQuery 1.6 supports a holdReady() API instead. 124 | * At some point remove the readyWait/ready() support and just stick 125 | * with using holdReady. 126 | */ 127 | function jQueryHoldReady($, shouldHold) { 128 | if ($.holdReady) { 129 | $.holdReady(shouldHold); 130 | } else if (shouldHold) { 131 | $.readyWait += 1; 132 | } else { 133 | $.ready(true); 134 | } 135 | } 136 | 137 | if (typeof define !== "undefined") { 138 | //If a define is already in play via another AMD loader, 139 | //do not overwrite. 140 | // return; 141 | } 142 | 143 | if (typeof requirejs !== "undefined") { 144 | if (isFunction(requirejs)) { 145 | //Do not overwrite and existing requirejs instance. 146 | return; 147 | } else { 148 | cfg = requirejs; 149 | requirejs = undefined; 150 | } 151 | } 152 | 153 | //Allow for a require config object 154 | if (typeof require !== "undefined" && !isFunction(require)) { 155 | //assume it is a config object. 156 | cfg = require; 157 | require = undefined; 158 | } 159 | 160 | /** 161 | * Creates a new context for use in require and define calls. 162 | * Handle most of the heavy lifting. Do not want to use an object 163 | * with prototype here to avoid using "this" in require, in case it 164 | * needs to be used in more super secure envs that do not want this. 165 | * Also there should not be that many contexts in the page. Usually just 166 | * one for the default context, but could be extra for multiversion cases 167 | * or if a package needs a special context for a dependency that conflicts 168 | * with the standard context. 169 | */ 170 | function newContext(contextName) { 171 | var context, resume, 172 | config = { 173 | waitSeconds: 7, 174 | baseUrl: "./", 175 | paths: {}, 176 | pkgs: {}, 177 | catchError: {} 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 | urlFetched = {}, 191 | managerCounter = 0, 192 | managerCallbacks = {}, 193 | plugins = {}, 194 | //Used to indicate which modules in a build scenario 195 | //need to be full executed. 196 | needFullExec = {}, 197 | fullExec = {}, 198 | resumeDepth = 0; 199 | 200 | /** 201 | * Trims the . and .. from an array of path segments. 202 | * It will keep a leading path segment if a .. will become 203 | * the first path segment, to help with module name lookups, 204 | * which act like paths, but can be remapped. But the end result, 205 | * all paths that use this function should look normalized. 206 | * NOTE: this method MODIFIES the input array. 207 | * @param {Array} ary the array of path segments. 208 | */ 209 | function trimDots(ary) { 210 | var i, part; 211 | for (i = 0; (part = ary[i]); i++) { 212 | if (part === ".") { 213 | ary.splice(i, 1); 214 | i -= 1; 215 | } else if (part === "..") { 216 | if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { 217 | //End of the line. Keep at least one non-dot 218 | //path segment at the front so it can be mapped 219 | //correctly to disk. Otherwise, there is likely 220 | //no path mapping for a path starting with '..'. 221 | //This can still fail, but catches the most reasonable 222 | //uses of .. 223 | break; 224 | } else if (i > 0) { 225 | ary.splice(i - 1, 2); 226 | i -= 2; 227 | } 228 | } 229 | } 230 | } 231 | 232 | /** 233 | * Given a relative module name, like ./something, normalize it to 234 | * a real name that can be mapped to a path. 235 | * @param {String} name the relative name 236 | * @param {String} baseName a real name that the name arg is relative 237 | * to. 238 | * @returns {String} normalized name 239 | */ 240 | function normalize(name, baseName) { 241 | var pkgName, pkgConfig; 242 | 243 | //Adjust any relative paths. 244 | if (name && name.charAt(0) === ".") { 245 | //If have a base name, try to normalize against it, 246 | //otherwise, assume it is a top-level require that will 247 | //be relative to baseUrl in the end. 248 | if (baseName) { 249 | if (config.pkgs[baseName]) { 250 | //If the baseName is a package name, then just treat it as one 251 | //name to concat the name with. 252 | baseName = [baseName]; 253 | } else { 254 | //Convert baseName to array, and lop off the last part, 255 | //so that . matches that "directory" and not name of the baseName's 256 | //module. For instance, baseName of "one/two/three", maps to 257 | //"one/two/three.js", but we want the directory, "one/two" for 258 | //this normalization. 259 | baseName = baseName.split("/"); 260 | baseName = baseName.slice(0, baseName.length - 1); 261 | } 262 | 263 | name = baseName.concat(name.split("/")); 264 | trimDots(name); 265 | 266 | //Some use of packages may use a . path to reference the 267 | //"main" module name, so normalize for that. 268 | pkgConfig = config.pkgs[(pkgName = name[0])]; 269 | name = name.join("/"); 270 | if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { 271 | name = pkgName; 272 | } 273 | } else if (name.indexOf("./") === 0) { 274 | // No baseName, so this is ID is resolved relative 275 | // to baseUrl, pull off the leading dot. 276 | name = name.substring(2); 277 | } 278 | } 279 | return name; 280 | } 281 | 282 | /** 283 | * Creates a module mapping that includes plugin prefix, module 284 | * name, and path. If parentModuleMap is provided it will 285 | * also normalize the name via require.normalize() 286 | * 287 | * @param {String} name the module name 288 | * @param {String} [parentModuleMap] parent module map 289 | * for the module name, used to resolve relative names. 290 | * 291 | * @returns {Object} 292 | */ 293 | function makeModuleMap(name, parentModuleMap) { 294 | var index = name ? name.indexOf("!") : -1, 295 | prefix = null, 296 | parentName = parentModuleMap ? parentModuleMap.name : null, 297 | originalName = name, 298 | normalizedName, url, pluginModule; 299 | 300 | if (index !== -1) { 301 | prefix = name.substring(0, index); 302 | name = name.substring(index + 1, name.length); 303 | } 304 | 305 | if (prefix) { 306 | prefix = normalize(prefix, parentName); 307 | } 308 | 309 | //Account for relative paths if there is a base name. 310 | if (name) { 311 | if (prefix) { 312 | pluginModule = defined[prefix]; 313 | if (pluginModule && pluginModule.normalize) { 314 | //Plugin is loaded, use its normalize method. 315 | normalizedName = pluginModule.normalize(name, function (name) { 316 | return normalize(name, parentName); 317 | }); 318 | } else { 319 | normalizedName = normalize(name, parentName); 320 | } 321 | } else { 322 | //A regular module. 323 | normalizedName = normalize(name, parentName); 324 | 325 | url = urlMap[normalizedName]; 326 | if (!url) { 327 | //Calculate url for the module, if it has a name. 328 | //Use name here since nameToUrl also calls normalize, 329 | //and for relative names that are outside the baseUrl 330 | //this causes havoc. Was thinking of just removing 331 | //parentModuleMap to avoid extra normalization, but 332 | //normalize() still does a dot removal because of 333 | //issue #142, so just pass in name here and redo 334 | //the normalization. Paths outside baseUrl are just 335 | //messy to support. 336 | url = context.nameToUrl(name, null, parentModuleMap); 337 | 338 | //Store the URL mapping for later. 339 | urlMap[normalizedName] = url; 340 | } 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 | function makeContextModuleFunc(func, relModuleMap, enableBuildCallback) { 376 | return function () { 377 | //A version of a require function that passes a moduleName 378 | //value for items that may need to 379 | //look up paths relative to the moduleName 380 | var args = aps.call(arguments, 0), lastArg; 381 | if (enableBuildCallback && 382 | isFunction((lastArg = args[args.length - 1]))) { 383 | lastArg.__requireJsBuild = true; 384 | } 385 | args.push(relModuleMap); 386 | return func.apply(null, args); 387 | }; 388 | } 389 | 390 | /** 391 | * Helper function that creates a require function object to give to 392 | * modules that ask for it as a dependency. It needs to be specific 393 | * per module because of the implication of path mappings that may 394 | * need to be relative to the module name. 395 | */ 396 | function makeRequire(relModuleMap, enableBuildCallback, altRequire) { 397 | var modRequire = makeContextModuleFunc(altRequire || context.require, relModuleMap, enableBuildCallback); 398 | 399 | mixin(modRequire, { 400 | nameToUrl: makeContextModuleFunc(context.nameToUrl, relModuleMap), 401 | toUrl: makeContextModuleFunc(context.toUrl, relModuleMap), 402 | defined: makeContextModuleFunc(context.requireDefined, relModuleMap), 403 | specified: makeContextModuleFunc(context.requireSpecified, relModuleMap), 404 | isBrowser: req.isBrowser 405 | }); 406 | return modRequire; 407 | } 408 | 409 | /* 410 | * Queues a dependency for checking after the loader is out of a 411 | * "paused" state, for example while a script file is being loaded 412 | * in the browser, where it may have many modules defined in it. 413 | */ 414 | function queueDependency(manager) { 415 | context.paused.push(manager); 416 | } 417 | 418 | function execManager(manager) { 419 | var i, ret, err, errFile, errModuleTree, 420 | cb = manager.callback, 421 | map = manager.map, 422 | fullName = map.fullName, 423 | args = manager.deps, 424 | listeners = manager.listeners, 425 | cjsModule; 426 | 427 | //Call the callback to define the module, if necessary. 428 | if (cb && isFunction(cb)) { 429 | if (config.catchError.define) { 430 | try { 431 | ret = req.execCb(fullName, manager.callback, args, defined[fullName]); 432 | } catch (e) { 433 | err = e; 434 | } 435 | } else { 436 | ret = req.execCb(fullName, manager.callback, args, defined[fullName]); 437 | } 438 | 439 | if (fullName) { 440 | //If setting exports via "module" is in play, 441 | //favor that over return value and exports. After that, 442 | //favor a non-undefined return value over exports use. 443 | cjsModule = manager.cjsModule; 444 | if (cjsModule && 445 | cjsModule.exports !== undefined && 446 | //Make sure it is not already the exports value 447 | cjsModule.exports !== defined[fullName]) { 448 | ret = defined[fullName] = manager.cjsModule.exports; 449 | } else if (ret === undefined && manager.usingExports) { 450 | //exports already set the defined value. 451 | ret = defined[fullName]; 452 | } else { 453 | //Use the return value from the function. 454 | defined[fullName] = ret; 455 | //If this module needed full execution in a build 456 | //environment, mark that now. 457 | if (needFullExec[fullName]) { 458 | fullExec[fullName] = true; 459 | } 460 | } 461 | } 462 | } else if (fullName) { 463 | //May just be an object definition for the module. Only 464 | //worry about defining if have a module name. 465 | ret = defined[fullName] = cb; 466 | 467 | //If this module needed full execution in a build 468 | //environment, mark that now. 469 | if (needFullExec[fullName]) { 470 | fullExec[fullName] = true; 471 | } 472 | } 473 | 474 | //Clean up waiting. Do this before error calls, and before 475 | //calling back listeners, so that bookkeeping is correct 476 | //in the event of an error and error is reported in correct order, 477 | //since the listeners will likely have errors if the 478 | //onError function does not throw. 479 | if (waiting[manager.id]) { 480 | delete waiting[manager.id]; 481 | manager.isDone = true; 482 | context.waitCount -= 1; 483 | if (context.waitCount === 0) { 484 | //Clear the wait array used for cycles. 485 | waitAry = []; 486 | } 487 | } 488 | 489 | //Do not need to track manager callback now that it is defined. 490 | delete managerCallbacks[fullName]; 491 | 492 | //Allow instrumentation like the optimizer to know the order 493 | //of modules executed and their dependencies. 494 | if (req.onResourceLoad && !manager.placeholder) { 495 | req.onResourceLoad(context, map, manager.depArray); 496 | } 497 | 498 | if (err) { 499 | errFile = (fullName ? makeModuleMap(fullName).url : '') || 500 | err.fileName || err.sourceURL; 501 | errModuleTree = err.moduleTree; 502 | err = makeError('defineerror', 'Error evaluating ' + 503 | 'module "' + fullName + '" at location "' + 504 | errFile + '":\n' + 505 | err + '\nfileName:' + errFile + 506 | '\nlineNumber: ' + (err.lineNumber || err.line), err); 507 | err.moduleName = fullName; 508 | err.moduleTree = errModuleTree; 509 | return req.onError(err); 510 | } 511 | 512 | //Let listeners know of this manager's value. 513 | for (i = 0; (cb = listeners[i]); i++) { 514 | cb(ret); 515 | } 516 | 517 | return undefined; 518 | } 519 | 520 | /** 521 | * Helper that creates a callack function that is called when a dependency 522 | * is ready, and sets the i-th dependency for the manager as the 523 | * value passed to the callback generated by this function. 524 | */ 525 | function makeArgCallback(manager, i) { 526 | return function (value) { 527 | //Only do the work if it has not been done 528 | //already for a dependency. Cycle breaking 529 | //logic in forceExec could mean this function 530 | //is called more than once for a given dependency. 531 | if (!manager.depDone[i]) { 532 | manager.depDone[i] = true; 533 | manager.deps[i] = value; 534 | manager.depCount -= 1; 535 | if (!manager.depCount) { 536 | //All done, execute! 537 | execManager(manager); 538 | } 539 | } 540 | }; 541 | } 542 | 543 | function callPlugin(pluginName, depManager) { 544 | var map = depManager.map, 545 | fullName = map.fullName, 546 | name = map.name, 547 | plugin = plugins[pluginName] || 548 | (plugins[pluginName] = defined[pluginName]), 549 | load; 550 | 551 | //No need to continue if the manager is already 552 | //in the process of loading. 553 | if (depManager.loading) { 554 | return; 555 | } 556 | depManager.loading = true; 557 | 558 | load = function (ret) { 559 | depManager.callback = function () { 560 | return ret; 561 | }; 562 | execManager(depManager); 563 | 564 | loaded[depManager.id] = true; 565 | 566 | //The loading of this plugin 567 | //might have placed other things 568 | //in the paused queue. In particular, 569 | //a loader plugin that depends on 570 | //a different plugin loaded resource. 571 | resume(); 572 | }; 573 | 574 | //Allow plugins to load other code without having to know the 575 | //context or how to "complete" the load. 576 | load.fromText = function (moduleName, text) { 577 | /*jslint evil: true */ 578 | var hasInteractive = useInteractive; 579 | 580 | //Indicate a the module is in process of loading. 581 | loaded[moduleName] = false; 582 | context.scriptCount += 1; 583 | 584 | //Indicate this is not a "real" module, so do not track it 585 | //for builds, it does not map to a real file. 586 | context.fake[moduleName] = true; 587 | 588 | //Turn off interactive script matching for IE for any define 589 | //calls in the text, then turn it back on at the end. 590 | if (hasInteractive) { 591 | useInteractive = false; 592 | } 593 | 594 | req.exec(text); 595 | 596 | if (hasInteractive) { 597 | useInteractive = true; 598 | } 599 | 600 | //Support anonymous modules. 601 | context.completeLoad(moduleName); 602 | }; 603 | 604 | //No need to continue if the plugin value has already been 605 | //defined by a build. 606 | if (fullName in defined) { 607 | load(defined[fullName]); 608 | } else { 609 | //Use parentName here since the plugin's name is not reliable, 610 | //could be some weird string with no path that actually wants to 611 | //reference the parentName's path. 612 | plugin.load(name, makeRequire(map.parentMap, true, function (deps, cb) { 613 | var moduleDeps = [], 614 | i, dep, depMap; 615 | //Convert deps to full names and hold on to them 616 | //for reference later, when figuring out if they 617 | //are blocked by a circular dependency. 618 | for (i = 0; (dep = deps[i]); i++) { 619 | depMap = makeModuleMap(dep, map.parentMap); 620 | deps[i] = depMap.fullName; 621 | if (!depMap.prefix) { 622 | moduleDeps.push(deps[i]); 623 | } 624 | } 625 | depManager.moduleDeps = (depManager.moduleDeps || []).concat(moduleDeps); 626 | return context.require(deps, cb); 627 | }), load, config); 628 | } 629 | } 630 | 631 | /** 632 | * Adds the manager to the waiting queue. Only fully 633 | * resolved items should be in the waiting queue. 634 | */ 635 | function addWait(manager) { 636 | if (!waiting[manager.id]) { 637 | waiting[manager.id] = manager; 638 | waitAry.push(manager); 639 | context.waitCount += 1; 640 | } 641 | } 642 | 643 | /** 644 | * Function added to every manager object. Created out here 645 | * to avoid new function creation for each manager instance. 646 | */ 647 | function managerAdd(cb) { 648 | this.listeners.push(cb); 649 | } 650 | 651 | function getManager(map, shouldQueue) { 652 | var fullName = map.fullName, 653 | prefix = map.prefix, 654 | plugin = prefix ? plugins[prefix] || 655 | (plugins[prefix] = defined[prefix]) : null, 656 | manager, created, pluginManager, prefixMap; 657 | 658 | if (fullName) { 659 | manager = managerCallbacks[fullName]; 660 | } 661 | 662 | if (!manager) { 663 | created = true; 664 | manager = { 665 | //ID is just the full name, but if it is a plugin resource 666 | //for a plugin that has not been loaded, 667 | //then add an ID counter to it. 668 | id: (prefix && !plugin ? 669 | (managerCounter++) + '__p@:' : '') + 670 | (fullName || '__r@' + (managerCounter++)), 671 | map: map, 672 | depCount: 0, 673 | depDone: [], 674 | depCallbacks: [], 675 | deps: [], 676 | listeners: [], 677 | add: managerAdd 678 | }; 679 | 680 | specified[manager.id] = true; 681 | 682 | //Only track the manager/reuse it if this is a non-plugin 683 | //resource. Also only track plugin resources once 684 | //the plugin has been loaded, and so the fullName is the 685 | //true normalized value. 686 | if (fullName && (!prefix || plugins[prefix])) { 687 | managerCallbacks[fullName] = manager; 688 | } 689 | } 690 | 691 | //If there is a plugin needed, but it is not loaded, 692 | //first load the plugin, then continue on. 693 | if (prefix && !plugin) { 694 | prefixMap = makeModuleMap(prefix); 695 | 696 | //Clear out defined and urlFetched if the plugin was previously 697 | //loaded/defined, but not as full module (as in a build 698 | //situation). However, only do this work if the plugin is in 699 | //defined but does not have a module export value. 700 | if (prefix in defined && !defined[prefix]) { 701 | delete defined[prefix]; 702 | delete urlFetched[prefixMap.url]; 703 | } 704 | 705 | pluginManager = getManager(prefixMap, true); 706 | pluginManager.add(function (plugin) { 707 | //Create a new manager for the normalized 708 | //resource ID and have it call this manager when 709 | //done. 710 | var newMap = makeModuleMap(map.originalName, map.parentMap), 711 | normalizedManager = getManager(newMap, true); 712 | 713 | //Indicate this manager is a placeholder for the real, 714 | //normalized thing. Important for when trying to map 715 | //modules and dependencies, for instance, in a build. 716 | manager.placeholder = true; 717 | 718 | normalizedManager.add(function (resource) { 719 | manager.callback = function () { 720 | return resource; 721 | }; 722 | execManager(manager); 723 | }); 724 | }); 725 | } else if (created && shouldQueue) { 726 | //Indicate the resource is not loaded yet if it is to be 727 | //queued. 728 | loaded[manager.id] = false; 729 | queueDependency(manager); 730 | addWait(manager); 731 | } 732 | 733 | return manager; 734 | } 735 | 736 | function main(inName, depArray, callback, relModuleMap) { 737 | var moduleMap = makeModuleMap(inName, relModuleMap), 738 | name = moduleMap.name, 739 | fullName = moduleMap.fullName, 740 | manager = getManager(moduleMap), 741 | id = manager.id, 742 | deps = manager.deps, 743 | i, depArg, depName, depPrefix, cjsMod; 744 | 745 | if (fullName) { 746 | //If module already defined for context, or already loaded, 747 | //then leave. Also leave if jQuery is registering but it does 748 | //not match the desired version number in the config. 749 | if (fullName in defined || loaded[id] === true || 750 | (fullName === "jquery" && config.jQuery && 751 | config.jQuery !== callback().fn.jquery)) { 752 | return; 753 | } 754 | 755 | //Set specified/loaded here for modules that are also loaded 756 | //as part of a layer, where onScriptLoad is not fired 757 | //for those cases. Do this after the inline define and 758 | //dependency tracing is done. 759 | specified[id] = true; 760 | loaded[id] = true; 761 | 762 | //If module is jQuery set up delaying its dom ready listeners. 763 | if (fullName === "jquery" && callback) { 764 | jQueryCheck(callback()); 765 | } 766 | } 767 | 768 | //Attach real depArray and callback to the manager. Do this 769 | //only if the module has not been defined already, so do this after 770 | //the fullName checks above. IE can call main() more than once 771 | //for a module. 772 | manager.depArray = depArray; 773 | manager.callback = callback; 774 | 775 | //Add the dependencies to the deps field, and register for callbacks 776 | //on the dependencies. 777 | for (i = 0; i < depArray.length; i++) { 778 | depArg = depArray[i]; 779 | //There could be cases like in IE, where a trailing comma will 780 | //introduce a null dependency, so only treat a real dependency 781 | //value as a dependency. 782 | if (depArg) { 783 | //Split the dependency name into plugin and name parts 784 | depArg = makeModuleMap(depArg, (name ? moduleMap : relModuleMap)); 785 | depName = depArg.fullName; 786 | depPrefix = depArg.prefix; 787 | 788 | //Fix the name in depArray to be just the name, since 789 | //that is how it will be called back later. 790 | depArray[i] = depName; 791 | 792 | //Fast path CommonJS standard dependencies. 793 | if (depName === "require") { 794 | deps[i] = makeRequire(moduleMap); 795 | } else if (depName === "exports") { 796 | //CommonJS module spec 1.1 797 | deps[i] = defined[fullName] = {}; 798 | manager.usingExports = true; 799 | } else if (depName === "module") { 800 | //CommonJS module spec 1.1 801 | manager.cjsModule = cjsMod = deps[i] = { 802 | id: name, 803 | uri: name ? context.nameToUrl(name, null, relModuleMap) : undefined, 804 | exports: defined[fullName] 805 | }; 806 | } else if (depName in defined && !(depName in waiting) && 807 | (!(fullName in needFullExec) || 808 | (fullName in needFullExec && fullExec[depName]))) { 809 | //Module already defined, and not in a build situation 810 | //where the module is a something that needs full 811 | //execution and this dependency has not been fully 812 | //executed. See r.js's requirePatch.js for more info 813 | //on fullExec. 814 | deps[i] = defined[depName]; 815 | } else { 816 | //Mark this dependency as needing full exec if 817 | //the current module needs full exec. 818 | if (fullName in needFullExec) { 819 | needFullExec[depName] = true; 820 | //Reset state so fully executed code will get 821 | //picked up correctly. 822 | delete defined[depName]; 823 | urlFetched[depArg.url] = false; 824 | } 825 | 826 | //Either a resource that is not loaded yet, or a plugin 827 | //resource for either a plugin that has not 828 | //loaded yet. 829 | manager.depCount += 1; 830 | manager.depCallbacks[i] = makeArgCallback(manager, i); 831 | getManager(depArg, true).add(manager.depCallbacks[i]); 832 | } 833 | } 834 | } 835 | 836 | //Do not bother tracking the manager if it is all done. 837 | if (!manager.depCount) { 838 | //All done, execute! 839 | execManager(manager); 840 | } else { 841 | addWait(manager); 842 | } 843 | } 844 | 845 | /** 846 | * Convenience method to call main for a define call that was put on 847 | * hold in the defQueue. 848 | */ 849 | function callDefMain(args) { 850 | main.apply(null, args); 851 | } 852 | 853 | /** 854 | * jQuery 1.4.3+ supports ways to hold off calling 855 | * calling jQuery ready callbacks until all scripts are loaded. Be sure 856 | * to track it if the capability exists.. Also, since jQuery 1.4.3 does 857 | * not register as a module, need to do some global inference checking. 858 | * Even if it does register as a module, not guaranteed to be the precise 859 | * name of the global. If a jQuery is tracked for this context, then go 860 | * ahead and register it as a module too, if not already in process. 861 | */ 862 | jQueryCheck = function (jqCandidate) { 863 | if (!context.jQuery) { 864 | var $ = jqCandidate || (typeof jQuery !== "undefined" ? jQuery : null); 865 | 866 | if ($) { 867 | //If a specific version of jQuery is wanted, make sure to only 868 | //use this jQuery if it matches. 869 | if (config.jQuery && $.fn.jquery !== config.jQuery) { 870 | return; 871 | } 872 | 873 | if ("holdReady" in $ || "readyWait" in $) { 874 | context.jQuery = $; 875 | 876 | //Manually create a "jquery" module entry if not one already 877 | //or in process. Note this could trigger an attempt at 878 | //a second jQuery registration, but does no harm since 879 | //the first one wins, and it is the same value anyway. 880 | callDefMain(["jquery", [], function () { 881 | return jQuery; 882 | }]); 883 | 884 | //Ask jQuery to hold DOM ready callbacks. 885 | if (context.scriptCount) { 886 | jQueryHoldReady($, true); 887 | context.jQueryIncremented = true; 888 | } 889 | } 890 | } 891 | } 892 | }; 893 | 894 | function findCycle(manager, traced) { 895 | var fullName = manager.map.fullName, 896 | depArray = manager.depArray, 897 | fullyLoaded = true, 898 | i, depName, depManager, result; 899 | 900 | if (manager.isDone || !fullName || !loaded[fullName]) { 901 | return result; 902 | } 903 | 904 | //Found the cycle. 905 | if (traced[fullName]) { 906 | return manager; 907 | } 908 | 909 | traced[fullName] = true; 910 | 911 | //Trace through the dependencies. 912 | if (depArray) { 913 | for (i = 0; i < depArray.length; i++) { 914 | //Some array members may be null, like if a trailing comma 915 | //IE, so do the explicit [i] access and check if it has a value. 916 | depName = depArray[i]; 917 | if (!loaded[depName] && !reservedDependencies[depName]) { 918 | fullyLoaded = false; 919 | break; 920 | } 921 | depManager = waiting[depName]; 922 | if (depManager && !depManager.isDone && loaded[depName]) { 923 | result = findCycle(depManager, traced); 924 | if (result) { 925 | break; 926 | } 927 | } 928 | } 929 | if (!fullyLoaded) { 930 | //Discard the cycle that was found, since it cannot 931 | //be forced yet. Also clear this module from traced. 932 | result = undefined; 933 | delete traced[fullName]; 934 | } 935 | } 936 | 937 | return result; 938 | } 939 | 940 | function forceExec(manager, traced) { 941 | var fullName = manager.map.fullName, 942 | depArray = manager.depArray, 943 | i, depName, depManager, prefix, prefixManager, value; 944 | 945 | 946 | if (manager.isDone || !fullName || !loaded[fullName]) { 947 | return undefined; 948 | } 949 | 950 | if (fullName) { 951 | if (traced[fullName]) { 952 | return defined[fullName]; 953 | } 954 | 955 | traced[fullName] = true; 956 | } 957 | 958 | //Trace through the dependencies. 959 | if (depArray) { 960 | for (i = 0; i < depArray.length; i++) { 961 | //Some array members may be null, like if a trailing comma 962 | //IE, so do the explicit [i] access and check if it has a value. 963 | depName = depArray[i]; 964 | if (depName) { 965 | //First, make sure if it is a plugin resource that the 966 | //plugin is not blocked. 967 | prefix = makeModuleMap(depName).prefix; 968 | if (prefix && (prefixManager = waiting[prefix])) { 969 | forceExec(prefixManager, traced); 970 | } 971 | depManager = waiting[depName]; 972 | if (depManager && !depManager.isDone && loaded[depName]) { 973 | value = forceExec(depManager, traced); 974 | manager.depCallbacks[i](value); 975 | } 976 | } 977 | } 978 | } 979 | 980 | return defined[fullName]; 981 | } 982 | 983 | /** 984 | * Checks if all modules for a context are loaded, and if so, evaluates the 985 | * new ones in right dependency order. 986 | * 987 | * @private 988 | */ 989 | function checkLoaded() { 990 | var waitInterval = config.waitSeconds * 1000, 991 | //It is possible to disable the wait interval by using waitSeconds of 0. 992 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), 993 | noLoads = "", hasLoadedProp = false, stillLoading = false, 994 | cycleDeps = [], 995 | i, prop, err, manager, cycleManager, moduleDeps; 996 | 997 | //If there are items still in the paused queue processing wait. 998 | //This is particularly important in the sync case where each paused 999 | //item is processed right away but there may be more waiting. 1000 | if (context.pausedCount > 0) { 1001 | return undefined; 1002 | } 1003 | 1004 | //Determine if priority loading is done. If so clear the priority. If 1005 | //not, then do not check 1006 | if (config.priorityWait) { 1007 | if (isPriorityDone()) { 1008 | //Call resume, since it could have 1009 | //some waiting dependencies to trace. 1010 | resume(); 1011 | } else { 1012 | return undefined; 1013 | } 1014 | } 1015 | 1016 | //See if anything is still in flight. 1017 | for (prop in loaded) { 1018 | if (!(prop in empty)) { 1019 | hasLoadedProp = true; 1020 | if (!loaded[prop]) { 1021 | if (expired) { 1022 | noLoads += prop + " "; 1023 | } else { 1024 | stillLoading = true; 1025 | if (prop.indexOf('!') === -1) { 1026 | //No reason to keep looking for unfinished 1027 | //loading. If the only stillLoading is a 1028 | //plugin resource though, keep going, 1029 | //because it may be that a plugin resource 1030 | //is waiting on a non-plugin cycle. 1031 | cycleDeps = []; 1032 | break; 1033 | } else { 1034 | moduleDeps = managerCallbacks[prop] && managerCallbacks[prop].moduleDeps; 1035 | if (moduleDeps) { 1036 | cycleDeps.push.apply(cycleDeps, moduleDeps); 1037 | } 1038 | } 1039 | } 1040 | } 1041 | } 1042 | } 1043 | 1044 | //Check for exit conditions. 1045 | if (!hasLoadedProp && !context.waitCount) { 1046 | //If the loaded object had no items, then the rest of 1047 | //the work below does not need to be done. 1048 | return undefined; 1049 | } 1050 | if (expired && noLoads) { 1051 | //If wait time expired, throw error of unloaded modules. 1052 | err = makeError("timeout", "Load timeout for modules: " + noLoads); 1053 | err.requireType = "timeout"; 1054 | err.requireModules = noLoads; 1055 | err.contextName = context.contextName; 1056 | return req.onError(err); 1057 | } 1058 | 1059 | //If still loading but a plugin is waiting on a regular module cycle 1060 | //break the cycle. 1061 | if (stillLoading && cycleDeps.length) { 1062 | for (i = 0; (manager = waiting[cycleDeps[i]]); i++) { 1063 | if ((cycleManager = findCycle(manager, {}))) { 1064 | forceExec(cycleManager, {}); 1065 | break; 1066 | } 1067 | } 1068 | 1069 | } 1070 | 1071 | //If still waiting on loads, and the waiting load is something 1072 | //other than a plugin resource, or there are still outstanding 1073 | //scripts, then just try back later. 1074 | if (!expired && (stillLoading || context.scriptCount)) { 1075 | //Something is still waiting to load. Wait for it, but only 1076 | //if a timeout is not already in effect. 1077 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 1078 | checkLoadedTimeoutId = setTimeout(function () { 1079 | checkLoadedTimeoutId = 0; 1080 | checkLoaded(); 1081 | }, 50); 1082 | } 1083 | return undefined; 1084 | } 1085 | 1086 | //If still have items in the waiting cue, but all modules have 1087 | //been loaded, then it means there are some circular dependencies 1088 | //that need to be broken. 1089 | //However, as a waiting thing is fired, then it can add items to 1090 | //the waiting cue, and those items should not be fired yet, so 1091 | //make sure to redo the checkLoaded call after breaking a single 1092 | //cycle, if nothing else loaded then this logic will pick it up 1093 | //again. 1094 | if (context.waitCount) { 1095 | //Cycle through the waitAry, and call items in sequence. 1096 | for (i = 0; (manager = waitAry[i]); i++) { 1097 | forceExec(manager, {}); 1098 | } 1099 | 1100 | //If anything got placed in the paused queue, run it down. 1101 | if (context.paused.length) { 1102 | resume(); 1103 | } 1104 | 1105 | //Only allow this recursion to a certain depth. Only 1106 | //triggered by errors in calling a module in which its 1107 | //modules waiting on it cannot finish loading, or some circular 1108 | //dependencies that then may add more dependencies. 1109 | //The value of 5 is a bit arbitrary. Hopefully just one extra 1110 | //pass, or two for the case of circular dependencies generating 1111 | //more work that gets resolved in the sync node case. 1112 | if (checkLoadedDepth < 5) { 1113 | checkLoadedDepth += 1; 1114 | checkLoaded(); 1115 | } 1116 | } 1117 | 1118 | checkLoadedDepth = 0; 1119 | 1120 | //Check for DOM ready, and nothing is waiting across contexts. 1121 | req.checkReadyState(); 1122 | 1123 | return undefined; 1124 | } 1125 | 1126 | /** 1127 | * Resumes tracing of dependencies and then checks if everything is loaded. 1128 | */ 1129 | resume = function () { 1130 | var manager, map, url, i, p, args, fullName; 1131 | 1132 | //Any defined modules in the global queue, intake them now. 1133 | context.takeGlobalQueue(); 1134 | 1135 | resumeDepth += 1; 1136 | 1137 | if (context.scriptCount <= 0) { 1138 | //Synchronous envs will push the number below zero with the 1139 | //decrement above, be sure to set it back to zero for good measure. 1140 | //require() calls that also do not end up loading scripts could 1141 | //push the number negative too. 1142 | context.scriptCount = 0; 1143 | } 1144 | 1145 | //Make sure any remaining defQueue items get properly processed. 1146 | while (defQueue.length) { 1147 | args = defQueue.shift(); 1148 | if (args[0] === null) { 1149 | return req.onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); 1150 | } else { 1151 | callDefMain(args); 1152 | } 1153 | } 1154 | 1155 | //Skip the resume of paused dependencies 1156 | //if current context is in priority wait. 1157 | if (!config.priorityWait || isPriorityDone()) { 1158 | while (context.paused.length) { 1159 | p = context.paused; 1160 | context.pausedCount += p.length; 1161 | //Reset paused list 1162 | context.paused = []; 1163 | 1164 | for (i = 0; (manager = p[i]); i++) { 1165 | map = manager.map; 1166 | url = map.url; 1167 | fullName = map.fullName; 1168 | 1169 | //If the manager is for a plugin managed resource, 1170 | //ask the plugin to load it now. 1171 | if (map.prefix) { 1172 | callPlugin(map.prefix, manager); 1173 | } else { 1174 | //Regular dependency. 1175 | if (!urlFetched[url] && !loaded[fullName]) { 1176 | req.load(context, fullName, url); 1177 | 1178 | //Mark the URL as fetched, but only if it is 1179 | //not an empty: URL, used by the optimizer. 1180 | //In that case we need to be sure to call 1181 | //load() for each module that is mapped to 1182 | //empty: so that dependencies are satisfied 1183 | //correctly. 1184 | if (url.indexOf('empty:') !== 0) { 1185 | urlFetched[url] = true; 1186 | } 1187 | } 1188 | } 1189 | } 1190 | 1191 | //Move the start time for timeout forward. 1192 | context.startTime = (new Date()).getTime(); 1193 | context.pausedCount -= p.length; 1194 | } 1195 | } 1196 | 1197 | //Only check if loaded when resume depth is 1. It is likely that 1198 | //it is only greater than 1 in sync environments where a factory 1199 | //function also then calls the callback-style require. In those 1200 | //cases, the checkLoaded should not occur until the resume 1201 | //depth is back at the top level. 1202 | if (resumeDepth === 1) { 1203 | checkLoaded(); 1204 | } 1205 | 1206 | resumeDepth -= 1; 1207 | 1208 | return undefined; 1209 | }; 1210 | 1211 | //Define the context object. Many of these fields are on here 1212 | //just to make debugging easier. 1213 | context = { 1214 | contextName: contextName, 1215 | config: config, 1216 | defQueue: defQueue, 1217 | waiting: waiting, 1218 | waitCount: 0, 1219 | specified: specified, 1220 | loaded: loaded, 1221 | urlMap: urlMap, 1222 | urlFetched: urlFetched, 1223 | scriptCount: 0, 1224 | defined: defined, 1225 | paused: [], 1226 | pausedCount: 0, 1227 | plugins: plugins, 1228 | needFullExec: needFullExec, 1229 | fake: {}, 1230 | fullExec: fullExec, 1231 | managerCallbacks: managerCallbacks, 1232 | makeModuleMap: makeModuleMap, 1233 | normalize: normalize, 1234 | /** 1235 | * Set a configuration for the context. 1236 | * @param {Object} cfg config object to integrate. 1237 | */ 1238 | configure: function (cfg) { 1239 | var paths, prop, packages, pkgs, packagePaths, requireWait; 1240 | 1241 | //Make sure the baseUrl ends in a slash. 1242 | if (cfg.baseUrl) { 1243 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== "/") { 1244 | cfg.baseUrl += "/"; 1245 | } 1246 | } 1247 | 1248 | //Save off the paths and packages since they require special processing, 1249 | //they are additive. 1250 | paths = config.paths; 1251 | packages = config.packages; 1252 | pkgs = config.pkgs; 1253 | 1254 | //Mix in the config values, favoring the new values over 1255 | //existing ones in context.config. 1256 | mixin(config, cfg, true); 1257 | 1258 | //Adjust paths if necessary. 1259 | if (cfg.paths) { 1260 | for (prop in cfg.paths) { 1261 | if (!(prop in empty)) { 1262 | paths[prop] = cfg.paths[prop]; 1263 | } 1264 | } 1265 | config.paths = paths; 1266 | } 1267 | 1268 | packagePaths = cfg.packagePaths; 1269 | if (packagePaths || cfg.packages) { 1270 | //Convert packagePaths into a packages config. 1271 | if (packagePaths) { 1272 | for (prop in packagePaths) { 1273 | if (!(prop in empty)) { 1274 | configurePackageDir(pkgs, packagePaths[prop], prop); 1275 | } 1276 | } 1277 | } 1278 | 1279 | //Adjust packages if necessary. 1280 | if (cfg.packages) { 1281 | configurePackageDir(pkgs, cfg.packages); 1282 | } 1283 | 1284 | //Done with modifications, assing packages back to context config 1285 | config.pkgs = pkgs; 1286 | } 1287 | 1288 | //If priority loading is in effect, trigger the loads now 1289 | if (cfg.priority) { 1290 | //Hold on to requireWait value, and reset it after done 1291 | requireWait = context.requireWait; 1292 | 1293 | //Allow tracing some require calls to allow the fetching 1294 | //of the priority config. 1295 | context.requireWait = false; 1296 | //But first, call resume to register any defined modules that may 1297 | //be in a data-main built file before the priority config 1298 | //call. 1299 | resume(); 1300 | 1301 | context.require(cfg.priority); 1302 | 1303 | //Trigger a resume right away, for the case when 1304 | //the script with the priority load is done as part 1305 | //of a data-main call. In that case the normal resume 1306 | //call will not happen because the scriptCount will be 1307 | //at 1, since the script for data-main is being processed. 1308 | resume(); 1309 | 1310 | //Restore previous state. 1311 | context.requireWait = requireWait; 1312 | config.priorityWait = cfg.priority; 1313 | } 1314 | 1315 | //If a deps array or a config callback is specified, then call 1316 | //require with those args. This is useful when require is defined as a 1317 | //config object before require.js is loaded. 1318 | if (cfg.deps || cfg.callback) { 1319 | context.require(cfg.deps || [], cfg.callback); 1320 | } 1321 | }, 1322 | 1323 | requireDefined: function (moduleName, relModuleMap) { 1324 | return makeModuleMap(moduleName, relModuleMap).fullName in defined; 1325 | }, 1326 | 1327 | requireSpecified: function (moduleName, relModuleMap) { 1328 | return makeModuleMap(moduleName, relModuleMap).fullName in specified; 1329 | }, 1330 | 1331 | require: function (deps, callback, relModuleMap) { 1332 | var moduleName, fullName, moduleMap; 1333 | if (typeof deps === "string") { 1334 | if (isFunction(callback)) { 1335 | //Invalid call 1336 | return req.onError(makeError("requireargs", "Invalid require call")); 1337 | } 1338 | 1339 | //Synchronous access to one module. If require.get is 1340 | //available (as in the Node adapter), prefer that. 1341 | //In this case deps is the moduleName and callback is 1342 | //the relModuleMap 1343 | if (req.get) { 1344 | return req.get(context, deps, callback); 1345 | } 1346 | 1347 | //Just return the module wanted. In this scenario, the 1348 | //second arg (if passed) is just the relModuleMap. 1349 | moduleName = deps; 1350 | relModuleMap = callback; 1351 | 1352 | //Normalize module name, if it contains . or .. 1353 | moduleMap = makeModuleMap(moduleName, relModuleMap); 1354 | fullName = moduleMap.fullName; 1355 | 1356 | if (!(fullName in defined)) { 1357 | return req.onError(makeError("notloaded", "Module name '" + 1358 | moduleMap.fullName + 1359 | "' has not been loaded yet for context: " + 1360 | contextName)); 1361 | } 1362 | return defined[fullName]; 1363 | } 1364 | 1365 | //Call main but only if there are dependencies or 1366 | //a callback to call. 1367 | if (deps && deps.length || callback) { 1368 | main(null, deps, callback, relModuleMap); 1369 | } 1370 | 1371 | //If the require call does not trigger anything new to load, 1372 | //then resume the dependency processing. 1373 | if (!context.requireWait) { 1374 | while (!context.scriptCount && context.paused.length) { 1375 | resume(); 1376 | } 1377 | } 1378 | return context.require; 1379 | }, 1380 | 1381 | /** 1382 | * Internal method to transfer globalQueue items to this context's 1383 | * defQueue. 1384 | */ 1385 | takeGlobalQueue: function () { 1386 | //Push all the globalDefQueue items into the context's defQueue 1387 | if (globalDefQueue.length) { 1388 | //Array splice in the values since the context code has a 1389 | //local var ref to defQueue, so cannot just reassign the one 1390 | //on context. 1391 | apsp.apply(context.defQueue, 1392 | [context.defQueue.length - 1, 0].concat(globalDefQueue)); 1393 | globalDefQueue = []; 1394 | } 1395 | }, 1396 | 1397 | /** 1398 | * Internal method used by environment adapters to complete a load event. 1399 | * A load event could be a script load or just a load pass from a synchronous 1400 | * load call. 1401 | * @param {String} moduleName the name of the module to potentially complete. 1402 | */ 1403 | completeLoad: function (moduleName) { 1404 | var args; 1405 | 1406 | context.takeGlobalQueue(); 1407 | 1408 | while (defQueue.length) { 1409 | args = defQueue.shift(); 1410 | 1411 | if (args[0] === null) { 1412 | args[0] = moduleName; 1413 | break; 1414 | } else if (args[0] === moduleName) { 1415 | //Found matching define call for this script! 1416 | break; 1417 | } else { 1418 | //Some other named define call, most likely the result 1419 | //of a build layer that included many define calls. 1420 | callDefMain(args); 1421 | args = null; 1422 | } 1423 | } 1424 | if (args) { 1425 | callDefMain(args); 1426 | } else { 1427 | //A script that does not call define(), so just simulate 1428 | //the call for it. Special exception for jQuery dynamic load. 1429 | callDefMain([moduleName, [], 1430 | moduleName === "jquery" && typeof jQuery !== "undefined" ? 1431 | function () { 1432 | return jQuery; 1433 | } : null]); 1434 | } 1435 | 1436 | //Doing this scriptCount decrement branching because sync envs 1437 | //need to decrement after resume, otherwise it looks like 1438 | //loading is complete after the first dependency is fetched. 1439 | //For browsers, it works fine to decrement after, but it means 1440 | //the checkLoaded setTimeout 50 ms cost is taken. To avoid 1441 | //that cost, decrement beforehand. 1442 | if (req.isAsync) { 1443 | context.scriptCount -= 1; 1444 | } 1445 | resume(); 1446 | if (!req.isAsync) { 1447 | context.scriptCount -= 1; 1448 | } 1449 | }, 1450 | 1451 | /** 1452 | * Converts a module name + .extension into an URL path. 1453 | * *Requires* the use of a module name. It does not support using 1454 | * plain URLs like nameToUrl. 1455 | */ 1456 | toUrl: function (moduleNamePlusExt, relModuleMap) { 1457 | var index = moduleNamePlusExt.lastIndexOf("."), 1458 | ext = null; 1459 | 1460 | if (index !== -1) { 1461 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); 1462 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index); 1463 | } 1464 | 1465 | return context.nameToUrl(moduleNamePlusExt, ext, relModuleMap); 1466 | }, 1467 | 1468 | /** 1469 | * Converts a module name to a file path. Supports cases where 1470 | * moduleName may actually be just an URL. 1471 | */ 1472 | nameToUrl: function (moduleName, ext, relModuleMap) { 1473 | var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, 1474 | config = context.config; 1475 | 1476 | //Normalize module name if have a base relative module name to work from. 1477 | moduleName = normalize(moduleName, relModuleMap && relModuleMap.fullName); 1478 | 1479 | //If a colon is in the URL, it indicates a protocol is used and it is just 1480 | //an URL to a file, or if it starts with a slash or ends with .js, it is just a plain file. 1481 | //The slash is important for protocol-less URLs as well as full paths. 1482 | if (req.jsExtRegExp.test(moduleName)) { 1483 | //Just a plain path, not module name lookup, so just return it. 1484 | //Add extension if it is included. This is a bit wonky, only non-.js things pass 1485 | //an extension, this method probably needs to be reworked. 1486 | url = moduleName + (ext ? ext : ""); 1487 | } else { 1488 | //A module that needs to be converted to a path. 1489 | paths = config.paths; 1490 | pkgs = config.pkgs; 1491 | 1492 | syms = moduleName.split("/"); 1493 | //For each module name segment, see if there is a path 1494 | //registered for it. Start with most specific name 1495 | //and work up from it. 1496 | for (i = syms.length; i > 0; i--) { 1497 | parentModule = syms.slice(0, i).join("/"); 1498 | if (paths[parentModule]) { 1499 | syms.splice(0, i, paths[parentModule]); 1500 | break; 1501 | } else if ((pkg = pkgs[parentModule])) { 1502 | //If module name is just the package name, then looking 1503 | //for the main module. 1504 | if (moduleName === pkg.name) { 1505 | pkgPath = pkg.location + '/' + pkg.main; 1506 | } else { 1507 | pkgPath = pkg.location; 1508 | } 1509 | syms.splice(0, i, pkgPath); 1510 | break; 1511 | } 1512 | } 1513 | 1514 | //Join the path parts together, then figure out if baseUrl is needed. 1515 | url = syms.join("/") + (ext || ".js"); 1516 | url = (url.charAt(0) === '/' || url.match(/^\w+:/) ? "" : config.baseUrl) + url; 1517 | } 1518 | 1519 | return config.urlArgs ? url + 1520 | ((url.indexOf('?') === -1 ? '?' : '&') + 1521 | config.urlArgs) : url; 1522 | } 1523 | }; 1524 | 1525 | //Make these visible on the context so can be called at the very 1526 | //end of the file to bootstrap 1527 | context.jQueryCheck = jQueryCheck; 1528 | context.resume = resume; 1529 | 1530 | return context; 1531 | } 1532 | 1533 | /** 1534 | * Main entry point. 1535 | * 1536 | * If the only argument to require is a string, then the module that 1537 | * is represented by that string is fetched for the appropriate context. 1538 | * 1539 | * If the first argument is an array, then it will be treated as an array 1540 | * of dependency string names to fetch. An optional function callback can 1541 | * be specified to execute when all of those dependencies are available. 1542 | * 1543 | * Make a local req variable to help Caja compliance (it assumes things 1544 | * on a require that are not standardized), and to give a short 1545 | * name for minification/local scope use. 1546 | */ 1547 | req = requirejs = function (deps, callback) { 1548 | 1549 | //Find the right context, use default 1550 | var contextName = defContextName, 1551 | context, config; 1552 | 1553 | // Determine if have config object in the call. 1554 | if (!isArray(deps) && typeof deps !== "string") { 1555 | // deps is a config object 1556 | config = deps; 1557 | if (isArray(callback)) { 1558 | // Adjust args if there are dependencies 1559 | deps = callback; 1560 | callback = arguments[2]; 1561 | } else { 1562 | deps = []; 1563 | } 1564 | } 1565 | 1566 | if (config && config.context) { 1567 | contextName = config.context; 1568 | } 1569 | 1570 | context = contexts[contextName] || 1571 | (contexts[contextName] = newContext(contextName)); 1572 | 1573 | if (config) { 1574 | context.configure(config); 1575 | } 1576 | 1577 | return context.require(deps, callback); 1578 | }; 1579 | 1580 | /** 1581 | * Support require.config() to make it easier to cooperate with other 1582 | * AMD loaders on globally agreed names. 1583 | */ 1584 | req.config = function (config) { 1585 | return req(config); 1586 | }; 1587 | 1588 | /** 1589 | * Export require as a global, but only if it does not already exist. 1590 | */ 1591 | if (!require) { 1592 | require = req; 1593 | } 1594 | 1595 | /** 1596 | * Global require.toUrl(), to match global require, mostly useful 1597 | * for debugging/work in the global space. 1598 | */ 1599 | req.toUrl = function (moduleNamePlusExt) { 1600 | return contexts[defContextName].toUrl(moduleNamePlusExt); 1601 | }; 1602 | 1603 | req.version = version; 1604 | 1605 | //Used to filter out dependencies that are already paths. 1606 | req.jsExtRegExp = /^\/|:|\?|\.js$/; 1607 | s = req.s = { 1608 | contexts: contexts, 1609 | //Stores a list of URLs that should not get async script tag treatment. 1610 | skipAsync: {} 1611 | }; 1612 | 1613 | req.isAsync = req.isBrowser = isBrowser; 1614 | if (isBrowser) { 1615 | head = s.head = document.getElementsByTagName("head")[0]; 1616 | //If BASE tag is in play, using appendChild is a problem for IE6. 1617 | //When that browser dies, this can be removed. Details in this jQuery bug: 1618 | //http://dev.jquery.com/ticket/2709 1619 | baseElement = document.getElementsByTagName("base")[0]; 1620 | if (baseElement) { 1621 | head = s.head = baseElement.parentNode; 1622 | } 1623 | } 1624 | 1625 | /** 1626 | * Any errors that require explicitly generates will be passed to this 1627 | * function. Intercept/override it if you want custom error handling. 1628 | * @param {Error} err the error object. 1629 | */ 1630 | req.onError = function (err) { 1631 | throw err; 1632 | }; 1633 | 1634 | /** 1635 | * Does the request to load a module for the browser case. 1636 | * Make this a separate function to allow other environments 1637 | * to override it. 1638 | * 1639 | * @param {Object} context the require context to find state. 1640 | * @param {String} moduleName the name of the module. 1641 | * @param {Object} url the URL to the module. 1642 | */ 1643 | req.load = function (context, moduleName, url) { 1644 | req.resourcesReady(false); 1645 | 1646 | context.scriptCount += 1; 1647 | req.attach(url, context, moduleName); 1648 | 1649 | //If tracking a jQuery, then make sure its ready callbacks 1650 | //are put on hold to prevent its ready callbacks from 1651 | //triggering too soon. 1652 | if (context.jQuery && !context.jQueryIncremented) { 1653 | jQueryHoldReady(context.jQuery, true); 1654 | context.jQueryIncremented = true; 1655 | } 1656 | }; 1657 | 1658 | function getInteractiveScript() { 1659 | var scripts, i, script; 1660 | if (interactiveScript && interactiveScript.readyState === 'interactive') { 1661 | return interactiveScript; 1662 | } 1663 | 1664 | scripts = document.getElementsByTagName('script'); 1665 | for (i = scripts.length - 1; i > -1 && (script = scripts[i]); i--) { 1666 | if (script.readyState === 'interactive') { 1667 | return (interactiveScript = script); 1668 | } 1669 | } 1670 | 1671 | return null; 1672 | } 1673 | 1674 | /** 1675 | * The function that handles definitions of modules. Differs from 1676 | * require() in that a string for the module should be the first argument, 1677 | * and the function to execute after dependencies are loaded should 1678 | * return a value to define the module corresponding to the first argument's 1679 | * name. 1680 | */ 1681 | define = function (name, deps, callback) { 1682 | var node, context; 1683 | 1684 | //Allow for anonymous functions 1685 | if (typeof name !== 'string') { 1686 | //Adjust args appropriately 1687 | callback = deps; 1688 | deps = name; 1689 | name = null; 1690 | } 1691 | 1692 | //This module may not have dependencies 1693 | if (!isArray(deps)) { 1694 | callback = deps; 1695 | deps = []; 1696 | } 1697 | 1698 | //If no name, and callback is a function, then figure out if it a 1699 | //CommonJS thing with dependencies. 1700 | if (!deps.length && isFunction(callback)) { 1701 | //Remove comments from the callback string, 1702 | //look for require calls, and pull them into the dependencies, 1703 | //but only if there are function args. 1704 | if (callback.length) { 1705 | callback 1706 | .toString() 1707 | .replace(commentRegExp, "") 1708 | .replace(cjsRequireRegExp, function (match, dep) { 1709 | deps.push(dep); 1710 | }); 1711 | 1712 | //May be a CommonJS thing even without require calls, but still 1713 | //could use exports, and module. Avoid doing exports and module 1714 | //work though if it just needs require. 1715 | //REQUIRES the function to expect the CommonJS variables in the 1716 | //order listed below. 1717 | deps = (callback.length === 1 ? ["require"] : ["require", "exports", "module"]).concat(deps); 1718 | } 1719 | } 1720 | 1721 | //If in IE 6-8 and hit an anonymous define() call, do the interactive 1722 | //work. 1723 | if (useInteractive) { 1724 | node = currentlyAddingScript || getInteractiveScript(); 1725 | if (node) { 1726 | if (!name) { 1727 | name = node.getAttribute("data-requiremodule"); 1728 | } 1729 | context = contexts[node.getAttribute("data-requirecontext")]; 1730 | } 1731 | } 1732 | 1733 | //Always save off evaluating the def call until the script onload handler. 1734 | //This allows multiple modules to be in a file without prematurely 1735 | //tracing dependencies, and allows for anonymous module support, 1736 | //where the module name is not known until the script onload event 1737 | //occurs. If no context, use the global queue, and get it processed 1738 | //in the onscript load callback. 1739 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); 1740 | 1741 | return undefined; 1742 | }; 1743 | 1744 | define.amd = { 1745 | multiversion: true, 1746 | plugins: true, 1747 | jQuery: true 1748 | }; 1749 | 1750 | /** 1751 | * Executes the text. Normally just uses eval, but can be modified 1752 | * to use a more environment specific call. 1753 | * @param {String} text the text to execute/evaluate. 1754 | */ 1755 | req.exec = function (text) { 1756 | return eval(text); 1757 | }; 1758 | 1759 | /** 1760 | * Executes a module callack function. Broken out as a separate function 1761 | * solely to allow the build system to sequence the files in the built 1762 | * layer in the right sequence. 1763 | * 1764 | * @private 1765 | */ 1766 | req.execCb = function (name, callback, args, exports) { 1767 | return callback.apply(exports, args); 1768 | }; 1769 | 1770 | 1771 | /** 1772 | * Adds a node to the DOM. Public function since used by the order plugin. 1773 | * This method should not normally be called by outside code. 1774 | */ 1775 | req.addScriptToDom = function (node) { 1776 | //For some cache cases in IE 6-8, the script executes before the end 1777 | //of the appendChild execution, so to tie an anonymous define 1778 | //call to the module name (which is stored on the node), hold on 1779 | //to a reference to this node, but clear after the DOM insertion. 1780 | currentlyAddingScript = node; 1781 | if (baseElement) { 1782 | head.insertBefore(node, baseElement); 1783 | } else { 1784 | head.appendChild(node); 1785 | } 1786 | currentlyAddingScript = null; 1787 | }; 1788 | 1789 | /** 1790 | * callback for script loads, used to check status of loading. 1791 | * 1792 | * @param {Event} evt the event from the browser for the script 1793 | * that was loaded. 1794 | * 1795 | * @private 1796 | */ 1797 | req.onScriptLoad = function (evt) { 1798 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1799 | //all old browsers will be supported, but this one was easy enough 1800 | //to support and still makes sense. 1801 | var node = evt.currentTarget || evt.srcElement, contextName, moduleName, 1802 | context; 1803 | 1804 | if (evt.type === "load" || (node && readyRegExp.test(node.readyState))) { 1805 | //Reset interactive script so a script node is not held onto for 1806 | //to long. 1807 | interactiveScript = null; 1808 | 1809 | //Pull out the name of the module and the context. 1810 | contextName = node.getAttribute("data-requirecontext"); 1811 | moduleName = node.getAttribute("data-requiremodule"); 1812 | context = contexts[contextName]; 1813 | 1814 | contexts[contextName].completeLoad(moduleName); 1815 | 1816 | //Clean up script binding. Favor detachEvent because of IE9 1817 | //issue, see attachEvent/addEventListener comment elsewhere 1818 | //in this file. 1819 | if (node.detachEvent && !isOpera) { 1820 | //Probably IE. If not it will throw an error, which will be 1821 | //useful to know. 1822 | node.detachEvent("onreadystatechange", req.onScriptLoad); 1823 | } else { 1824 | node.removeEventListener("load", req.onScriptLoad, false); 1825 | } 1826 | } 1827 | }; 1828 | 1829 | /** 1830 | * Attaches the script represented by the URL to the current 1831 | * environment. Right now only supports browser loading, 1832 | * but can be redefined in other environments to do the right thing. 1833 | * @param {String} url the url of the script to attach. 1834 | * @param {Object} context the context that wants the script. 1835 | * @param {moduleName} the name of the module that is associated with the script. 1836 | * @param {Function} [callback] optional callback, defaults to require.onScriptLoad 1837 | * @param {String} [type] optional type, defaults to text/javascript 1838 | * @param {Function} [fetchOnlyFunction] optional function to indicate the script node 1839 | * should be set up to fetch the script but do not attach it to the DOM 1840 | * so that it can later be attached to execute it. This is a way for the 1841 | * order plugin to support ordered loading in IE. Once the script is fetched, 1842 | * but not executed, the fetchOnlyFunction will be called. 1843 | */ 1844 | req.attach = function (url, context, moduleName, callback, type, fetchOnlyFunction) { 1845 | var node; 1846 | if (isBrowser) { 1847 | //In the browser so use a script tag 1848 | callback = callback || req.onScriptLoad; 1849 | node = context && context.config && context.config.xhtml ? 1850 | document.createElementNS("http://www.w3.org/1999/xhtml", "html:script") : 1851 | document.createElement("script"); 1852 | node.type = type || (context && context.config.scriptType) || 1853 | "text/javascript"; 1854 | node.charset = "utf-8"; 1855 | //Use async so Gecko does not block on executing the script if something 1856 | //like a long-polling comet tag is being run first. Gecko likes 1857 | //to evaluate scripts in DOM order, even for dynamic scripts. 1858 | //It will fetch them async, but only evaluate the contents in DOM 1859 | //order, so a long-polling script tag can delay execution of scripts 1860 | //after it. But telling Gecko we expect async gets us the behavior 1861 | //we want -- execute it whenever it is finished downloading. Only 1862 | //Helps Firefox 3.6+ 1863 | //Allow some URLs to not be fetched async. Mostly helps the order! 1864 | //plugin 1865 | node.async = !s.skipAsync[url]; 1866 | 1867 | if (context) { 1868 | node.setAttribute("data-requirecontext", context.contextName); 1869 | } 1870 | node.setAttribute("data-requiremodule", moduleName); 1871 | 1872 | //Set up load listener. Test attachEvent first because IE9 has 1873 | //a subtle issue in its addEventListener and script onload firings 1874 | //that do not match the behavior of all other browsers with 1875 | //addEventListener support, which fire the onload event for a 1876 | //script right after the script execution. See: 1877 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution 1878 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script 1879 | //script execution mode. 1880 | if (node.attachEvent && !isOpera) { 1881 | //Probably IE. IE (at least 6-8) do not fire 1882 | //script onload right after executing the script, so 1883 | //we cannot tie the anonymous define call to a name. 1884 | //However, IE reports the script as being in "interactive" 1885 | //readyState at the time of the define call. 1886 | useInteractive = true; 1887 | 1888 | 1889 | if (fetchOnlyFunction) { 1890 | //Need to use old school onreadystate here since 1891 | //when the event fires and the node is not attached 1892 | //to the DOM, the evt.srcElement is null, so use 1893 | //a closure to remember the node. 1894 | node.onreadystatechange = function (evt) { 1895 | //Script loaded but not executed. 1896 | //Clear loaded handler, set the real one that 1897 | //waits for script execution. 1898 | if (node.readyState === 'loaded') { 1899 | node.onreadystatechange = null; 1900 | node.attachEvent("onreadystatechange", callback); 1901 | fetchOnlyFunction(node); 1902 | } 1903 | }; 1904 | } else { 1905 | node.attachEvent("onreadystatechange", callback); 1906 | } 1907 | } else { 1908 | node.addEventListener("load", callback, false); 1909 | } 1910 | node.src = url; 1911 | 1912 | //Fetch only means waiting to attach to DOM after loaded. 1913 | if (!fetchOnlyFunction) { 1914 | req.addScriptToDom(node); 1915 | } 1916 | 1917 | return node; 1918 | } else if (isWebWorker) { 1919 | //In a web worker, use importScripts. This is not a very 1920 | //efficient use of importScripts, importScripts will block until 1921 | //its script is downloaded and evaluated. However, if web workers 1922 | //are in play, the expectation that a build has been done so that 1923 | //only one script needs to be loaded anyway. This may need to be 1924 | //reevaluated if other use cases become common. 1925 | importScripts(url); 1926 | 1927 | //Account for anonymous modules 1928 | context.completeLoad(moduleName); 1929 | } 1930 | return null; 1931 | }; 1932 | 1933 | //Look for a data-main script attribute, which could also adjust the baseUrl. 1934 | if (isBrowser) { 1935 | //Figure out baseUrl. Get it from the script tag with require.js in it. 1936 | scripts = document.getElementsByTagName("script"); 1937 | 1938 | for (globalI = scripts.length - 1; globalI > -1 && (script = scripts[globalI]); globalI--) { 1939 | //Set the "head" where we can append children by 1940 | //using the script's parent. 1941 | if (!head) { 1942 | head = script.parentNode; 1943 | } 1944 | 1945 | //Look for a data-main attribute to set main script for the page 1946 | //to load. If it is there, the path to data main becomes the 1947 | //baseUrl, if it is not already set. 1948 | if ((dataMain = script.getAttribute('data-main'))) { 1949 | if (!cfg.baseUrl) { 1950 | //Pull off the directory of data-main for use as the 1951 | //baseUrl. 1952 | src = dataMain.split('/'); 1953 | mainScript = src.pop(); 1954 | subPath = src.length ? src.join('/') + '/' : './'; 1955 | 1956 | //Set final config. 1957 | cfg.baseUrl = subPath; 1958 | //Strip off any trailing .js since dataMain is now 1959 | //like a module name. 1960 | dataMain = mainScript.replace(jsSuffixRegExp, ''); 1961 | } 1962 | 1963 | //Put the data-main script in the files to load. 1964 | cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; 1965 | 1966 | break; 1967 | } 1968 | } 1969 | } 1970 | 1971 | //See if there is nothing waiting across contexts, and if not, trigger 1972 | //resourcesReady. 1973 | req.checkReadyState = function () { 1974 | var contexts = s.contexts, prop; 1975 | for (prop in contexts) { 1976 | if (!(prop in empty)) { 1977 | if (contexts[prop].waitCount) { 1978 | return; 1979 | } 1980 | } 1981 | } 1982 | req.resourcesReady(true); 1983 | }; 1984 | 1985 | /** 1986 | * Internal function that is triggered whenever all scripts/resources 1987 | * have been loaded by the loader. Can be overridden by other, for 1988 | * instance the domReady plugin, which wants to know when all resources 1989 | * are loaded. 1990 | */ 1991 | req.resourcesReady = function (isReady) { 1992 | var contexts, context, prop; 1993 | 1994 | //First, set the public variable indicating that resources are loading. 1995 | req.resourcesDone = isReady; 1996 | 1997 | if (req.resourcesDone) { 1998 | //If jQuery with DOM ready delayed, release it now. 1999 | contexts = s.contexts; 2000 | for (prop in contexts) { 2001 | if (!(prop in empty)) { 2002 | context = contexts[prop]; 2003 | if (context.jQueryIncremented) { 2004 | jQueryHoldReady(context.jQuery, false); 2005 | context.jQueryIncremented = false; 2006 | } 2007 | } 2008 | } 2009 | } 2010 | }; 2011 | 2012 | //FF < 3.6 readyState fix. Needed so that domReady plugin 2013 | //works well in that environment, since require.js is normally 2014 | //loaded via an HTML script tag so it will be there before window load, 2015 | //where the domReady plugin is more likely to be loaded after window load. 2016 | req.pageLoaded = function () { 2017 | if (document.readyState !== "complete") { 2018 | document.readyState = "complete"; 2019 | } 2020 | }; 2021 | if (isBrowser) { 2022 | if (document.addEventListener) { 2023 | if (!document.readyState) { 2024 | document.readyState = "loading"; 2025 | window.addEventListener("load", req.pageLoaded, false); 2026 | } 2027 | } 2028 | } 2029 | 2030 | //Set up default context. If require was a configuration object, use that as base config. 2031 | req(cfg); 2032 | 2033 | //If modules are built into require.js, then need to make sure dependencies are 2034 | //traced. Use a setTimeout in the browser world, to allow all the modules to register 2035 | //themselves. In a non-browser env, assume that modules are not built into require.js, 2036 | //which seems odd to do on the server. 2037 | if (req.isAsync && typeof setTimeout !== "undefined") { 2038 | ctx = s.contexts[(cfg.context || defContextName)]; 2039 | //Indicate that the script that includes require() is still loading, 2040 | //so that require()'d dependencies are not traced until the end of the 2041 | //file is parsed (approximated via the setTimeout call). 2042 | ctx.requireWait = true; 2043 | setTimeout(function () { 2044 | ctx.requireWait = false; 2045 | 2046 | if (!ctx.scriptCount) { 2047 | ctx.resume(); 2048 | } 2049 | req.checkReadyState(); 2050 | }, 0); 2051 | } 2052 | }()); 2053 | -------------------------------------------------------------------------------- /examples-build/js/lib/rolls.js: -------------------------------------------------------------------------------- 1 | rolls = { 2 | name: "rolls", 3 | ingredients: [wheat] //depends on the global wheat.js being available 4 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/sugar.js: -------------------------------------------------------------------------------- 1 | sugar = { 2 | name:"sugar" 3 | }; -------------------------------------------------------------------------------- /examples-build/js/lib/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 1.0.2 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 regexp: false, nomen: false, plusplus: false, strict: false */ 7 | /*global require: false, XMLHttpRequest: false, ActiveXObject: false, 8 | define: false, window: false, process: false, Packages: false, 9 | java: false, location: false */ 10 | (function () { 11 | var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 12 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 13 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 14 | hasLocation = typeof location !== 'undefined' && location.href, 15 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 16 | defaultHostName = hasLocation && location.hostname, 17 | defaultPort = hasLocation && (location.port || undefined), 18 | buildMap = []; 19 | 20 | define(function () { 21 | var text, get, fs; 22 | 23 | if (typeof window !== "undefined" && window.navigator && window.document) { 24 | get = function (url, callback) { 25 | var xhr = text.createXhr(); 26 | xhr.open('GET', url, true); 27 | xhr.onreadystatechange = function (evt) { 28 | //Do not explicitly handle errors, those should be 29 | //visible via console output in the browser. 30 | if (xhr.readyState === 4) { 31 | callback(xhr.responseText); 32 | } 33 | }; 34 | xhr.send(null); 35 | }; 36 | } else if (typeof process !== "undefined" && 37 | process.versions && 38 | !!process.versions.node) { 39 | //Using special require.nodeRequire, something added by r.js. 40 | fs = require.nodeRequire('fs'); 41 | 42 | get = function (url, callback) { 43 | callback(fs.readFileSync(url, 'utf8')); 44 | }; 45 | } else if (typeof Packages !== 'undefined') { 46 | //Why Java, why is this so awkward? 47 | get = function (url, callback) { 48 | var encoding = "utf-8", 49 | file = new java.io.File(url), 50 | lineSeparator = java.lang.System.getProperty("line.separator"), 51 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 52 | stringBuffer, line, 53 | content = ''; 54 | try { 55 | stringBuffer = new java.lang.StringBuffer(); 56 | line = input.readLine(); 57 | 58 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 59 | // http://www.unicode.org/faq/utf_bom.html 60 | 61 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 62 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 63 | if (line && line.length() && line.charAt(0) === 0xfeff) { 64 | // Eat the BOM, since we've already found the encoding on this file, 65 | // and we plan to concatenating this buffer with others; the BOM should 66 | // only appear at the top of a file. 67 | line = line.substring(1); 68 | } 69 | 70 | stringBuffer.append(line); 71 | 72 | while ((line = input.readLine()) !== null) { 73 | stringBuffer.append(lineSeparator); 74 | stringBuffer.append(line); 75 | } 76 | //Make sure we return a JavaScript string and not a Java string. 77 | content = String(stringBuffer.toString()); //String 78 | } finally { 79 | input.close(); 80 | } 81 | callback(content); 82 | }; 83 | } 84 | 85 | text = { 86 | version: '1.0.2', 87 | 88 | strip: function (content) { 89 | //Strips declarations so that external SVG and XML 90 | //documents can be added to a document without worry. Also, if the string 91 | //is an HTML document, only the part inside the body tag is returned. 92 | if (content) { 93 | content = content.replace(xmlRegExp, ""); 94 | var matches = content.match(bodyRegExp); 95 | if (matches) { 96 | content = matches[1]; 97 | } 98 | } else { 99 | content = ""; 100 | } 101 | return content; 102 | }, 103 | 104 | jsEscape: function (content) { 105 | return content.replace(/(['\\])/g, '\\$1') 106 | .replace(/[\f]/g, "\\f") 107 | .replace(/[\b]/g, "\\b") 108 | .replace(/[\n]/g, "\\n") 109 | .replace(/[\t]/g, "\\t") 110 | .replace(/[\r]/g, "\\r"); 111 | }, 112 | 113 | createXhr: function () { 114 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 115 | var xhr, i, progId; 116 | if (typeof XMLHttpRequest !== "undefined") { 117 | return new XMLHttpRequest(); 118 | } else { 119 | for (i = 0; i < 3; i++) { 120 | progId = progIds[i]; 121 | try { 122 | xhr = new ActiveXObject(progId); 123 | } catch (e) {} 124 | 125 | if (xhr) { 126 | progIds = [progId]; // so faster next time 127 | break; 128 | } 129 | } 130 | } 131 | 132 | if (!xhr) { 133 | throw new Error("createXhr(): XMLHttpRequest not available"); 134 | } 135 | 136 | return xhr; 137 | }, 138 | 139 | get: get, 140 | 141 | /** 142 | * Parses a resource name into its component parts. Resource names 143 | * look like: module/name.ext!strip, where the !strip part is 144 | * optional. 145 | * @param {String} name the resource name 146 | * @returns {Object} with properties "moduleName", "ext" and "strip" 147 | * where strip is a boolean. 148 | */ 149 | parseName: function (name) { 150 | var strip = false, index = name.indexOf("."), 151 | modName = name.substring(0, index), 152 | ext = name.substring(index + 1, name.length); 153 | 154 | index = ext.indexOf("!"); 155 | if (index !== -1) { 156 | //Pull off the strip arg. 157 | strip = ext.substring(index + 1, ext.length); 158 | strip = strip === "strip"; 159 | ext = ext.substring(0, index); 160 | } 161 | 162 | return { 163 | moduleName: modName, 164 | ext: ext, 165 | strip: strip 166 | }; 167 | }, 168 | 169 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 170 | 171 | /** 172 | * Is an URL on another domain. Only works for browser use, returns 173 | * false in non-browser environments. Only used to know if an 174 | * optimized .js version of a text resource should be loaded 175 | * instead. 176 | * @param {String} url 177 | * @returns Boolean 178 | */ 179 | useXhr: function (url, protocol, hostname, port) { 180 | var match = text.xdRegExp.exec(url), 181 | uProtocol, uHostName, uPort; 182 | if (!match) { 183 | return true; 184 | } 185 | uProtocol = match[2]; 186 | uHostName = match[3]; 187 | 188 | uHostName = uHostName.split(':'); 189 | uPort = uHostName[1]; 190 | uHostName = uHostName[0]; 191 | 192 | return (!uProtocol || uProtocol === protocol) && 193 | (!uHostName || uHostName === hostname) && 194 | ((!uPort && !uHostName) || uPort === port); 195 | }, 196 | 197 | finishLoad: function (name, strip, content, onLoad, config) { 198 | content = strip ? text.strip(content) : content; 199 | if (config.isBuild) { 200 | buildMap[name] = content; 201 | } 202 | onLoad(content); 203 | }, 204 | 205 | load: function (name, req, onLoad, config) { 206 | //Name has format: some.module.filext!strip 207 | //The strip part is optional. 208 | //if strip is present, then that means only get the string contents 209 | //inside a body tag in an HTML string. For XML/SVG content it means 210 | //removing the declarations so the content can be inserted 211 | //into the current doc without problems. 212 | 213 | // Do not bother with the work if a build and text will 214 | // not be inlined. 215 | if (config.isBuild && !config.inlineText) { 216 | onLoad(); 217 | return; 218 | } 219 | 220 | var parsed = text.parseName(name), 221 | nonStripName = parsed.moduleName + '.' + parsed.ext, 222 | url = req.toUrl(nonStripName), 223 | useXhr = (config && config.text && config.text.useXhr) || 224 | text.useXhr; 225 | 226 | 227 | //Load the text. Use XHR if possible and in a browser. 228 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 229 | text.get(url, function (content) { 230 | text.finishLoad(name, parsed.strip, content, onLoad, config); 231 | }); 232 | } else { 233 | //Need to fetch the resource across domains. Assume 234 | //the resource has been optimized into a JS module. Fetch 235 | //by the module name + extension, but do not include the 236 | //!strip part to avoid file system issues. 237 | req([nonStripName], function (content) { 238 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 239 | parsed.strip, content, onLoad, config); 240 | }); 241 | } 242 | }, 243 | 244 | write: function (pluginName, moduleName, write, config) { 245 | if (moduleName in buildMap) { 246 | var content = text.jsEscape(buildMap[moduleName]); 247 | write.asModule(pluginName + "!" + moduleName, 248 | "define(function () { return '" + 249 | content + 250 | "';});\n"); 251 | } 252 | }, 253 | 254 | writeFile: function (pluginName, moduleName, req, write, config) { 255 | var parsed = text.parseName(moduleName), 256 | nonStripName = parsed.moduleName + '.' + parsed.ext, 257 | //Use a '.js' file name so that it indicates it is a 258 | //script that can be loaded across domains. 259 | fileName = req.toUrl(parsed.moduleName + '.' + 260 | parsed.ext) + '.js'; 261 | 262 | //Leverage own load() method to load plugin value, but only 263 | //write out values that do not have the strip argument, 264 | //to avoid any potential issues with ! in file names. 265 | text.load(nonStripName, req, function (value) { 266 | //Use own write() method to construct full module value. 267 | //But need to create shell that translates writeFile's 268 | //write() to the right interface. 269 | var textWrite = function (contents) { 270 | return write(fileName, contents); 271 | }; 272 | textWrite.asModule = function (moduleName, contents) { 273 | return write.asModule(moduleName, fileName, contents); 274 | }; 275 | 276 | text.write(pluginName, nonStripName, textWrite, config); 277 | }, config); 278 | } 279 | }; 280 | 281 | return text; 282 | }); 283 | }()); 284 | -------------------------------------------------------------------------------- /examples-build/js/lib/wheat.js: -------------------------------------------------------------------------------- 1 | wheat = { 2 | name:"wheat" 3 | }; -------------------------------------------------------------------------------- /examples-build/js/pizza.js: -------------------------------------------------------------------------------- 1 | pizza = { 2 | name: "pizza", 3 | ingredients: [cheese] //depends on the global cheese.js being available 4 | }; -------------------------------------------------------------------------------- /examples-build/use-path-option.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | use the path option 5 | 6 | 32 | 33 | -------------------------------------------------------------------------------- /examples/as-globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | keep as globals 5 | 6 | 27 | 28 | -------------------------------------------------------------------------------- /examples/as-modules-only.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | turn into modules with no more globals 5 | 6 | 35 | 36 | -------------------------------------------------------------------------------- /examples/as-modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | turn scripts into modules 5 | 6 | 31 | 32 | -------------------------------------------------------------------------------- /examples/build-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | load the build-time generated modules 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/js/build-example.js: -------------------------------------------------------------------------------- 1 | //see config.js for the wrapJS config being used here 2 | require(['require', 'config'], function(require){ 3 | // requiring donut will now make sure frosting is loaded first 4 | require(['wrap!donut'], function (donut) { 5 | console.log("donut: ", donut); 6 | // notice that the scripts are available as parameters to this callback, as if it were an AMD module 7 | console.log('mmm', donut.name); 8 | console.log('mmm', donut.ingredients[0].name); 9 | 10 | // also notice the donut variable is no longer available as a global 11 | console.log('look no more global: ', window.donut); 12 | }); 13 | }); -------------------------------------------------------------------------------- /examples/js/cheese.js: -------------------------------------------------------------------------------- 1 | cheese = { 2 | name:"cheese" 3 | }; -------------------------------------------------------------------------------- /examples/js/config.js: -------------------------------------------------------------------------------- 1 | //wrap the scripts that have dependencies, specify what to return for the AMD module, and remove the script's global 2 | require.config({ 3 | paths:{ 'wrap':'../../wrap', 'text':'lib/text'}, 4 | wrapJS:{ 5 | 'donut':{ 6 | deps:['wrap!frosting'], 7 | attach:function () { 8 | var donut = this.donut; // this === window (global) 9 | delete this.donut; //kill the global 10 | return donut; //tell the module what to return 11 | }, 12 | path: 'lib/donut' 13 | } 14 | ,'frosting': { 15 | attach: 'frosting', 16 | path: 'lib/frosting' 17 | } 18 | } 19 | }); -------------------------------------------------------------------------------- /examples/js/lib/candy.js: -------------------------------------------------------------------------------- 1 | candy = { 2 | name: "candy", 3 | ingredients: [sugar] //depends on the global sugar.js being available 4 | }; -------------------------------------------------------------------------------- /examples/js/lib/donut.js: -------------------------------------------------------------------------------- 1 | donut = { 2 | name: "donut", 3 | ingredients: [frosting] //depends on the global sugar.js being available 4 | }; -------------------------------------------------------------------------------- /examples/js/lib/frosting.js: -------------------------------------------------------------------------------- 1 | frosting = { 2 | name:"frosting" 3 | }; -------------------------------------------------------------------------------- /examples/js/lib/ketchup.js: -------------------------------------------------------------------------------- 1 | ketchup = { 2 | name:"ketchup" 3 | }; -------------------------------------------------------------------------------- /examples/js/lib/nachos.js: -------------------------------------------------------------------------------- 1 | var nachos = { 2 | name: 'nachos', 3 | ingredients: [cheese] //depends on the global cheese.js being available 4 | }; -------------------------------------------------------------------------------- /examples/js/lib/rolls.js: -------------------------------------------------------------------------------- 1 | rolls = { 2 | name: "rolls", 3 | ingredients: [wheat] //depends on the global wheat.js being available 4 | }; -------------------------------------------------------------------------------- /examples/js/lib/sugar.js: -------------------------------------------------------------------------------- 1 | sugar = { 2 | name:"sugar" 3 | }; -------------------------------------------------------------------------------- /examples/js/lib/tasty.hamburger.js: -------------------------------------------------------------------------------- 1 | hamburger = { 2 | name: "hamburger", 3 | ingredients: [ketchup] //depends on the global ketchup.js being available 4 | }; -------------------------------------------------------------------------------- /examples/js/lib/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 1.0.7 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 regexp: false, nomen: false, plusplus: false, strict: false */ 7 | /*global require: false, XMLHttpRequest: false, ActiveXObject: false, 8 | define: false, window: false, process: false, Packages: false, 9 | java: false, location: false */ 10 | 11 | (function () { 12 | var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 13 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 14 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 15 | hasLocation = typeof location !== 'undefined' && location.href, 16 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 17 | defaultHostName = hasLocation && location.hostname, 18 | defaultPort = hasLocation && (location.port || undefined), 19 | buildMap = []; 20 | 21 | define(function () { 22 | var text, get, fs; 23 | 24 | if (typeof window !== "undefined" && window.navigator && window.document) { 25 | get = function (url, callback) { 26 | var xhr = text.createXhr(); 27 | xhr.open('GET', url, true); 28 | xhr.onreadystatechange = function (evt) { 29 | //Do not explicitly handle errors, those should be 30 | //visible via console output in the browser. 31 | if (xhr.readyState === 4) { 32 | callback(xhr.responseText); 33 | } 34 | }; 35 | xhr.send(null); 36 | }; 37 | } else if (typeof process !== "undefined" && 38 | process.versions && 39 | !!process.versions.node) { 40 | //Using special require.nodeRequire, something added by r.js. 41 | fs = require.nodeRequire('fs'); 42 | 43 | get = function (url, callback) { 44 | var file = fs.readFileSync(url, 'utf8'); 45 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 46 | if (file.indexOf('\uFEFF') === 0) { 47 | file = file.substring(1); 48 | } 49 | callback(file); 50 | }; 51 | } else if (typeof Packages !== 'undefined') { 52 | //Why Java, why is this so awkward? 53 | get = function (url, callback) { 54 | var encoding = "utf-8", 55 | file = new java.io.File(url), 56 | lineSeparator = java.lang.System.getProperty("line.separator"), 57 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 58 | stringBuffer, line, 59 | content = ''; 60 | try { 61 | stringBuffer = new java.lang.StringBuffer(); 62 | line = input.readLine(); 63 | 64 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 65 | // http://www.unicode.org/faq/utf_bom.html 66 | 67 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 68 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 69 | if (line && line.length() && line.charAt(0) === 0xfeff) { 70 | // Eat the BOM, since we've already found the encoding on this file, 71 | // and we plan to concatenating this buffer with others; the BOM should 72 | // only appear at the top of a file. 73 | line = line.substring(1); 74 | } 75 | 76 | stringBuffer.append(line); 77 | 78 | while ((line = input.readLine()) !== null) { 79 | stringBuffer.append(lineSeparator); 80 | stringBuffer.append(line); 81 | } 82 | //Make sure we return a JavaScript string and not a Java string. 83 | content = String(stringBuffer.toString()); //String 84 | } finally { 85 | input.close(); 86 | } 87 | callback(content); 88 | }; 89 | } 90 | 91 | text = { 92 | version: '1.0.7', 93 | 94 | strip: function (content) { 95 | //Strips declarations so that external SVG and XML 96 | //documents can be added to a document without worry. Also, if the string 97 | //is an HTML document, only the part inside the body tag is returned. 98 | if (content) { 99 | content = content.replace(xmlRegExp, ""); 100 | var matches = content.match(bodyRegExp); 101 | if (matches) { 102 | content = matches[1]; 103 | } 104 | } else { 105 | content = ""; 106 | } 107 | return content; 108 | }, 109 | 110 | jsEscape: function (content) { 111 | return content.replace(/(['\\])/g, '\\$1') 112 | .replace(/[\f]/g, "\\f") 113 | .replace(/[\b]/g, "\\b") 114 | .replace(/[\n]/g, "\\n") 115 | .replace(/[\t]/g, "\\t") 116 | .replace(/[\r]/g, "\\r"); 117 | }, 118 | 119 | createXhr: function () { 120 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 121 | var xhr, i, progId; 122 | if (typeof XMLHttpRequest !== "undefined") { 123 | return new XMLHttpRequest(); 124 | } else { 125 | for (i = 0; i < 3; i++) { 126 | progId = progIds[i]; 127 | try { 128 | xhr = new ActiveXObject(progId); 129 | } catch (e) {} 130 | 131 | if (xhr) { 132 | progIds = [progId]; // so faster next time 133 | break; 134 | } 135 | } 136 | } 137 | 138 | if (!xhr) { 139 | throw new Error("createXhr(): XMLHttpRequest not available"); 140 | } 141 | 142 | return xhr; 143 | }, 144 | 145 | get: get, 146 | 147 | /** 148 | * Parses a resource name into its component parts. Resource names 149 | * look like: module/name.ext!strip, where the !strip part is 150 | * optional. 151 | * @param {String} name the resource name 152 | * @returns {Object} with properties "moduleName", "ext" and "strip" 153 | * where strip is a boolean. 154 | */ 155 | parseName: function (name) { 156 | var strip = false, index = name.indexOf("."), 157 | modName = name.substring(0, index), 158 | ext = name.substring(index + 1, name.length); 159 | 160 | index = ext.indexOf("!"); 161 | if (index !== -1) { 162 | //Pull off the strip arg. 163 | strip = ext.substring(index + 1, ext.length); 164 | strip = strip === "strip"; 165 | ext = ext.substring(0, index); 166 | } 167 | 168 | return { 169 | moduleName: modName, 170 | ext: ext, 171 | strip: strip 172 | }; 173 | }, 174 | 175 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 176 | 177 | /** 178 | * Is an URL on another domain. Only works for browser use, returns 179 | * false in non-browser environments. Only used to know if an 180 | * optimized .js version of a text resource should be loaded 181 | * instead. 182 | * @param {String} url 183 | * @returns Boolean 184 | */ 185 | useXhr: function (url, protocol, hostname, port) { 186 | var match = text.xdRegExp.exec(url), 187 | uProtocol, uHostName, uPort; 188 | if (!match) { 189 | return true; 190 | } 191 | uProtocol = match[2]; 192 | uHostName = match[3]; 193 | 194 | uHostName = uHostName.split(':'); 195 | uPort = uHostName[1]; 196 | uHostName = uHostName[0]; 197 | 198 | return (!uProtocol || uProtocol === protocol) && 199 | (!uHostName || uHostName === hostname) && 200 | ((!uPort && !uHostName) || uPort === port); 201 | }, 202 | 203 | finishLoad: function (name, strip, content, onLoad, config) { 204 | content = strip ? text.strip(content) : content; 205 | if (config.isBuild) { 206 | buildMap[name] = content; 207 | } 208 | onLoad(content); 209 | }, 210 | 211 | load: function (name, req, onLoad, config) { 212 | //Name has format: some.module.filext!strip 213 | //The strip part is optional. 214 | //if strip is present, then that means only get the string contents 215 | //inside a body tag in an HTML string. For XML/SVG content it means 216 | //removing the declarations so the content can be inserted 217 | //into the current doc without problems. 218 | 219 | // Do not bother with the work if a build and text will 220 | // not be inlined. 221 | if (config.isBuild && !config.inlineText) { 222 | onLoad(); 223 | return; 224 | } 225 | 226 | var parsed = text.parseName(name), 227 | nonStripName = parsed.moduleName + '.' + parsed.ext, 228 | url = req.toUrl(nonStripName), 229 | useXhr = (config && config.text && config.text.useXhr) || 230 | text.useXhr; 231 | 232 | //Load the text. Use XHR if possible and in a browser. 233 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 234 | text.get(url, function (content) { 235 | text.finishLoad(name, parsed.strip, content, onLoad, config); 236 | }); 237 | } else { 238 | //Need to fetch the resource across domains. Assume 239 | //the resource has been optimized into a JS module. Fetch 240 | //by the module name + extension, but do not include the 241 | //!strip part to avoid file system issues. 242 | req([nonStripName], function (content) { 243 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 244 | parsed.strip, content, onLoad, config); 245 | }); 246 | } 247 | }, 248 | 249 | write: function (pluginName, moduleName, write, config) { 250 | if (moduleName in buildMap) { 251 | var content = text.jsEscape(buildMap[moduleName]); 252 | write.asModule(pluginName + "!" + moduleName, 253 | "define(function () { return '" + 254 | content + 255 | "';});\n"); 256 | } 257 | }, 258 | 259 | writeFile: function (pluginName, moduleName, req, write, config) { 260 | var parsed = text.parseName(moduleName), 261 | nonStripName = parsed.moduleName + '.' + parsed.ext, 262 | //Use a '.js' file name so that it indicates it is a 263 | //script that can be loaded across domains. 264 | fileName = req.toUrl(parsed.moduleName + '.' + 265 | parsed.ext) + '.js'; 266 | 267 | //Leverage own load() method to load plugin value, but only 268 | //write out values that do not have the strip argument, 269 | //to avoid any potential issues with ! in file names. 270 | text.load(nonStripName, req, function (value) { 271 | //Use own write() method to construct full module value. 272 | //But need to create shell that translates writeFile's 273 | //write() to the right interface. 274 | var textWrite = function (contents) { 275 | return write(fileName, contents); 276 | }; 277 | textWrite.asModule = function (moduleName, contents) { 278 | return write.asModule(moduleName, fileName, contents); 279 | }; 280 | 281 | text.write(pluginName, nonStripName, textWrite, config); 282 | }, config); 283 | } 284 | }; 285 | 286 | return text; 287 | }); 288 | }()); 289 | -------------------------------------------------------------------------------- /examples/js/lib/wheat.js: -------------------------------------------------------------------------------- 1 | wheat = { 2 | name:"wheat" 3 | }; -------------------------------------------------------------------------------- /examples/js/pizza.js: -------------------------------------------------------------------------------- 1 | pizza = { 2 | name: "pizza", 3 | ingredients: [cheese] //depends on the global cheese.js being available 4 | }; -------------------------------------------------------------------------------- /examples/use-path-option.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | use the path option 5 | 6 | 32 | 33 | -------------------------------------------------------------------------------- /spec/browser/wrapjs-test.js: -------------------------------------------------------------------------------- 1 | buster.spec.expose(); 2 | require.config({ 3 | baseUrl: 'examples/js/', 4 | paths: { 'wrap': '../../wrap', 'text': 'lib/text', 'candy': 'lib/candy' }, 5 | wrapJS: { 6 | 'pizza': { 7 | deps: ['cheese'], 8 | attach: 'pizza' 9 | }, 10 | 'candy': { 11 | deps: ['lib/sugar'], 12 | attach: function(){ 13 | var candy = this.candy; // this === window (global) 14 | delete this.candy; //kill the global 15 | return candy; //tell the module what to return 16 | } 17 | }, 18 | 'rolls': { 19 | deps: ['lib/wheat'], 20 | attach: 'rolls', 21 | path: 'lib/rolls' 22 | }, 23 | 'lib/tasty.hamburger': { 24 | deps: ['lib/ketchup'], 25 | attach: "hamburger" 26 | } 27 | } 28 | }); 29 | 30 | describe("simple attach", function (run) { 31 | require(['wrap!pizza'], function (pza) { 32 | run(function(){ 33 | it("should be available as a global", function () { 34 | expect(window.pizza.name).toEqual('pizza'); 35 | expect(window.pizza.ingredients[0].name).toEqual('cheese'); 36 | }); 37 | 38 | it('should be available as a module', function(){ 39 | expect(pza.name).toEqual('pizza'); 40 | expect(pza.ingredients[0].name).toEqual('cheese'); 41 | }); 42 | }); 43 | }); 44 | }); 45 | 46 | describe('removing the global variable', function(run){ 47 | require(['wrap!candy'], function (candy) { 48 | run(function(){ 49 | it('should be available as a module', function(){ 50 | expect(candy.name).toEqual('candy'); 51 | expect(candy.ingredients[0].name).toEqual('sugar'); 52 | }); 53 | 54 | it('but not as a global', function(){ 55 | expect(window.candy).not().toBeDefined(); 56 | }); 57 | }); 58 | }); 59 | }); 60 | 61 | describe('using the path option', function(run){ 62 | require(['wrap!rolls'], function (rolls) { 63 | run(function(){ 64 | it('should find the right lib based on the path', function(){ 65 | expect(rolls.name).toEqual('rolls'); 66 | expect(rolls.ingredients[0].name).toEqual('wheat'); 67 | }); 68 | }); 69 | }); 70 | }); 71 | 72 | describe('paths containing a dot', function(run){ 73 | require(['wrap!lib/tasty.hamburger'], function (hamburger) { 74 | run(function(){ 75 | it('should find the right lib based on the path', function(){ 76 | expect(hamburger.name).toEqual('hamburger'); 77 | expect(hamburger.ingredients[0].name).toEqual('ketchup'); 78 | }); 79 | }); 80 | }); 81 | }); 82 | 83 | describe('post-build', function(run){ 84 | require(['../../examples-build/js/build-example'], function(){ 85 | run(function(){ 86 | it('should wrap the script in an AMD module', function(){ 87 | var wrappedDonut = require('wrap!donut'); 88 | expect(wrappedDonut).toBeDefined(); 89 | expect(wrappedDonut.name).toEqual('donut'); 90 | }); 91 | 92 | it('should not have loaded a duplicate script separately as a global', function(){ 93 | expect(window.donut).not().toBeDefined(); 94 | }); 95 | 96 | it('frosting dependency should still be global', function(){ 97 | expect(window.frosting).toBeDefined(); 98 | expect(window.frosting.name).toEqual('frosting'); 99 | }); 100 | }); 101 | }); 102 | }); -------------------------------------------------------------------------------- /spec/buster.js: -------------------------------------------------------------------------------- 1 | var config = module.exports; 2 | 3 | config["Browser Tests"] = { 4 | environment:"browser", 5 | rootPath:"../", 6 | libs:["examples/js/lib/require.js"], 7 | tests:[ 8 | "spec/browser/*.js" 9 | ], 10 | resources:['examples/js/*.js', 'examples/js/lib/*.js', 'examples-build/js/*.js', 'wrap.js'] 11 | }; -------------------------------------------------------------------------------- /spec/module/amd-test.js: -------------------------------------------------------------------------------- 1 | 2 | //IDEAL, but doesn't currently work because buster ends before it can finish 3 | require(['lib/test!http://raw.github.com/gist/3eb65c2883af6b6f2b8d/41b6fbfaf24b8106268dd3c770ad22a976afcf3e/mymodule'], function (module) { 4 | describe("AMD", function (done) { 5 | it("should work", function () { 6 | console.log('YAY'); 7 | expect(module.name).toEqual("A Module"); 8 | expect(true).toEqual(false); 9 | done(); 10 | }); 11 | }); 12 | }); 13 | 14 | //works most of the time, but only because it happens before buster thinks things are done 15 | require(['lib/test!lib/mymodule'], function(module){ 16 | buster.spec.expose(); //todo where to put this? 17 | describe("AMD", function () { 18 | it("should work", function () { 19 | expect(module.name).toEqual("A Module"); 20 | expect(module.dependsOn).toEqual("a dependency"); 21 | expect(true).toEqual(true); 22 | }); 23 | }); 24 | }); 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /spec/module/amd-test2.js: -------------------------------------------------------------------------------- 1 | require(['lib/mymodule'], function(module){ 2 | buster.spec.expose(); //todo where to put this? 3 | describe("Another AMD", function () { 4 | window.it("should work", function () { 5 | expect(module.name).toEqual("A Module"); 6 | expect(true).toEqual(true); 7 | }); 8 | }); 9 | }); 10 | 11 | 12 | -------------------------------------------------------------------------------- /spec/module/amd-test3.js: -------------------------------------------------------------------------------- 1 | buster.spec.expose(); 2 | describe('another way to test AMD', function(){ 3 | require(['lib/mymodule'], function(run){ 4 | run(function(){ 5 | console.log('loaded the thingy to test: ', module); 6 | it('should work', function () { 7 | expect(true).toEqual(true); 8 | }); 9 | }); 10 | }); 11 | }); -------------------------------------------------------------------------------- /wrap.js: -------------------------------------------------------------------------------- 1 | /* wrap.js RequireJS plugin 2 | * Copyright 2012, Dave Geddes (@geddesign) 3 | * wrap.js may be freely distributed under the MIT license. 4 | * version 0.2.2 5 | * docs: http://github.com/geddesign/wrapjs 6 | */ 7 | 8 | define(['text'], function (text) { 9 | return { 10 | buildMap:{}, 11 | load:function (name, req, load, config) { 12 | var _this = this, 13 | module = config.wrapJS && config.wrapJS[name], 14 | //use the `path` attribute if specified 15 | path = config.wrapJS[name].path || name; 16 | 17 | // if no module to load return early. 18 | if (!module) return load(); 19 | 20 | // load the wrapped script's dependencies 21 | req(module.deps || [], function () { 22 | //for the build, get the contents with the text plugin and store the contents of the script for the write() function 23 | if (config.isBuild) { 24 | text.get(req.toUrl(path), function (scriptContent) { 25 | _this.buildMap[name] = { 26 | content:scriptContent, 27 | deps:module.deps || [], 28 | attach:config.wrapJS[name].attach 29 | }; 30 | return load(); 31 | }); 32 | } 33 | else { 34 | // load the script now that dependencies are loaded. 35 | req([path], function () { 36 | // Attach property 37 | return load(getAttach(config.wrapJS[name].attach)); 38 | }); 39 | } 40 | }); 41 | }, 42 | 43 | /* write the output during the build, effectively turning the script into an AMD module */ 44 | write:function (pluginName, name, write) { 45 | var module = this.buildMap[name], 46 | deps = module.deps.map(toQuotes).join(', '), 47 | generatedName = pluginName + '!' + name, 48 | output = createModule(generatedName, deps, module.content, getReturnVal(module.attach)); 49 | write(output); 50 | } 51 | }; 52 | 53 | /* Generate AMD module to wrap the script */ 54 | function createModule(name, deps, content, ret){ 55 | return '/* script wrapped by the wrap! plugin */\n'+ 56 | 'define("' + name + '", ['+ deps + '], function(){ \n' + 57 | content + '\n' + 58 | 'return ' + ret + ';\n' + 59 | '});\n'; 60 | } 61 | 62 | /* 63 | Determines what to have the wrapping module return. Will either be a global variable 64 | or an immediate function that will execute when the module is loaded, 65 | enabling the removal of global variables once wrapped. 66 | */ 67 | function getReturnVal(attach){ 68 | if(typeof attach !== 'function') return attach; 69 | return "(function () {\n" + 70 | "var attach = "+attach+"; \n" + 71 | "return (typeof attach === 'function') ? attach.apply(this) : attach; \n" + 72 | "}())" 73 | } 74 | 75 | /* surround a value with quotes */ 76 | function toQuotes(val) { 77 | return "\"" + val + "\""; 78 | } 79 | 80 | /* return the correct attached object */ 81 | function getAttach(attach){ 82 | return (typeof attach === 'function') ? attach.apply(this, arguments) : this[attach]; 83 | } 84 | }); --------------------------------------------------------------------------------