├── .gitignore ├── README.md ├── dataset ├── mappings │ └── nfl_mapping.json └── nfl_2013.json ├── index.html └── scripts ├── d3.v3.js ├── elasticsearch.js ├── main.js └── require.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFL Elasticsearch Aggregations 2 | 3 | This repository contains the files for the [data visualization tutorial](http://www.elasticsearch.org/blog/data-visualization-elasticsearch-aggregations/) using Elasticsearch aggregations (1.0 release) and D3. 4 | -------------------------------------------------------------------------------- /dataset/mappings/nfl_mapping.json: -------------------------------------------------------------------------------- 1 | { 2 | "2013" : { 3 | "_index" : { 4 | "enabled" : true 5 | }, 6 | "_id" : { 7 | "index" : "not_analyzed", 8 | "store" : "yes" 9 | }, 10 | "properties" : { 11 | "gameid" : { 12 | "type" : "string", 13 | "index" : "not_analyzed", 14 | "store" : "yes" 15 | }, 16 | "qtr" : { 17 | "type" : "short", 18 | "store" : "yes" 19 | }, 20 | "min" : { 21 | "type" : "short", 22 | "store" : "yes", 23 | "ignore_malformed" : "true" 24 | }, 25 | "sec" : { 26 | "type" : "short", 27 | "store" : "yes", 28 | "ignore_malformed" : "true" 29 | }, 30 | "off" : { 31 | "type" : "string", 32 | "index" : "not_analyzed" 33 | }, 34 | "def" : { 35 | "type" : "string", 36 | "index" : "not_analyzed" 37 | }, 38 | "down" : { 39 | "type" : "short", 40 | "store" : "yes", 41 | "ignore_malformed" : "true" 42 | }, 43 | "togo" : { 44 | "type" : "short", 45 | "store" : "yes", 46 | "ignore_malformed" : "true" 47 | }, 48 | "ydline" : { 49 | "type" : "short", 50 | "store" : "yes", 51 | "ignore_malformed" : "true" 52 | }, 53 | "scorediff" : { 54 | "type" : "short", 55 | "store" : "yes" 56 | }, 57 | "series1stdn" : { 58 | "type" : "short", 59 | "store" : "yes" 60 | }, 61 | "description" : { 62 | "type" : "string", 63 | "store" : "yes" 64 | }, 65 | "scorechange" : { 66 | "type" : "short", 67 | "store" : "yes" 68 | }, 69 | "nextscore" : { 70 | "type" : "short", 71 | "store" : "yes" 72 | }, 73 | "teamwin" : { 74 | "type" : "short", 75 | "store" : "yes" 76 | }, 77 | "offscore" : { 78 | "type" : "short", 79 | "store" : "yes" 80 | }, 81 | "defscore" : { 82 | "type" : "short", 83 | "store" : "yes" 84 | }, 85 | "season" : { 86 | "type" : "date", 87 | "format" : "YYYY", 88 | "ignore_malformed" : "true" 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elastic Aggregations 5 | 6 | 7 | 33 | 34 | 35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Created by shelbysturgis on 1/23/14. 4 | */ 5 | 6 | define(['scripts/d3.v3', 'scripts/elasticsearch'], function (d3, elasticsearch) { 7 | 8 | "use strict"; 9 | var client = new elasticsearch.Client(); 10 | 11 | client.search({ 12 | index: 'nfl', 13 | size: 5, 14 | body: { 15 | // Begin query. 16 | query: { 17 | // Boolean query for matching and excluding items. 18 | bool: { 19 | must: { match: { "description": "TOUCHDOWN" }}, 20 | must_not: { match: { "qtr": 5 }} 21 | } 22 | }, 23 | // Aggregate on the results 24 | aggs: { 25 | touchdowns: { 26 | terms: { 27 | field: "qtr", 28 | order: { "_term" : "asc" } 29 | } 30 | } 31 | } 32 | // End query. 33 | } 34 | }).then(function (resp) { 35 | console.log(resp); 36 | 37 | // D3 code goes here. 38 | var touchdowns = resp.aggregations.touchdowns.buckets; 39 | 40 | // d3 donut chart 41 | var width = 600, 42 | height = 300, 43 | radius = Math.min(width, height) / 2; 44 | 45 | var color = ['#ff7f0e', '#d62728', '#2ca02c', '#1f77b4']; 46 | 47 | var arc = d3.svg.arc() 48 | .outerRadius(radius - 60) 49 | .innerRadius(120); 50 | 51 | var pie = d3.layout.pie() 52 | .sort(null) 53 | .value(function (d) { return d.doc_count; }); 54 | 55 | var svg = d3.select("#donut-chart").append("svg") 56 | .attr("width", width) 57 | .attr("height", height) 58 | .append("g") 59 | .attr("transform", "translate(" + width/1.4 + "," + height/2 + ")"); 60 | 61 | var g = svg.selectAll(".arc") 62 | .data(pie(touchdowns)) 63 | .enter() 64 | .append("g") 65 | .attr("class", "arc"); 66 | 67 | g.append("path") 68 | .attr("d", arc) 69 | .style("fill", function (d, i) { 70 | return color[i]; 71 | }); 72 | 73 | g.append("text") 74 | .attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; }) 75 | .attr("dy", ".35em") 76 | .style("text-anchor", "middle") 77 | .style("fill", "white") 78 | .text(function (d) { return d.data.key; }); 79 | }); 80 | 81 | client.search({ 82 | index: 'nfl', 83 | size: 5, 84 | body: { 85 | query: { 86 | bool: { 87 | must: { match: { "description": "TOUCHDOWN"}}, 88 | must_not: [ 89 | { match: { "description": "intercepted"}}, 90 | { match: { "description": "incomplete"}}, 91 | { match: { "description": "FUMBLES"}}, 92 | { match: { "description": "NULLIFIED"}} 93 | ] 94 | } 95 | }, 96 | aggs: { 97 | teams: { 98 | terms: { 99 | field: "off", 100 | exclude: "", 101 | size: 5 102 | }, 103 | aggs: { 104 | players: { 105 | terms: { 106 | field: "description", 107 | include: "([a-z]?[.][a-z]+)", 108 | size: 200 109 | }, 110 | aggs: { 111 | qtrs: { 112 | terms: { 113 | field: "qtr", 114 | order: { "_term": "asc" } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | }).then(function (resp) { 124 | console.log(resp); 125 | 126 | // D3 code goes here. 127 | var root = createChildNodes(resp); 128 | 129 | // d3 dendrogram 130 | var width = 600, 131 | height = 2000; 132 | 133 | var color = ['#ff7f0e', '#d62728', '#2ca02c', '#1f77b4']; 134 | 135 | var cluster = d3.layout.cluster() 136 | .size([height, width - 200]); 137 | 138 | var diagonal = d3.svg.diagonal() 139 | .projection(function(d) { return [d.y, d.x]; }); 140 | 141 | var svg = d3.select("#dendrogram").append("svg") 142 | .attr("width", width) 143 | .attr("height", height) 144 | .append("g") 145 | .attr("transform", "translate(120,0)"); 146 | 147 | var nodes = cluster.nodes(root), 148 | links = cluster.links(nodes); 149 | 150 | var link = svg.selectAll(".link") 151 | .data(links) 152 | .enter().append("path") 153 | .attr("class", "link") 154 | .attr("d", diagonal); 155 | 156 | var node = svg.selectAll(".node") 157 | .data(nodes) 158 | .enter().append("g") 159 | .attr("class", "node") 160 | .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); 161 | 162 | node.append("circle") 163 | .attr("r", 4.5) 164 | .style("fill", function (d) { 165 | return d.children ? "#ffffff" : color[+d.key - 1]; 166 | }) 167 | .style("stroke", function (d) { 168 | return d.children ? "#4682B4" : color[+d.key - 1]; 169 | }); 170 | 171 | node.append("text") 172 | .attr("dx", function(d) { return d.children ? -8 : 8; }) 173 | .attr("dy", 3) 174 | .style("text-anchor", function(d) { return d.children ? "end" : "start"; }) 175 | .text(function(d) { return d.children? d.key : d.key + ": " + d.doc_count; }); 176 | 177 | d3.select(self.frameElement).style("height", height + "px"); 178 | 179 | function createChildNodes(dataObj) { 180 | var root = {}; 181 | 182 | root.key = "NFL"; 183 | root.children = dataObj.aggregations.teams.buckets; 184 | root.children.forEach(function (d) { d.children = d.players.buckets; }); 185 | root.children.forEach(function (d) { 186 | d.children.forEach(function (d) { 187 | d.children = d.qtrs.buckets; 188 | }); 189 | }); 190 | 191 | return root; 192 | } 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /scripts/require.js: -------------------------------------------------------------------------------- 1 | /** vim: et:ts=4:sw=4:sts=4 2 | * @license RequireJS 2.1.10 Copyright (c) 2010-2014, 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 | //Not using strict: uneven strict support in browsers, #392, and causes 7 | //problems with requirejs.exec()/transpiler plugins that may not be strict. 8 | /*jslint regexp: true, nomen: true, sloppy: true */ 9 | /*global window, navigator, document, importScripts, setTimeout, opera */ 10 | 11 | var requirejs, require, define; 12 | (function (global) { 13 | var req, s, head, baseElement, dataMain, src, 14 | interactiveScript, currentlyAddingScript, mainScript, subPath, 15 | version = '2.1.10', 16 | commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, 17 | cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, 18 | jsSuffixRegExp = /\.js$/, 19 | currDirRegExp = /^\.\//, 20 | op = Object.prototype, 21 | ostring = op.toString, 22 | hasOwn = op.hasOwnProperty, 23 | ap = Array.prototype, 24 | apsp = ap.splice, 25 | isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), 26 | isWebWorker = !isBrowser && typeof importScripts !== 'undefined', 27 | //PS3 indicates loaded and complete, but need to wait for complete 28 | //specifically. Sequence is 'loading', 'loaded', execution, 29 | // then 'complete'. The UA check is unfortunate, but not sure how 30 | //to feature test w/o causing perf issues. 31 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? 32 | /^complete$/ : /^(complete|loaded)$/, 33 | defContextName = '_', 34 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason. 35 | isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', 36 | contexts = {}, 37 | cfg = {}, 38 | globalDefQueue = [], 39 | useInteractive = false; 40 | 41 | function isFunction(it) { 42 | return ostring.call(it) === '[object Function]'; 43 | } 44 | 45 | function isArray(it) { 46 | return ostring.call(it) === '[object Array]'; 47 | } 48 | 49 | /** 50 | * Helper function for iterating over an array. If the func returns 51 | * a true value, it will break out of the loop. 52 | */ 53 | function each(ary, func) { 54 | if (ary) { 55 | var i; 56 | for (i = 0; i < ary.length; i += 1) { 57 | if (ary[i] && func(ary[i], i, ary)) { 58 | break; 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Helper function for iterating over an array backwards. If the func 66 | * returns a true value, it will break out of the loop. 67 | */ 68 | function eachReverse(ary, func) { 69 | if (ary) { 70 | var i; 71 | for (i = ary.length - 1; i > -1; i -= 1) { 72 | if (ary[i] && func(ary[i], i, ary)) { 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | 79 | function hasProp(obj, prop) { 80 | return hasOwn.call(obj, prop); 81 | } 82 | 83 | function getOwn(obj, prop) { 84 | return hasProp(obj, prop) && obj[prop]; 85 | } 86 | 87 | /** 88 | * Cycles over properties in an object and calls a function for each 89 | * property value. If the function returns a truthy value, then the 90 | * iteration is stopped. 91 | */ 92 | function eachProp(obj, func) { 93 | var prop; 94 | for (prop in obj) { 95 | if (hasProp(obj, prop)) { 96 | if (func(obj[prop], prop)) { 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Simple function to mix in properties from source into target, 105 | * but only if target does not already have a property of the same name. 106 | */ 107 | function mixin(target, source, force, deepStringMixin) { 108 | if (source) { 109 | eachProp(source, function (value, prop) { 110 | if (force || !hasProp(target, prop)) { 111 | if (deepStringMixin && typeof value === 'object' && value && 112 | !isArray(value) && !isFunction(value) && 113 | !(value instanceof RegExp)) { 114 | 115 | if (!target[prop]) { 116 | target[prop] = {}; 117 | } 118 | mixin(target[prop], value, force, deepStringMixin); 119 | } else { 120 | target[prop] = value; 121 | } 122 | } 123 | }); 124 | } 125 | return target; 126 | } 127 | 128 | //Similar to Function.prototype.bind, but the 'this' object is specified 129 | //first, since it is easier to read/figure out what 'this' will be. 130 | function bind(obj, fn) { 131 | return function () { 132 | return fn.apply(obj, arguments); 133 | }; 134 | } 135 | 136 | function scripts() { 137 | return document.getElementsByTagName('script'); 138 | } 139 | 140 | function defaultOnError(err) { 141 | throw err; 142 | } 143 | 144 | //Allow getting a global that expressed in 145 | //dot notation, like 'a.b.c'. 146 | function getGlobal(value) { 147 | if (!value) { 148 | return value; 149 | } 150 | var g = global; 151 | each(value.split('.'), function (part) { 152 | g = g[part]; 153 | }); 154 | return g; 155 | } 156 | 157 | /** 158 | * Constructs an error with a pointer to an URL with more information. 159 | * @param {String} id the error ID that maps to an ID on a web page. 160 | * @param {String} message human readable error. 161 | * @param {Error} [err] the original error, if there is one. 162 | * 163 | * @returns {Error} 164 | */ 165 | function makeError(id, msg, err, requireModules) { 166 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); 167 | e.requireType = id; 168 | e.requireModules = requireModules; 169 | if (err) { 170 | e.originalError = err; 171 | } 172 | return e; 173 | } 174 | 175 | if (typeof define !== 'undefined') { 176 | //If a define is already in play via another AMD loader, 177 | //do not overwrite. 178 | return; 179 | } 180 | 181 | if (typeof requirejs !== 'undefined') { 182 | if (isFunction(requirejs)) { 183 | //Do not overwrite and existing requirejs instance. 184 | return; 185 | } 186 | cfg = requirejs; 187 | requirejs = undefined; 188 | } 189 | 190 | //Allow for a require config object 191 | if (typeof require !== 'undefined' && !isFunction(require)) { 192 | //assume it is a config object. 193 | cfg = require; 194 | require = undefined; 195 | } 196 | 197 | function newContext(contextName) { 198 | var inCheckLoaded, Module, context, handlers, 199 | checkLoadedTimeoutId, 200 | config = { 201 | //Defaults. Do not set a default for map 202 | //config to speed up normalize(), which 203 | //will run faster if there is no default. 204 | waitSeconds: 7, 205 | baseUrl: './', 206 | paths: {}, 207 | bundles: {}, 208 | pkgs: {}, 209 | shim: {}, 210 | config: {} 211 | }, 212 | registry = {}, 213 | //registry of just enabled modules, to speed 214 | //cycle breaking code when lots of modules 215 | //are registered, but not activated. 216 | enabledRegistry = {}, 217 | undefEvents = {}, 218 | defQueue = [], 219 | defined = {}, 220 | urlFetched = {}, 221 | bundlesMap = {}, 222 | requireCounter = 1, 223 | unnormalizedCounter = 1; 224 | 225 | /** 226 | * Trims the . and .. from an array of path segments. 227 | * It will keep a leading path segment if a .. will become 228 | * the first path segment, to help with module name lookups, 229 | * which act like paths, but can be remapped. But the end result, 230 | * all paths that use this function should look normalized. 231 | * NOTE: this method MODIFIES the input array. 232 | * @param {Array} ary the array of path segments. 233 | */ 234 | function trimDots(ary) { 235 | var i, part, length = ary.length; 236 | for (i = 0; i < length; i++) { 237 | part = ary[i]; 238 | if (part === '.') { 239 | ary.splice(i, 1); 240 | i -= 1; 241 | } else if (part === '..') { 242 | if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { 243 | //End of the line. Keep at least one non-dot 244 | //path segment at the front so it can be mapped 245 | //correctly to disk. Otherwise, there is likely 246 | //no path mapping for a path starting with '..'. 247 | //This can still fail, but catches the most reasonable 248 | //uses of .. 249 | break; 250 | } else if (i > 0) { 251 | ary.splice(i - 1, 2); 252 | i -= 2; 253 | } 254 | } 255 | } 256 | } 257 | 258 | /** 259 | * Given a relative module name, like ./something, normalize it to 260 | * a real name that can be mapped to a path. 261 | * @param {String} name the relative name 262 | * @param {String} baseName a real name that the name arg is relative 263 | * to. 264 | * @param {Boolean} applyMap apply the map config to the value. Should 265 | * only be done if this normalization is for a dependency ID. 266 | * @returns {String} normalized name 267 | */ 268 | function normalize(name, baseName, applyMap) { 269 | var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, 270 | foundMap, foundI, foundStarMap, starI, 271 | baseParts = baseName && baseName.split('/'), 272 | normalizedBaseParts = baseParts, 273 | map = config.map, 274 | starMap = map && map['*']; 275 | 276 | //Adjust any relative paths. 277 | if (name && name.charAt(0) === '.') { 278 | //If have a base name, try to normalize against it, 279 | //otherwise, assume it is a top-level require that will 280 | //be relative to baseUrl in the end. 281 | if (baseName) { 282 | //Convert baseName to array, and lop off the last part, 283 | //so that . matches that 'directory' and not name of the baseName's 284 | //module. For instance, baseName of 'one/two/three', maps to 285 | //'one/two/three.js', but we want the directory, 'one/two' for 286 | //this normalization. 287 | normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); 288 | name = name.split('/'); 289 | lastIndex = name.length - 1; 290 | 291 | // If wanting node ID compatibility, strip .js from end 292 | // of IDs. Have to do this here, and not in nameToUrl 293 | // because node allows either .js or non .js to map 294 | // to same file. 295 | if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { 296 | name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); 297 | } 298 | 299 | name = normalizedBaseParts.concat(name); 300 | trimDots(name); 301 | name = name.join('/'); 302 | } else if (name.indexOf('./') === 0) { 303 | // No baseName, so this is ID is resolved relative 304 | // to baseUrl, pull off the leading dot. 305 | name = name.substring(2); 306 | } 307 | } 308 | 309 | //Apply map config if available. 310 | if (applyMap && map && (baseParts || starMap)) { 311 | nameParts = name.split('/'); 312 | 313 | outerLoop: for (i = nameParts.length; i > 0; i -= 1) { 314 | nameSegment = nameParts.slice(0, i).join('/'); 315 | 316 | if (baseParts) { 317 | //Find the longest baseName segment match in the config. 318 | //So, do joins on the biggest to smallest lengths of baseParts. 319 | for (j = baseParts.length; j > 0; j -= 1) { 320 | mapValue = getOwn(map, baseParts.slice(0, j).join('/')); 321 | 322 | //baseName segment has config, find if it has one for 323 | //this name. 324 | if (mapValue) { 325 | mapValue = getOwn(mapValue, nameSegment); 326 | if (mapValue) { 327 | //Match, update name to the new value. 328 | foundMap = mapValue; 329 | foundI = i; 330 | break outerLoop; 331 | } 332 | } 333 | } 334 | } 335 | 336 | //Check for a star map match, but just hold on to it, 337 | //if there is a shorter segment match later in a matching 338 | //config, then favor over this star map. 339 | if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { 340 | foundStarMap = getOwn(starMap, nameSegment); 341 | starI = i; 342 | } 343 | } 344 | 345 | if (!foundMap && foundStarMap) { 346 | foundMap = foundStarMap; 347 | foundI = starI; 348 | } 349 | 350 | if (foundMap) { 351 | nameParts.splice(0, foundI, foundMap); 352 | name = nameParts.join('/'); 353 | } 354 | } 355 | 356 | // If the name points to a package's name, use 357 | // the package main instead. 358 | pkgMain = getOwn(config.pkgs, name); 359 | 360 | return pkgMain ? pkgMain : name; 361 | } 362 | 363 | function removeScript(name) { 364 | if (isBrowser) { 365 | each(scripts(), function (scriptNode) { 366 | if (scriptNode.getAttribute('data-requiremodule') === name && 367 | scriptNode.getAttribute('data-requirecontext') === context.contextName) { 368 | scriptNode.parentNode.removeChild(scriptNode); 369 | return true; 370 | } 371 | }); 372 | } 373 | } 374 | 375 | function hasPathFallback(id) { 376 | var pathConfig = getOwn(config.paths, id); 377 | if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { 378 | //Pop off the first array value, since it failed, and 379 | //retry 380 | pathConfig.shift(); 381 | context.require.undef(id); 382 | context.require([id]); 383 | return true; 384 | } 385 | } 386 | 387 | //Turns a plugin!resource to [plugin, resource] 388 | //with the plugin being undefined if the name 389 | //did not have a plugin prefix. 390 | function splitPrefix(name) { 391 | var prefix, 392 | index = name ? name.indexOf('!') : -1; 393 | if (index > -1) { 394 | prefix = name.substring(0, index); 395 | name = name.substring(index + 1, name.length); 396 | } 397 | return [prefix, name]; 398 | } 399 | 400 | /** 401 | * Creates a module mapping that includes plugin prefix, module 402 | * name, and path. If parentModuleMap is provided it will 403 | * also normalize the name via require.normalize() 404 | * 405 | * @param {String} name the module name 406 | * @param {String} [parentModuleMap] parent module map 407 | * for the module name, used to resolve relative names. 408 | * @param {Boolean} isNormalized: is the ID already normalized. 409 | * This is true if this call is done for a define() module ID. 410 | * @param {Boolean} applyMap: apply the map config to the ID. 411 | * Should only be true if this map is for a dependency. 412 | * 413 | * @returns {Object} 414 | */ 415 | function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { 416 | var url, pluginModule, suffix, nameParts, 417 | prefix = null, 418 | parentName = parentModuleMap ? parentModuleMap.name : null, 419 | originalName = name, 420 | isDefine = true, 421 | normalizedName = ''; 422 | 423 | //If no name, then it means it is a require call, generate an 424 | //internal name. 425 | if (!name) { 426 | isDefine = false; 427 | name = '_@r' + (requireCounter += 1); 428 | } 429 | 430 | nameParts = splitPrefix(name); 431 | prefix = nameParts[0]; 432 | name = nameParts[1]; 433 | 434 | if (prefix) { 435 | prefix = normalize(prefix, parentName, applyMap); 436 | pluginModule = getOwn(defined, prefix); 437 | } 438 | 439 | //Account for relative paths if there is a base name. 440 | if (name) { 441 | if (prefix) { 442 | if (pluginModule && pluginModule.normalize) { 443 | //Plugin is loaded, use its normalize method. 444 | normalizedName = pluginModule.normalize(name, function (name) { 445 | return normalize(name, parentName, applyMap); 446 | }); 447 | } else { 448 | normalizedName = normalize(name, parentName, applyMap); 449 | } 450 | } else { 451 | //A regular module. 452 | normalizedName = normalize(name, parentName, applyMap); 453 | 454 | //Normalized name may be a plugin ID due to map config 455 | //application in normalize. The map config values must 456 | //already be normalized, so do not need to redo that part. 457 | nameParts = splitPrefix(normalizedName); 458 | prefix = nameParts[0]; 459 | normalizedName = nameParts[1]; 460 | isNormalized = true; 461 | 462 | url = context.nameToUrl(normalizedName); 463 | } 464 | } 465 | 466 | //If the id is a plugin id that cannot be determined if it needs 467 | //normalization, stamp it with a unique ID so two matching relative 468 | //ids that may conflict can be separate. 469 | suffix = prefix && !pluginModule && !isNormalized ? 470 | '_unnormalized' + (unnormalizedCounter += 1) : 471 | ''; 472 | 473 | return { 474 | prefix: prefix, 475 | name: normalizedName, 476 | parentMap: parentModuleMap, 477 | unnormalized: !!suffix, 478 | url: url, 479 | originalName: originalName, 480 | isDefine: isDefine, 481 | id: (prefix ? 482 | prefix + '!' + normalizedName : 483 | normalizedName) + suffix 484 | }; 485 | } 486 | 487 | function getModule(depMap) { 488 | var id = depMap.id, 489 | mod = getOwn(registry, id); 490 | 491 | if (!mod) { 492 | mod = registry[id] = new context.Module(depMap); 493 | } 494 | 495 | return mod; 496 | } 497 | 498 | function on(depMap, name, fn) { 499 | var id = depMap.id, 500 | mod = getOwn(registry, id); 501 | 502 | if (hasProp(defined, id) && 503 | (!mod || mod.defineEmitComplete)) { 504 | if (name === 'defined') { 505 | fn(defined[id]); 506 | } 507 | } else { 508 | mod = getModule(depMap); 509 | if (mod.error && name === 'error') { 510 | fn(mod.error); 511 | } else { 512 | mod.on(name, fn); 513 | } 514 | } 515 | } 516 | 517 | function onError(err, errback) { 518 | var ids = err.requireModules, 519 | notified = false; 520 | 521 | if (errback) { 522 | errback(err); 523 | } else { 524 | each(ids, function (id) { 525 | var mod = getOwn(registry, id); 526 | if (mod) { 527 | //Set error on module, so it skips timeout checks. 528 | mod.error = err; 529 | if (mod.events.error) { 530 | notified = true; 531 | mod.emit('error', err); 532 | } 533 | } 534 | }); 535 | 536 | if (!notified) { 537 | req.onError(err); 538 | } 539 | } 540 | } 541 | 542 | /** 543 | * Internal method to transfer globalQueue items to this context's 544 | * defQueue. 545 | */ 546 | function takeGlobalQueue() { 547 | //Push all the globalDefQueue items into the context's defQueue 548 | if (globalDefQueue.length) { 549 | //Array splice in the values since the context code has a 550 | //local var ref to defQueue, so cannot just reassign the one 551 | //on context. 552 | apsp.apply(defQueue, 553 | [defQueue.length, 0].concat(globalDefQueue)); 554 | globalDefQueue = []; 555 | } 556 | } 557 | 558 | handlers = { 559 | 'require': function (mod) { 560 | if (mod.require) { 561 | return mod.require; 562 | } else { 563 | return (mod.require = context.makeRequire(mod.map)); 564 | } 565 | }, 566 | 'exports': function (mod) { 567 | mod.usingExports = true; 568 | if (mod.map.isDefine) { 569 | if (mod.exports) { 570 | return mod.exports; 571 | } else { 572 | return (mod.exports = defined[mod.map.id] = {}); 573 | } 574 | } 575 | }, 576 | 'module': function (mod) { 577 | if (mod.module) { 578 | return mod.module; 579 | } else { 580 | return (mod.module = { 581 | id: mod.map.id, 582 | uri: mod.map.url, 583 | config: function () { 584 | return getOwn(config.config, mod.map.id) || {}; 585 | }, 586 | exports: handlers.exports(mod) 587 | }); 588 | } 589 | } 590 | }; 591 | 592 | function cleanRegistry(id) { 593 | //Clean up machinery used for waiting modules. 594 | delete registry[id]; 595 | delete enabledRegistry[id]; 596 | } 597 | 598 | function breakCycle(mod, traced, processed) { 599 | var id = mod.map.id; 600 | 601 | if (mod.error) { 602 | mod.emit('error', mod.error); 603 | } else { 604 | traced[id] = true; 605 | each(mod.depMaps, function (depMap, i) { 606 | var depId = depMap.id, 607 | dep = getOwn(registry, depId); 608 | 609 | //Only force things that have not completed 610 | //being defined, so still in the registry, 611 | //and only if it has not been matched up 612 | //in the module already. 613 | if (dep && !mod.depMatched[i] && !processed[depId]) { 614 | if (getOwn(traced, depId)) { 615 | mod.defineDep(i, defined[depId]); 616 | mod.check(); //pass false? 617 | } else { 618 | breakCycle(dep, traced, processed); 619 | } 620 | } 621 | }); 622 | processed[id] = true; 623 | } 624 | } 625 | 626 | function checkLoaded() { 627 | var err, usingPathFallback, 628 | waitInterval = config.waitSeconds * 1000, 629 | //It is possible to disable the wait interval by using waitSeconds of 0. 630 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), 631 | noLoads = [], 632 | reqCalls = [], 633 | stillLoading = false, 634 | needCycleCheck = true; 635 | 636 | //Do not bother if this call was a result of a cycle break. 637 | if (inCheckLoaded) { 638 | return; 639 | } 640 | 641 | inCheckLoaded = true; 642 | 643 | //Figure out the state of all the modules. 644 | eachProp(enabledRegistry, function (mod) { 645 | var map = mod.map, 646 | modId = map.id; 647 | 648 | //Skip things that are not enabled or in error state. 649 | if (!mod.enabled) { 650 | return; 651 | } 652 | 653 | if (!map.isDefine) { 654 | reqCalls.push(mod); 655 | } 656 | 657 | if (!mod.error) { 658 | //If the module should be executed, and it has not 659 | //been inited and time is up, remember it. 660 | if (!mod.inited && expired) { 661 | if (hasPathFallback(modId)) { 662 | usingPathFallback = true; 663 | stillLoading = true; 664 | } else { 665 | noLoads.push(modId); 666 | removeScript(modId); 667 | } 668 | } else if (!mod.inited && mod.fetched && map.isDefine) { 669 | stillLoading = true; 670 | if (!map.prefix) { 671 | //No reason to keep looking for unfinished 672 | //loading. If the only stillLoading is a 673 | //plugin resource though, keep going, 674 | //because it may be that a plugin resource 675 | //is waiting on a non-plugin cycle. 676 | return (needCycleCheck = false); 677 | } 678 | } 679 | } 680 | }); 681 | 682 | if (expired && noLoads.length) { 683 | //If wait time expired, throw error of unloaded modules. 684 | err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); 685 | err.contextName = context.contextName; 686 | return onError(err); 687 | } 688 | 689 | //Not expired, check for a cycle. 690 | if (needCycleCheck) { 691 | each(reqCalls, function (mod) { 692 | breakCycle(mod, {}, {}); 693 | }); 694 | } 695 | 696 | //If still waiting on loads, and the waiting load is something 697 | //other than a plugin resource, or there are still outstanding 698 | //scripts, then just try back later. 699 | if ((!expired || usingPathFallback) && stillLoading) { 700 | //Something is still waiting to load. Wait for it, but only 701 | //if a timeout is not already in effect. 702 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 703 | checkLoadedTimeoutId = setTimeout(function () { 704 | checkLoadedTimeoutId = 0; 705 | checkLoaded(); 706 | }, 50); 707 | } 708 | } 709 | 710 | inCheckLoaded = false; 711 | } 712 | 713 | Module = function (map) { 714 | this.events = getOwn(undefEvents, map.id) || {}; 715 | this.map = map; 716 | this.shim = getOwn(config.shim, map.id); 717 | this.depExports = []; 718 | this.depMaps = []; 719 | this.depMatched = []; 720 | this.pluginMaps = {}; 721 | this.depCount = 0; 722 | 723 | /* this.exports this.factory 724 | this.depMaps = [], 725 | this.enabled, this.fetched 726 | */ 727 | }; 728 | 729 | Module.prototype = { 730 | init: function (depMaps, factory, errback, options) { 731 | options = options || {}; 732 | 733 | //Do not do more inits if already done. Can happen if there 734 | //are multiple define calls for the same module. That is not 735 | //a normal, common case, but it is also not unexpected. 736 | if (this.inited) { 737 | return; 738 | } 739 | 740 | this.factory = factory; 741 | 742 | if (errback) { 743 | //Register for errors on this module. 744 | this.on('error', errback); 745 | } else if (this.events.error) { 746 | //If no errback already, but there are error listeners 747 | //on this module, set up an errback to pass to the deps. 748 | errback = bind(this, function (err) { 749 | this.emit('error', err); 750 | }); 751 | } 752 | 753 | //Do a copy of the dependency array, so that 754 | //source inputs are not modified. For example 755 | //"shim" deps are passed in here directly, and 756 | //doing a direct modification of the depMaps array 757 | //would affect that config. 758 | this.depMaps = depMaps && depMaps.slice(0); 759 | 760 | this.errback = errback; 761 | 762 | //Indicate this module has be initialized 763 | this.inited = true; 764 | 765 | this.ignore = options.ignore; 766 | 767 | //Could have option to init this module in enabled mode, 768 | //or could have been previously marked as enabled. However, 769 | //the dependencies are not known until init is called. So 770 | //if enabled previously, now trigger dependencies as enabled. 771 | if (options.enabled || this.enabled) { 772 | //Enable this module and dependencies. 773 | //Will call this.check() 774 | this.enable(); 775 | } else { 776 | this.check(); 777 | } 778 | }, 779 | 780 | defineDep: function (i, depExports) { 781 | //Because of cycles, defined callback for a given 782 | //export can be called more than once. 783 | if (!this.depMatched[i]) { 784 | this.depMatched[i] = true; 785 | this.depCount -= 1; 786 | this.depExports[i] = depExports; 787 | } 788 | }, 789 | 790 | fetch: function () { 791 | if (this.fetched) { 792 | return; 793 | } 794 | this.fetched = true; 795 | 796 | context.startTime = (new Date()).getTime(); 797 | 798 | var map = this.map; 799 | 800 | //If the manager is for a plugin managed resource, 801 | //ask the plugin to load it now. 802 | if (this.shim) { 803 | context.makeRequire(this.map, { 804 | enableBuildCallback: true 805 | })(this.shim.deps || [], bind(this, function () { 806 | return map.prefix ? this.callPlugin() : this.load(); 807 | })); 808 | } else { 809 | //Regular dependency. 810 | return map.prefix ? this.callPlugin() : this.load(); 811 | } 812 | }, 813 | 814 | load: function () { 815 | var url = this.map.url; 816 | 817 | //Regular dependency. 818 | if (!urlFetched[url]) { 819 | urlFetched[url] = true; 820 | context.load(this.map.id, url); 821 | } 822 | }, 823 | 824 | /** 825 | * Checks if the module is ready to define itself, and if so, 826 | * define it. 827 | */ 828 | check: function () { 829 | if (!this.enabled || this.enabling) { 830 | return; 831 | } 832 | 833 | var err, cjsModule, 834 | id = this.map.id, 835 | depExports = this.depExports, 836 | exports = this.exports, 837 | factory = this.factory; 838 | 839 | if (!this.inited) { 840 | this.fetch(); 841 | } else if (this.error) { 842 | this.emit('error', this.error); 843 | } else if (!this.defining) { 844 | //The factory could trigger another require call 845 | //that would result in checking this module to 846 | //define itself again. If already in the process 847 | //of doing that, skip this work. 848 | this.defining = true; 849 | 850 | if (this.depCount < 1 && !this.defined) { 851 | if (isFunction(factory)) { 852 | //If there is an error listener, favor passing 853 | //to that instead of throwing an error. However, 854 | //only do it for define()'d modules. require 855 | //errbacks should not be called for failures in 856 | //their callbacks (#699). However if a global 857 | //onError is set, use that. 858 | if ((this.events.error && this.map.isDefine) || 859 | req.onError !== defaultOnError) { 860 | try { 861 | exports = context.execCb(id, factory, depExports, exports); 862 | } catch (e) { 863 | err = e; 864 | } 865 | } else { 866 | exports = context.execCb(id, factory, depExports, exports); 867 | } 868 | 869 | // Favor return value over exports. If node/cjs in play, 870 | // then will not have a return value anyway. Favor 871 | // module.exports assignment over exports object. 872 | if (this.map.isDefine && exports === undefined) { 873 | cjsModule = this.module; 874 | if (cjsModule) { 875 | exports = cjsModule.exports; 876 | } else if (this.usingExports) { 877 | //exports already set the defined value. 878 | exports = this.exports; 879 | } 880 | } 881 | 882 | if (err) { 883 | err.requireMap = this.map; 884 | err.requireModules = this.map.isDefine ? [this.map.id] : null; 885 | err.requireType = this.map.isDefine ? 'define' : 'require'; 886 | return onError((this.error = err)); 887 | } 888 | 889 | } else { 890 | //Just a literal value 891 | exports = factory; 892 | } 893 | 894 | this.exports = exports; 895 | 896 | if (this.map.isDefine && !this.ignore) { 897 | defined[id] = exports; 898 | 899 | if (req.onResourceLoad) { 900 | req.onResourceLoad(context, this.map, this.depMaps); 901 | } 902 | } 903 | 904 | //Clean up 905 | cleanRegistry(id); 906 | 907 | this.defined = true; 908 | } 909 | 910 | //Finished the define stage. Allow calling check again 911 | //to allow define notifications below in the case of a 912 | //cycle. 913 | this.defining = false; 914 | 915 | if (this.defined && !this.defineEmitted) { 916 | this.defineEmitted = true; 917 | this.emit('defined', this.exports); 918 | this.defineEmitComplete = true; 919 | } 920 | 921 | } 922 | }, 923 | 924 | callPlugin: function () { 925 | var map = this.map, 926 | id = map.id, 927 | //Map already normalized the prefix. 928 | pluginMap = makeModuleMap(map.prefix); 929 | 930 | //Mark this as a dependency for this plugin, so it 931 | //can be traced for cycles. 932 | this.depMaps.push(pluginMap); 933 | 934 | on(pluginMap, 'defined', bind(this, function (plugin) { 935 | var load, normalizedMap, normalizedMod, 936 | bundleId = getOwn(bundlesMap, this.map.id), 937 | name = this.map.name, 938 | parentName = this.map.parentMap ? this.map.parentMap.name : null, 939 | localRequire = context.makeRequire(map.parentMap, { 940 | enableBuildCallback: true 941 | }); 942 | 943 | //If current map is not normalized, wait for that 944 | //normalized name to load instead of continuing. 945 | if (this.map.unnormalized) { 946 | //Normalize the ID if the plugin allows it. 947 | if (plugin.normalize) { 948 | name = plugin.normalize(name, function (name) { 949 | return normalize(name, parentName, true); 950 | }) || ''; 951 | } 952 | 953 | //prefix and name should already be normalized, no need 954 | //for applying map config again either. 955 | normalizedMap = makeModuleMap(map.prefix + '!' + name, 956 | this.map.parentMap); 957 | on(normalizedMap, 958 | 'defined', bind(this, function (value) { 959 | this.init([], function () { return value; }, null, { 960 | enabled: true, 961 | ignore: true 962 | }); 963 | })); 964 | 965 | normalizedMod = getOwn(registry, normalizedMap.id); 966 | if (normalizedMod) { 967 | //Mark this as a dependency for this plugin, so it 968 | //can be traced for cycles. 969 | this.depMaps.push(normalizedMap); 970 | 971 | if (this.events.error) { 972 | normalizedMod.on('error', bind(this, function (err) { 973 | this.emit('error', err); 974 | })); 975 | } 976 | normalizedMod.enable(); 977 | } 978 | 979 | return; 980 | } 981 | 982 | //If a paths config, then just load that file instead to 983 | //resolve the plugin, as it is built into that paths layer. 984 | if (bundleId) { 985 | this.map.url = context.nameToUrl(bundleId); 986 | this.load(); 987 | return; 988 | } 989 | 990 | load = bind(this, function (value) { 991 | this.init([], function () { return value; }, null, { 992 | enabled: true 993 | }); 994 | }); 995 | 996 | load.error = bind(this, function (err) { 997 | this.inited = true; 998 | this.error = err; 999 | err.requireModules = [id]; 1000 | 1001 | //Remove temp unnormalized modules for this module, 1002 | //since they will never be resolved otherwise now. 1003 | eachProp(registry, function (mod) { 1004 | if (mod.map.id.indexOf(id + '_unnormalized') === 0) { 1005 | cleanRegistry(mod.map.id); 1006 | } 1007 | }); 1008 | 1009 | onError(err); 1010 | }); 1011 | 1012 | //Allow plugins to load other code without having to know the 1013 | //context or how to 'complete' the load. 1014 | load.fromText = bind(this, function (text, textAlt) { 1015 | /*jslint evil: true */ 1016 | var moduleName = map.name, 1017 | moduleMap = makeModuleMap(moduleName), 1018 | hasInteractive = useInteractive; 1019 | 1020 | //As of 2.1.0, support just passing the text, to reinforce 1021 | //fromText only being called once per resource. Still 1022 | //support old style of passing moduleName but discard 1023 | //that moduleName in favor of the internal ref. 1024 | if (textAlt) { 1025 | text = textAlt; 1026 | } 1027 | 1028 | //Turn off interactive script matching for IE for any define 1029 | //calls in the text, then turn it back on at the end. 1030 | if (hasInteractive) { 1031 | useInteractive = false; 1032 | } 1033 | 1034 | //Prime the system by creating a module instance for 1035 | //it. 1036 | getModule(moduleMap); 1037 | 1038 | //Transfer any config to this other module. 1039 | if (hasProp(config.config, id)) { 1040 | config.config[moduleName] = config.config[id]; 1041 | } 1042 | 1043 | try { 1044 | req.exec(text); 1045 | } catch (e) { 1046 | return onError(makeError('fromtexteval', 1047 | 'fromText eval for ' + id + 1048 | ' failed: ' + e, 1049 | e, 1050 | [id])); 1051 | } 1052 | 1053 | if (hasInteractive) { 1054 | useInteractive = true; 1055 | } 1056 | 1057 | //Mark this as a dependency for the plugin 1058 | //resource 1059 | this.depMaps.push(moduleMap); 1060 | 1061 | //Support anonymous modules. 1062 | context.completeLoad(moduleName); 1063 | 1064 | //Bind the value of that module to the value for this 1065 | //resource ID. 1066 | localRequire([moduleName], load); 1067 | }); 1068 | 1069 | //Use parentName here since the plugin's name is not reliable, 1070 | //could be some weird string with no path that actually wants to 1071 | //reference the parentName's path. 1072 | plugin.load(map.name, localRequire, load, config); 1073 | })); 1074 | 1075 | context.enable(pluginMap, this); 1076 | this.pluginMaps[pluginMap.id] = pluginMap; 1077 | }, 1078 | 1079 | enable: function () { 1080 | enabledRegistry[this.map.id] = this; 1081 | this.enabled = true; 1082 | 1083 | //Set flag mentioning that the module is enabling, 1084 | //so that immediate calls to the defined callbacks 1085 | //for dependencies do not trigger inadvertent load 1086 | //with the depCount still being zero. 1087 | this.enabling = true; 1088 | 1089 | //Enable each dependency 1090 | each(this.depMaps, bind(this, function (depMap, i) { 1091 | var id, mod, handler; 1092 | 1093 | if (typeof depMap === 'string') { 1094 | //Dependency needs to be converted to a depMap 1095 | //and wired up to this module. 1096 | depMap = makeModuleMap(depMap, 1097 | (this.map.isDefine ? this.map : this.map.parentMap), 1098 | false, 1099 | !this.skipMap); 1100 | this.depMaps[i] = depMap; 1101 | 1102 | handler = getOwn(handlers, depMap.id); 1103 | 1104 | if (handler) { 1105 | this.depExports[i] = handler(this); 1106 | return; 1107 | } 1108 | 1109 | this.depCount += 1; 1110 | 1111 | on(depMap, 'defined', bind(this, function (depExports) { 1112 | this.defineDep(i, depExports); 1113 | this.check(); 1114 | })); 1115 | 1116 | if (this.errback) { 1117 | on(depMap, 'error', bind(this, this.errback)); 1118 | } 1119 | } 1120 | 1121 | id = depMap.id; 1122 | mod = registry[id]; 1123 | 1124 | //Skip special modules like 'require', 'exports', 'module' 1125 | //Also, don't call enable if it is already enabled, 1126 | //important in circular dependency cases. 1127 | if (!hasProp(handlers, id) && mod && !mod.enabled) { 1128 | context.enable(depMap, this); 1129 | } 1130 | })); 1131 | 1132 | //Enable each plugin that is used in 1133 | //a dependency 1134 | eachProp(this.pluginMaps, bind(this, function (pluginMap) { 1135 | var mod = getOwn(registry, pluginMap.id); 1136 | if (mod && !mod.enabled) { 1137 | context.enable(pluginMap, this); 1138 | } 1139 | })); 1140 | 1141 | this.enabling = false; 1142 | 1143 | this.check(); 1144 | }, 1145 | 1146 | on: function (name, cb) { 1147 | var cbs = this.events[name]; 1148 | if (!cbs) { 1149 | cbs = this.events[name] = []; 1150 | } 1151 | cbs.push(cb); 1152 | }, 1153 | 1154 | emit: function (name, evt) { 1155 | each(this.events[name], function (cb) { 1156 | cb(evt); 1157 | }); 1158 | if (name === 'error') { 1159 | //Now that the error handler was triggered, remove 1160 | //the listeners, since this broken Module instance 1161 | //can stay around for a while in the registry. 1162 | delete this.events[name]; 1163 | } 1164 | } 1165 | }; 1166 | 1167 | function callGetModule(args) { 1168 | //Skip modules already defined. 1169 | if (!hasProp(defined, args[0])) { 1170 | getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); 1171 | } 1172 | } 1173 | 1174 | function removeListener(node, func, name, ieName) { 1175 | //Favor detachEvent because of IE9 1176 | //issue, see attachEvent/addEventListener comment elsewhere 1177 | //in this file. 1178 | if (node.detachEvent && !isOpera) { 1179 | //Probably IE. If not it will throw an error, which will be 1180 | //useful to know. 1181 | if (ieName) { 1182 | node.detachEvent(ieName, func); 1183 | } 1184 | } else { 1185 | node.removeEventListener(name, func, false); 1186 | } 1187 | } 1188 | 1189 | /** 1190 | * Given an event from a script node, get the requirejs info from it, 1191 | * and then removes the event listeners on the node. 1192 | * @param {Event} evt 1193 | * @returns {Object} 1194 | */ 1195 | function getScriptData(evt) { 1196 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1197 | //all old browsers will be supported, but this one was easy enough 1198 | //to support and still makes sense. 1199 | var node = evt.currentTarget || evt.srcElement; 1200 | 1201 | //Remove the listeners once here. 1202 | removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); 1203 | removeListener(node, context.onScriptError, 'error'); 1204 | 1205 | return { 1206 | node: node, 1207 | id: node && node.getAttribute('data-requiremodule') 1208 | }; 1209 | } 1210 | 1211 | function intakeDefines() { 1212 | var args; 1213 | 1214 | //Any defined modules in the global queue, intake them now. 1215 | takeGlobalQueue(); 1216 | 1217 | //Make sure any remaining defQueue items get properly processed. 1218 | while (defQueue.length) { 1219 | args = defQueue.shift(); 1220 | if (args[0] === null) { 1221 | return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); 1222 | } else { 1223 | //args are id, deps, factory. Should be normalized by the 1224 | //define() function. 1225 | callGetModule(args); 1226 | } 1227 | } 1228 | } 1229 | 1230 | context = { 1231 | config: config, 1232 | contextName: contextName, 1233 | registry: registry, 1234 | defined: defined, 1235 | urlFetched: urlFetched, 1236 | defQueue: defQueue, 1237 | Module: Module, 1238 | makeModuleMap: makeModuleMap, 1239 | nextTick: req.nextTick, 1240 | onError: onError, 1241 | 1242 | /** 1243 | * Set a configuration for the context. 1244 | * @param {Object} cfg config object to integrate. 1245 | */ 1246 | configure: function (cfg) { 1247 | //Make sure the baseUrl ends in a slash. 1248 | if (cfg.baseUrl) { 1249 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { 1250 | cfg.baseUrl += '/'; 1251 | } 1252 | } 1253 | 1254 | //Save off the paths since they require special processing, 1255 | //they are additive. 1256 | var shim = config.shim, 1257 | objs = { 1258 | paths: true, 1259 | bundles: true, 1260 | config: true, 1261 | map: true 1262 | }; 1263 | 1264 | eachProp(cfg, function (value, prop) { 1265 | if (objs[prop]) { 1266 | if (!config[prop]) { 1267 | config[prop] = {}; 1268 | } 1269 | mixin(config[prop], value, true, true); 1270 | } else { 1271 | config[prop] = value; 1272 | } 1273 | }); 1274 | 1275 | //Reverse map the bundles 1276 | if (cfg.bundles) { 1277 | eachProp(cfg.bundles, function (value, prop) { 1278 | each(value, function (v) { 1279 | if (v !== prop) { 1280 | bundlesMap[v] = prop; 1281 | } 1282 | }); 1283 | }); 1284 | } 1285 | 1286 | //Merge shim 1287 | if (cfg.shim) { 1288 | eachProp(cfg.shim, function (value, id) { 1289 | //Normalize the structure 1290 | if (isArray(value)) { 1291 | value = { 1292 | deps: value 1293 | }; 1294 | } 1295 | if ((value.exports || value.init) && !value.exportsFn) { 1296 | value.exportsFn = context.makeShimExports(value); 1297 | } 1298 | shim[id] = value; 1299 | }); 1300 | config.shim = shim; 1301 | } 1302 | 1303 | //Adjust packages if necessary. 1304 | if (cfg.packages) { 1305 | each(cfg.packages, function (pkgObj) { 1306 | var location, name; 1307 | 1308 | pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; 1309 | 1310 | name = pkgObj.name; 1311 | location = pkgObj.location; 1312 | if (location) { 1313 | config.paths[name] = pkgObj.location; 1314 | } 1315 | 1316 | //Save pointer to main module ID for pkg name. 1317 | //Remove leading dot in main, so main paths are normalized, 1318 | //and remove any trailing .js, since different package 1319 | //envs have different conventions: some use a module name, 1320 | //some use a file name. 1321 | config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') 1322 | .replace(currDirRegExp, '') 1323 | .replace(jsSuffixRegExp, ''); 1324 | }); 1325 | } 1326 | 1327 | //If there are any "waiting to execute" modules in the registry, 1328 | //update the maps for them, since their info, like URLs to load, 1329 | //may have changed. 1330 | eachProp(registry, function (mod, id) { 1331 | //If module already has init called, since it is too 1332 | //late to modify them, and ignore unnormalized ones 1333 | //since they are transient. 1334 | if (!mod.inited && !mod.map.unnormalized) { 1335 | mod.map = makeModuleMap(id); 1336 | } 1337 | }); 1338 | 1339 | //If a deps array or a config callback is specified, then call 1340 | //require with those args. This is useful when require is defined as a 1341 | //config object before require.js is loaded. 1342 | if (cfg.deps || cfg.callback) { 1343 | context.require(cfg.deps || [], cfg.callback); 1344 | } 1345 | }, 1346 | 1347 | makeShimExports: function (value) { 1348 | function fn() { 1349 | var ret; 1350 | if (value.init) { 1351 | ret = value.init.apply(global, arguments); 1352 | } 1353 | return ret || (value.exports && getGlobal(value.exports)); 1354 | } 1355 | return fn; 1356 | }, 1357 | 1358 | makeRequire: function (relMap, options) { 1359 | options = options || {}; 1360 | 1361 | function localRequire(deps, callback, errback) { 1362 | var id, map, requireMod; 1363 | 1364 | if (options.enableBuildCallback && callback && isFunction(callback)) { 1365 | callback.__requireJsBuild = true; 1366 | } 1367 | 1368 | if (typeof deps === 'string') { 1369 | if (isFunction(callback)) { 1370 | //Invalid call 1371 | return onError(makeError('requireargs', 'Invalid require call'), errback); 1372 | } 1373 | 1374 | //If require|exports|module are requested, get the 1375 | //value for them from the special handlers. Caveat: 1376 | //this only works while module is being defined. 1377 | if (relMap && hasProp(handlers, deps)) { 1378 | return handlers[deps](registry[relMap.id]); 1379 | } 1380 | 1381 | //Synchronous access to one module. If require.get is 1382 | //available (as in the Node adapter), prefer that. 1383 | if (req.get) { 1384 | return req.get(context, deps, relMap, localRequire); 1385 | } 1386 | 1387 | //Normalize module name, if it contains . or .. 1388 | map = makeModuleMap(deps, relMap, false, true); 1389 | id = map.id; 1390 | 1391 | if (!hasProp(defined, id)) { 1392 | return onError(makeError('notloaded', 'Module name "' + 1393 | id + 1394 | '" has not been loaded yet for context: ' + 1395 | contextName + 1396 | (relMap ? '' : '. Use require([])'))); 1397 | } 1398 | return defined[id]; 1399 | } 1400 | 1401 | //Grab defines waiting in the global queue. 1402 | intakeDefines(); 1403 | 1404 | //Mark all the dependencies as needing to be loaded. 1405 | context.nextTick(function () { 1406 | //Some defines could have been added since the 1407 | //require call, collect them. 1408 | intakeDefines(); 1409 | 1410 | requireMod = getModule(makeModuleMap(null, relMap)); 1411 | 1412 | //Store if map config should be applied to this require 1413 | //call for dependencies. 1414 | requireMod.skipMap = options.skipMap; 1415 | 1416 | requireMod.init(deps, callback, errback, { 1417 | enabled: true 1418 | }); 1419 | 1420 | checkLoaded(); 1421 | }); 1422 | 1423 | return localRequire; 1424 | } 1425 | 1426 | mixin(localRequire, { 1427 | isBrowser: isBrowser, 1428 | 1429 | /** 1430 | * Converts a module name + .extension into an URL path. 1431 | * *Requires* the use of a module name. It does not support using 1432 | * plain URLs like nameToUrl. 1433 | */ 1434 | toUrl: function (moduleNamePlusExt) { 1435 | var ext, 1436 | index = moduleNamePlusExt.lastIndexOf('.'), 1437 | segment = moduleNamePlusExt.split('/')[0], 1438 | isRelative = segment === '.' || segment === '..'; 1439 | 1440 | //Have a file extension alias, and it is not the 1441 | //dots from a relative path. 1442 | if (index !== -1 && (!isRelative || index > 1)) { 1443 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); 1444 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index); 1445 | } 1446 | 1447 | return context.nameToUrl(normalize(moduleNamePlusExt, 1448 | relMap && relMap.id, true), ext, true); 1449 | }, 1450 | 1451 | defined: function (id) { 1452 | return hasProp(defined, makeModuleMap(id, relMap, false, true).id); 1453 | }, 1454 | 1455 | specified: function (id) { 1456 | id = makeModuleMap(id, relMap, false, true).id; 1457 | return hasProp(defined, id) || hasProp(registry, id); 1458 | } 1459 | }); 1460 | 1461 | //Only allow undef on top level require calls 1462 | if (!relMap) { 1463 | localRequire.undef = function (id) { 1464 | //Bind any waiting define() calls to this context, 1465 | //fix for #408 1466 | takeGlobalQueue(); 1467 | 1468 | var map = makeModuleMap(id, relMap, true), 1469 | mod = getOwn(registry, id); 1470 | 1471 | removeScript(id); 1472 | 1473 | delete defined[id]; 1474 | delete urlFetched[map.url]; 1475 | delete undefEvents[id]; 1476 | 1477 | //Clean queued defines too. Go backwards 1478 | //in array so that the splices do not 1479 | //mess up the iteration. 1480 | eachReverse(defQueue, function(args, i) { 1481 | if(args[0] === id) { 1482 | defQueue.splice(i, 1); 1483 | } 1484 | }); 1485 | 1486 | if (mod) { 1487 | //Hold on to listeners in case the 1488 | //module will be attempted to be reloaded 1489 | //using a different config. 1490 | if (mod.events.defined) { 1491 | undefEvents[id] = mod.events; 1492 | } 1493 | 1494 | cleanRegistry(id); 1495 | } 1496 | }; 1497 | } 1498 | 1499 | return localRequire; 1500 | }, 1501 | 1502 | /** 1503 | * Called to enable a module if it is still in the registry 1504 | * awaiting enablement. A second arg, parent, the parent module, 1505 | * is passed in for context, when this method is overriden by 1506 | * the optimizer. Not shown here to keep code compact. 1507 | */ 1508 | enable: function (depMap) { 1509 | var mod = getOwn(registry, depMap.id); 1510 | if (mod) { 1511 | getModule(depMap).enable(); 1512 | } 1513 | }, 1514 | 1515 | /** 1516 | * Internal method used by environment adapters to complete a load event. 1517 | * A load event could be a script load or just a load pass from a synchronous 1518 | * load call. 1519 | * @param {String} moduleName the name of the module to potentially complete. 1520 | */ 1521 | completeLoad: function (moduleName) { 1522 | var found, args, mod, 1523 | shim = getOwn(config.shim, moduleName) || {}, 1524 | shExports = shim.exports; 1525 | 1526 | takeGlobalQueue(); 1527 | 1528 | while (defQueue.length) { 1529 | args = defQueue.shift(); 1530 | if (args[0] === null) { 1531 | args[0] = moduleName; 1532 | //If already found an anonymous module and bound it 1533 | //to this name, then this is some other anon module 1534 | //waiting for its completeLoad to fire. 1535 | if (found) { 1536 | break; 1537 | } 1538 | found = true; 1539 | } else if (args[0] === moduleName) { 1540 | //Found matching define call for this script! 1541 | found = true; 1542 | } 1543 | 1544 | callGetModule(args); 1545 | } 1546 | 1547 | //Do this after the cycle of callGetModule in case the result 1548 | //of those calls/init calls changes the registry. 1549 | mod = getOwn(registry, moduleName); 1550 | 1551 | if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { 1552 | if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { 1553 | if (hasPathFallback(moduleName)) { 1554 | return; 1555 | } else { 1556 | return onError(makeError('nodefine', 1557 | 'No define call for ' + moduleName, 1558 | null, 1559 | [moduleName])); 1560 | } 1561 | } else { 1562 | //A script that does not call define(), so just simulate 1563 | //the call for it. 1564 | callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); 1565 | } 1566 | } 1567 | 1568 | checkLoaded(); 1569 | }, 1570 | 1571 | /** 1572 | * Converts a module name to a file path. Supports cases where 1573 | * moduleName may actually be just an URL. 1574 | * Note that it **does not** call normalize on the moduleName, 1575 | * it is assumed to have already been normalized. This is an 1576 | * internal API, not a public one. Use toUrl for the public API. 1577 | */ 1578 | nameToUrl: function (moduleName, ext, skipExt) { 1579 | var paths, syms, i, parentModule, url, 1580 | parentPath, bundleId, 1581 | pkgMain = getOwn(config.pkgs, moduleName); 1582 | 1583 | if (pkgMain) { 1584 | moduleName = pkgMain; 1585 | } 1586 | 1587 | bundleId = getOwn(bundlesMap, moduleName); 1588 | 1589 | if (bundleId) { 1590 | return context.nameToUrl(bundleId, ext, skipExt); 1591 | } 1592 | 1593 | //If a colon is in the URL, it indicates a protocol is used and it is just 1594 | //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) 1595 | //or ends with .js, then assume the user meant to use an url and not a module id. 1596 | //The slash is important for protocol-less URLs as well as full paths. 1597 | if (req.jsExtRegExp.test(moduleName)) { 1598 | //Just a plain path, not module name lookup, so just return it. 1599 | //Add extension if it is included. This is a bit wonky, only non-.js things pass 1600 | //an extension, this method probably needs to be reworked. 1601 | url = moduleName + (ext || ''); 1602 | } else { 1603 | //A module that needs to be converted to a path. 1604 | paths = config.paths; 1605 | 1606 | syms = moduleName.split('/'); 1607 | //For each module name segment, see if there is a path 1608 | //registered for it. Start with most specific name 1609 | //and work up from it. 1610 | for (i = syms.length; i > 0; i -= 1) { 1611 | parentModule = syms.slice(0, i).join('/'); 1612 | 1613 | parentPath = getOwn(paths, parentModule); 1614 | if (parentPath) { 1615 | //If an array, it means there are a few choices, 1616 | //Choose the one that is desired 1617 | if (isArray(parentPath)) { 1618 | parentPath = parentPath[0]; 1619 | } 1620 | syms.splice(0, i, parentPath); 1621 | break; 1622 | } 1623 | } 1624 | 1625 | //Join the path parts together, then figure out if baseUrl is needed. 1626 | url = syms.join('/'); 1627 | url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); 1628 | url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; 1629 | } 1630 | 1631 | return config.urlArgs ? url + 1632 | ((url.indexOf('?') === -1 ? '?' : '&') + 1633 | config.urlArgs) : url; 1634 | }, 1635 | 1636 | //Delegates to req.load. Broken out as a separate function to 1637 | //allow overriding in the optimizer. 1638 | load: function (id, url) { 1639 | req.load(context, id, url); 1640 | }, 1641 | 1642 | /** 1643 | * Executes a module callback function. Broken out as a separate function 1644 | * solely to allow the build system to sequence the files in the built 1645 | * layer in the right sequence. 1646 | * 1647 | * @private 1648 | */ 1649 | execCb: function (name, callback, args, exports) { 1650 | return callback.apply(exports, args); 1651 | }, 1652 | 1653 | /** 1654 | * callback for script loads, used to check status of loading. 1655 | * 1656 | * @param {Event} evt the event from the browser for the script 1657 | * that was loaded. 1658 | */ 1659 | onScriptLoad: function (evt) { 1660 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1661 | //all old browsers will be supported, but this one was easy enough 1662 | //to support and still makes sense. 1663 | if (evt.type === 'load' || 1664 | (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { 1665 | //Reset interactive script so a script node is not held onto for 1666 | //to long. 1667 | interactiveScript = null; 1668 | 1669 | //Pull out the name of the module and the context. 1670 | var data = getScriptData(evt); 1671 | context.completeLoad(data.id); 1672 | } 1673 | }, 1674 | 1675 | /** 1676 | * Callback for script errors. 1677 | */ 1678 | onScriptError: function (evt) { 1679 | var data = getScriptData(evt); 1680 | if (!hasPathFallback(data.id)) { 1681 | return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); 1682 | } 1683 | } 1684 | }; 1685 | 1686 | context.require = context.makeRequire(); 1687 | return context; 1688 | } 1689 | 1690 | /** 1691 | * Main entry point. 1692 | * 1693 | * If the only argument to require is a string, then the module that 1694 | * is represented by that string is fetched for the appropriate context. 1695 | * 1696 | * If the first argument is an array, then it will be treated as an array 1697 | * of dependency string names to fetch. An optional function callback can 1698 | * be specified to execute when all of those dependencies are available. 1699 | * 1700 | * Make a local req variable to help Caja compliance (it assumes things 1701 | * on a require that are not standardized), and to give a short 1702 | * name for minification/local scope use. 1703 | */ 1704 | req = requirejs = function (deps, callback, errback, optional) { 1705 | 1706 | //Find the right context, use default 1707 | var context, config, 1708 | contextName = defContextName; 1709 | 1710 | // Determine if have config object in the call. 1711 | if (!isArray(deps) && typeof deps !== 'string') { 1712 | // deps is a config object 1713 | config = deps; 1714 | if (isArray(callback)) { 1715 | // Adjust args if there are dependencies 1716 | deps = callback; 1717 | callback = errback; 1718 | errback = optional; 1719 | } else { 1720 | deps = []; 1721 | } 1722 | } 1723 | 1724 | if (config && config.context) { 1725 | contextName = config.context; 1726 | } 1727 | 1728 | context = getOwn(contexts, contextName); 1729 | if (!context) { 1730 | context = contexts[contextName] = req.s.newContext(contextName); 1731 | } 1732 | 1733 | if (config) { 1734 | context.configure(config); 1735 | } 1736 | 1737 | return context.require(deps, callback, errback); 1738 | }; 1739 | 1740 | /** 1741 | * Support require.config() to make it easier to cooperate with other 1742 | * AMD loaders on globally agreed names. 1743 | */ 1744 | req.config = function (config) { 1745 | return req(config); 1746 | }; 1747 | 1748 | /** 1749 | * Execute something after the current tick 1750 | * of the event loop. Override for other envs 1751 | * that have a better solution than setTimeout. 1752 | * @param {Function} fn function to execute later. 1753 | */ 1754 | req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { 1755 | setTimeout(fn, 4); 1756 | } : function (fn) { fn(); }; 1757 | 1758 | /** 1759 | * Export require as a global, but only if it does not already exist. 1760 | */ 1761 | if (!require) { 1762 | require = req; 1763 | } 1764 | 1765 | req.version = version; 1766 | 1767 | //Used to filter out dependencies that are already paths. 1768 | req.jsExtRegExp = /^\/|:|\?|\.js$/; 1769 | req.isBrowser = isBrowser; 1770 | s = req.s = { 1771 | contexts: contexts, 1772 | newContext: newContext 1773 | }; 1774 | 1775 | //Create default context. 1776 | req({}); 1777 | 1778 | //Exports some context-sensitive methods on global require. 1779 | each([ 1780 | 'toUrl', 1781 | 'undef', 1782 | 'defined', 1783 | 'specified' 1784 | ], function (prop) { 1785 | //Reference from contexts instead of early binding to default context, 1786 | //so that during builds, the latest instance of the default context 1787 | //with its config gets used. 1788 | req[prop] = function () { 1789 | var ctx = contexts[defContextName]; 1790 | return ctx.require[prop].apply(ctx, arguments); 1791 | }; 1792 | }); 1793 | 1794 | if (isBrowser) { 1795 | head = s.head = document.getElementsByTagName('head')[0]; 1796 | //If BASE tag is in play, using appendChild is a problem for IE6. 1797 | //When that browser dies, this can be removed. Details in this jQuery bug: 1798 | //http://dev.jquery.com/ticket/2709 1799 | baseElement = document.getElementsByTagName('base')[0]; 1800 | if (baseElement) { 1801 | head = s.head = baseElement.parentNode; 1802 | } 1803 | } 1804 | 1805 | /** 1806 | * Any errors that require explicitly generates will be passed to this 1807 | * function. Intercept/override it if you want custom error handling. 1808 | * @param {Error} err the error object. 1809 | */ 1810 | req.onError = defaultOnError; 1811 | 1812 | /** 1813 | * Creates the node for the load command. Only used in browser envs. 1814 | */ 1815 | req.createNode = function (config, moduleName, url) { 1816 | var node = config.xhtml ? 1817 | document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : 1818 | document.createElement('script'); 1819 | node.type = config.scriptType || 'text/javascript'; 1820 | node.charset = 'utf-8'; 1821 | node.async = true; 1822 | return node; 1823 | }; 1824 | 1825 | /** 1826 | * Does the request to load a module for the browser case. 1827 | * Make this a separate function to allow other environments 1828 | * to override it. 1829 | * 1830 | * @param {Object} context the require context to find state. 1831 | * @param {String} moduleName the name of the module. 1832 | * @param {Object} url the URL to the module. 1833 | */ 1834 | req.load = function (context, moduleName, url) { 1835 | var config = (context && context.config) || {}, 1836 | node; 1837 | if (isBrowser) { 1838 | //In the browser so use a script tag 1839 | node = req.createNode(config, moduleName, url); 1840 | 1841 | node.setAttribute('data-requirecontext', context.contextName); 1842 | node.setAttribute('data-requiremodule', moduleName); 1843 | 1844 | //Set up load listener. Test attachEvent first because IE9 has 1845 | //a subtle issue in its addEventListener and script onload firings 1846 | //that do not match the behavior of all other browsers with 1847 | //addEventListener support, which fire the onload event for a 1848 | //script right after the script execution. See: 1849 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution 1850 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script 1851 | //script execution mode. 1852 | if (node.attachEvent && 1853 | //Check if node.attachEvent is artificially added by custom script or 1854 | //natively supported by browser 1855 | //read https://github.com/jrburke/requirejs/issues/187 1856 | //if we can NOT find [native code] then it must NOT natively supported. 1857 | //in IE8, node.attachEvent does not have toString() 1858 | //Note the test for "[native code" with no closing brace, see: 1859 | //https://github.com/jrburke/requirejs/issues/273 1860 | !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && 1861 | !isOpera) { 1862 | //Probably IE. IE (at least 6-8) do not fire 1863 | //script onload right after executing the script, so 1864 | //we cannot tie the anonymous define call to a name. 1865 | //However, IE reports the script as being in 'interactive' 1866 | //readyState at the time of the define call. 1867 | useInteractive = true; 1868 | 1869 | node.attachEvent('onreadystatechange', context.onScriptLoad); 1870 | //It would be great to add an error handler here to catch 1871 | //404s in IE9+. However, onreadystatechange will fire before 1872 | //the error handler, so that does not help. If addEventListener 1873 | //is used, then IE will fire error before load, but we cannot 1874 | //use that pathway given the connect.microsoft.com issue 1875 | //mentioned above about not doing the 'script execute, 1876 | //then fire the script load event listener before execute 1877 | //next script' that other browsers do. 1878 | //Best hope: IE10 fixes the issues, 1879 | //and then destroys all installs of IE 6-9. 1880 | //node.attachEvent('onerror', context.onScriptError); 1881 | } else { 1882 | node.addEventListener('load', context.onScriptLoad, false); 1883 | node.addEventListener('error', context.onScriptError, false); 1884 | } 1885 | node.src = url; 1886 | 1887 | //For some cache cases in IE 6-8, the script executes before the end 1888 | //of the appendChild execution, so to tie an anonymous define 1889 | //call to the module name (which is stored on the node), hold on 1890 | //to a reference to this node, but clear after the DOM insertion. 1891 | currentlyAddingScript = node; 1892 | if (baseElement) { 1893 | head.insertBefore(node, baseElement); 1894 | } else { 1895 | head.appendChild(node); 1896 | } 1897 | currentlyAddingScript = null; 1898 | 1899 | return node; 1900 | } else if (isWebWorker) { 1901 | try { 1902 | //In a web worker, use importScripts. This is not a very 1903 | //efficient use of importScripts, importScripts will block until 1904 | //its script is downloaded and evaluated. However, if web workers 1905 | //are in play, the expectation that a build has been done so that 1906 | //only one script needs to be loaded anyway. This may need to be 1907 | //reevaluated if other use cases become common. 1908 | importScripts(url); 1909 | 1910 | //Account for anonymous modules 1911 | context.completeLoad(moduleName); 1912 | } catch (e) { 1913 | context.onError(makeError('importscripts', 1914 | 'importScripts failed for ' + 1915 | moduleName + ' at ' + url, 1916 | e, 1917 | [moduleName])); 1918 | } 1919 | } 1920 | }; 1921 | 1922 | function getInteractiveScript() { 1923 | if (interactiveScript && interactiveScript.readyState === 'interactive') { 1924 | return interactiveScript; 1925 | } 1926 | 1927 | eachReverse(scripts(), function (script) { 1928 | if (script.readyState === 'interactive') { 1929 | return (interactiveScript = script); 1930 | } 1931 | }); 1932 | return interactiveScript; 1933 | } 1934 | 1935 | //Look for a data-main script attribute, which could also adjust the baseUrl. 1936 | if (isBrowser && !cfg.skipDataMain) { 1937 | //Figure out baseUrl. Get it from the script tag with require.js in it. 1938 | eachReverse(scripts(), function (script) { 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 | dataMain = script.getAttribute('data-main'); 1949 | if (dataMain) { 1950 | //Preserve dataMain in case it is a path (i.e. contains '?') 1951 | mainScript = dataMain; 1952 | 1953 | //Set final baseUrl if there is not already an explicit one. 1954 | if (!cfg.baseUrl) { 1955 | //Pull off the directory of data-main for use as the 1956 | //baseUrl. 1957 | src = mainScript.split('/'); 1958 | mainScript = src.pop(); 1959 | subPath = src.length ? src.join('/') + '/' : './'; 1960 | 1961 | cfg.baseUrl = subPath; 1962 | } 1963 | 1964 | //Strip off any trailing .js since mainScript is now 1965 | //like a module name. 1966 | mainScript = mainScript.replace(jsSuffixRegExp, ''); 1967 | 1968 | //If mainScript is still a path, fall back to dataMain 1969 | if (req.jsExtRegExp.test(mainScript)) { 1970 | mainScript = dataMain; 1971 | } 1972 | 1973 | //Put the data-main script in the files to load. 1974 | cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; 1975 | 1976 | return true; 1977 | } 1978 | }); 1979 | } 1980 | 1981 | /** 1982 | * The function that handles definitions of modules. Differs from 1983 | * require() in that a string for the module should be the first argument, 1984 | * and the function to execute after dependencies are loaded should 1985 | * return a value to define the module corresponding to the first argument's 1986 | * name. 1987 | */ 1988 | define = function (name, deps, callback) { 1989 | var node, context; 1990 | 1991 | //Allow for anonymous modules 1992 | if (typeof name !== 'string') { 1993 | //Adjust args appropriately 1994 | callback = deps; 1995 | deps = name; 1996 | name = null; 1997 | } 1998 | 1999 | //This module may not have dependencies 2000 | if (!isArray(deps)) { 2001 | callback = deps; 2002 | deps = null; 2003 | } 2004 | 2005 | //If no name, and callback is a function, then figure out if it a 2006 | //CommonJS thing with dependencies. 2007 | if (!deps && isFunction(callback)) { 2008 | deps = []; 2009 | //Remove comments from the callback string, 2010 | //look for require calls, and pull them into the dependencies, 2011 | //but only if there are function args. 2012 | if (callback.length) { 2013 | callback 2014 | .toString() 2015 | .replace(commentRegExp, '') 2016 | .replace(cjsRequireRegExp, function (match, dep) { 2017 | deps.push(dep); 2018 | }); 2019 | 2020 | //May be a CommonJS thing even without require calls, but still 2021 | //could use exports, and module. Avoid doing exports and module 2022 | //work though if it just needs require. 2023 | //REQUIRES the function to expect the CommonJS variables in the 2024 | //order listed below. 2025 | deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); 2026 | } 2027 | } 2028 | 2029 | //If in IE 6-8 and hit an anonymous define() call, do the interactive 2030 | //work. 2031 | if (useInteractive) { 2032 | node = currentlyAddingScript || getInteractiveScript(); 2033 | if (node) { 2034 | if (!name) { 2035 | name = node.getAttribute('data-requiremodule'); 2036 | } 2037 | context = contexts[node.getAttribute('data-requirecontext')]; 2038 | } 2039 | } 2040 | 2041 | //Always save off evaluating the def call until the script onload handler. 2042 | //This allows multiple modules to be in a file without prematurely 2043 | //tracing dependencies, and allows for anonymous module support, 2044 | //where the module name is not known until the script onload event 2045 | //occurs. If no context, use the global queue, and get it processed 2046 | //in the onscript load callback. 2047 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); 2048 | }; 2049 | 2050 | define.amd = { 2051 | jQuery: true 2052 | }; 2053 | 2054 | 2055 | /** 2056 | * Executes the text. Normally just uses eval, but can be modified 2057 | * to use a better, environment-specific call. Only used for transpiling 2058 | * loader plugins, not for plain JS modules. 2059 | * @param {String} text the text to execute/evaluate. 2060 | */ 2061 | req.exec = function (text) { 2062 | /*jslint evil: true */ 2063 | return eval(text); 2064 | }; 2065 | 2066 | //Set up with config info. 2067 | req(cfg); 2068 | }(this)); 2069 | --------------------------------------------------------------------------------