UNPKG

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