blob: 2a0658829a00df8c2f97b4d37ae653ab3e3207a0 [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/**
2 * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
3 * Available via the MIT or new BSD license.
4 * see: http://github.com/jrburke/almond for details
5 */
6//Going sloppy to avoid 'use strict' string cost, but strict practices should
7//be followed.
8/*jslint sloppy: true */
9/*global setTimeout: false */
10
11var requirejs, require, define;
12(function (undef) {
13 var main, req, makeMap, handlers,
14 defined = {},
15 waiting = {},
16 config = {},
17 defining = {},
18 hasOwn = Object.prototype.hasOwnProperty,
19 aps = [].slice,
20 jsSuffixRegExp = /\.js$/;
21
22 function hasProp(obj, prop) {
23 return hasOwn.call(obj, prop);
24 }
25
26 /**
27 * Given a relative module name, like ./something, normalize it to
28 * a real name that can be mapped to a path.
29 * @param {String} name the relative name
30 * @param {String} baseName a real name that the name arg is relative
31 * to.
32 * @returns {String} normalized name
33 */
34 function normalize(name, baseName) {
35 var nameParts, nameSegment, mapValue, foundMap, lastIndex,
36 foundI, foundStarMap, starI, i, j, part,
37 baseParts = baseName && baseName.split("/"),
38 map = config.map,
39 starMap = (map && map['*']) || {};
40
41 //Adjust any relative paths.
42 if (name && name.charAt(0) === ".") {
43 //If have a base name, try to normalize against it,
44 //otherwise, assume it is a top-level require that will
45 //be relative to baseUrl in the end.
46 if (baseName) {
47 name = name.split('/');
48 lastIndex = name.length - 1;
49
50 // Node .js allowance:
51 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
52 name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
53 }
54
55 //Lop off the last part of baseParts, so that . matches the
56 //"directory" and not name of the baseName's module. For instance,
57 //baseName of "one/two/three", maps to "one/two/three.js", but we
58 //want the directory, "one/two" for this normalization.
59 name = baseParts.slice(0, baseParts.length - 1).concat(name);
60
61 //start trimDots
62 for (i = 0; i < name.length; i += 1) {
63 part = name[i];
64 if (part === ".") {
65 name.splice(i, 1);
66 i -= 1;
67 } else if (part === "..") {
68 if (i === 1 && (name[2] === '..' || name[0] === '..')) {
69 //End of the line. Keep at least one non-dot
70 //path segment at the front so it can be mapped
71 //correctly to disk. Otherwise, there is likely
72 //no path mapping for a path starting with '..'.
73 //This can still fail, but catches the most reasonable
74 //uses of ..
75 break;
76 } else if (i > 0) {
77 name.splice(i - 1, 2);
78 i -= 2;
79 }
80 }
81 }
82 //end trimDots
83
84 name = name.join("/");
85 } else if (name.indexOf('./') === 0) {
86 // No baseName, so this is ID is resolved relative
87 // to baseUrl, pull off the leading dot.
88 name = name.substring(2);
89 }
90 }
91
92 //Apply map config if available.
93 if ((baseParts || starMap) && map) {
94 nameParts = name.split('/');
95
96 for (i = nameParts.length; i > 0; i -= 1) {
97 nameSegment = nameParts.slice(0, i).join("/");
98
99 if (baseParts) {
100 //Find the longest baseName segment match in the config.
101 //So, do joins on the biggest to smallest lengths of baseParts.
102 for (j = baseParts.length; j > 0; j -= 1) {
103 mapValue = map[baseParts.slice(0, j).join('/')];
104
105 //baseName segment has config, find if it has one for
106 //this name.
107 if (mapValue) {
108 mapValue = mapValue[nameSegment];
109 if (mapValue) {
110 //Match, update name to the new value.
111 foundMap = mapValue;
112 foundI = i;
113 break;
114 }
115 }
116 }
117 }
118
119 if (foundMap) {
120 break;
121 }
122
123 //Check for a star map match, but just hold on to it,
124 //if there is a shorter segment match later in a matching
125 //config, then favor over this star map.
126 if (!foundStarMap && starMap && starMap[nameSegment]) {
127 foundStarMap = starMap[nameSegment];
128 starI = i;
129 }
130 }
131
132 if (!foundMap && foundStarMap) {
133 foundMap = foundStarMap;
134 foundI = starI;
135 }
136
137 if (foundMap) {
138 nameParts.splice(0, foundI, foundMap);
139 name = nameParts.join('/');
140 }
141 }
142
143 return name;
144 }
145
146 function makeRequire(relName, forceSync) {
147 return function () {
148 //A version of a require function that passes a moduleName
149 //value for items that may need to
150 //look up paths relative to the moduleName
151 var args = aps.call(arguments, 0);
152
153 //If first arg is not require('string'), and there is only
154 //one arg, it is the array form without a callback. Insert
155 //a null so that the following concat is correct.
156 if (typeof args[0] !== 'string' && args.length === 1) {
157 args.push(null);
158 }
159 return req.apply(undef, args.concat([relName, forceSync]));
160 };
161 }
162
163 function makeNormalize(relName) {
164 return function (name) {
165 return normalize(name, relName);
166 };
167 }
168
169 function makeLoad(depName) {
170 return function (value) {
171 defined[depName] = value;
172 };
173 }
174
175 function callDep(name) {
176 if (hasProp(waiting, name)) {
177 var args = waiting[name];
178 delete waiting[name];
179 defining[name] = true;
180 main.apply(undef, args);
181 }
182
183 if (!hasProp(defined, name) && !hasProp(defining, name)) {
184 throw new Error('No ' + name);
185 }
186 return defined[name];
187 }
188
189 //Turns a plugin!resource to [plugin, resource]
190 //with the plugin being undefined if the name
191 //did not have a plugin prefix.
192 function splitPrefix(name) {
193 var prefix,
194 index = name ? name.indexOf('!') : -1;
195 if (index > -1) {
196 prefix = name.substring(0, index);
197 name = name.substring(index + 1, name.length);
198 }
199 return [prefix, name];
200 }
201
202 /**
203 * Makes a name map, normalizing the name, and using a plugin
204 * for normalization if necessary. Grabs a ref to plugin
205 * too, as an optimization.
206 */
207 makeMap = function (name, relName) {
208 var plugin,
209 parts = splitPrefix(name),
210 prefix = parts[0];
211
212 name = parts[1];
213
214 if (prefix) {
215 prefix = normalize(prefix, relName);
216 plugin = callDep(prefix);
217 }
218
219 //Normalize according
220 if (prefix) {
221 if (plugin && plugin.normalize) {
222 name = plugin.normalize(name, makeNormalize(relName));
223 } else {
224 name = normalize(name, relName);
225 }
226 } else {
227 name = normalize(name, relName);
228 parts = splitPrefix(name);
229 prefix = parts[0];
230 name = parts[1];
231 if (prefix) {
232 plugin = callDep(prefix);
233 }
234 }
235
236 //Using ridiculous property names for space reasons
237 return {
238 f: prefix ? prefix + '!' + name : name, //fullName
239 n: name,
240 pr: prefix,
241 p: plugin
242 };
243 };
244
245 function makeConfig(name) {
246 return function () {
247 return (config && config.config && config.config[name]) || {};
248 };
249 }
250
251 handlers = {
252 require: function (name) {
253 return makeRequire(name);
254 },
255 exports: function (name) {
256 var e = defined[name];
257 if (typeof e !== 'undefined') {
258 return e;
259 } else {
260 return (defined[name] = {});
261 }
262 },
263 module: function (name) {
264 return {
265 id: name,
266 uri: '',
267 exports: defined[name],
268 config: makeConfig(name)
269 };
270 }
271 };
272
273 main = function (name, deps, callback, relName) {
274 var cjsModule, depName, ret, map, i,
275 args = [],
276 callbackType = typeof callback,
277 usingExports;
278
279 //Use name if no relName
280 relName = relName || name;
281
282 //Call the callback to define the module, if necessary.
283 if (callbackType === 'undefined' || callbackType === 'function') {
284 //Pull out the defined dependencies and pass the ordered
285 //values to the callback.
286 //Default to [require, exports, module] if no deps
287 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
288 for (i = 0; i < deps.length; i += 1) {
289 map = makeMap(deps[i], relName);
290 depName = map.f;
291
292 //Fast path CommonJS standard dependencies.
293 if (depName === "require") {
294 args[i] = handlers.require(name);
295 } else if (depName === "exports") {
296 //CommonJS module spec 1.1
297 args[i] = handlers.exports(name);
298 usingExports = true;
299 } else if (depName === "module") {
300 //CommonJS module spec 1.1
301 cjsModule = args[i] = handlers.module(name);
302 } else if (hasProp(defined, depName) ||
303 hasProp(waiting, depName) ||
304 hasProp(defining, depName)) {
305 args[i] = callDep(depName);
306 } else if (map.p) {
307 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
308 args[i] = defined[depName];
309 } else {
310 throw new Error(name + ' missing ' + depName);
311 }
312 }
313
314 ret = callback ? callback.apply(defined[name], args) : undefined;
315
316 if (name) {
317 //If setting exports via "module" is in play,
318 //favor that over return value and exports. After that,
319 //favor a non-undefined return value over exports use.
320 if (cjsModule && cjsModule.exports !== undef &&
321 cjsModule.exports !== defined[name]) {
322 defined[name] = cjsModule.exports;
323 } else if (ret !== undef || !usingExports) {
324 //Use the return value from the function.
325 defined[name] = ret;
326 }
327 }
328 } else if (name) {
329 //May just be an object definition for the module. Only
330 //worry about defining if have a module name.
331 defined[name] = callback;
332 }
333 };
334
335 requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
336 if (typeof deps === "string") {
337 if (handlers[deps]) {
338 //callback in this case is really relName
339 return handlers[deps](callback);
340 }
341 //Just return the module wanted. In this scenario, the
342 //deps arg is the module name, and second arg (if passed)
343 //is just the relName.
344 //Normalize module name, if it contains . or ..
345 return callDep(makeMap(deps, callback).f);
346 } else if (!deps.splice) {
347 //deps is a config object, not an array.
348 config = deps;
349 if (config.deps) {
350 req(config.deps, config.callback);
351 }
352 if (!callback) {
353 return;
354 }
355
356 if (callback.splice) {
357 //callback is an array, which means it is a dependency list.
358 //Adjust args if there are dependencies
359 deps = callback;
360 callback = relName;
361 relName = null;
362 } else {
363 deps = undef;
364 }
365 }
366
367 //Support require(['a'])
368 callback = callback || function () {};
369
370 //If relName is a function, it is an errback handler,
371 //so remove it.
372 if (typeof relName === 'function') {
373 relName = forceSync;
374 forceSync = alt;
375 }
376
377 //Simulate async callback;
378 if (forceSync) {
379 main(undef, deps, callback, relName);
380 } else {
381 //Using a non-zero value because of concern for what old browsers
382 //do, and latest browsers "upgrade" to 4 if lower value is used:
383 //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
384 //If want a value immediately, use require('id') instead -- something
385 //that works in almond on the global level, but not guaranteed and
386 //unlikely to work in other AMD implementations.
387 setTimeout(function () {
388 main(undef, deps, callback, relName);
389 }, 4);
390 }
391
392 return req;
393 };
394
395 /**
396 * Just drops the config on the floor, but returns req in case
397 * the config return value is used.
398 */
399 req.config = function (cfg) {
400 return req(cfg);
401 };
402
403 /**
404 * Expose module registry for debugging and tooling
405 */
406 requirejs._defined = defined;
407
408 define = function (name, deps, callback) {
409 if (typeof name !== 'string') {
410 throw new Error('See almond README: incorrect module build, no module name');
411 }
412
413 //This module may not have dependencies
414 if (!deps.splice) {
415 //deps is not an array, so probably means
416 //an object literal or factory function for
417 //the value. Adjust args.
418 callback = deps;
419 deps = [];
420 }
421
422 if (!hasProp(defined, name) && !hasProp(waiting, name)) {
423 waiting[name] = [name, deps, callback];
424 }
425 };
426
427 define.amd = {
428 jQuery: true
429 };
430}());