UNPKG

11 kBJavaScriptView Raw
1/**
2 * almond 0.1.1 Copyright (c) 2011, 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 defined = {},
14 waiting = {},
15 config = {},
16 defining = {},
17 aps = [].slice,
18 main, req;
19
20 /**
21 * Given a relative module name, like ./something, normalize it to
22 * a real name that can be mapped to a path.
23 * @param {String} name the relative name
24 * @param {String} baseName a real name that the name arg is relative
25 * to.
26 * @returns {String} normalized name
27 */
28 function normalize(name, baseName) {
29 var baseParts = baseName && baseName.split("/"),
30 map = config.map,
31 starMap = (map && map['*']) || {},
32 nameParts, nameSegment, mapValue, foundMap, i, j, part;
33
34 //Adjust any relative paths.
35 if (name && name.charAt(0) === ".") {
36 //If have a base name, try to normalize against it,
37 //otherwise, assume it is a top-level require that will
38 //be relative to baseUrl in the end.
39 if (baseName) {
40 //Convert baseName to array, and lop off the last part,
41 //so that . matches that "directory" and not name of the baseName's
42 //module. For instance, baseName of "one/two/three", maps to
43 //"one/two/three.js", but we want the directory, "one/two" for
44 //this normalization.
45 baseParts = baseParts.slice(0, baseParts.length - 1);
46
47 name = baseParts.concat(name.split("/"));
48
49 //start trimDots
50 for (i = 0; (part = name[i]); i++) {
51 if (part === ".") {
52 name.splice(i, 1);
53 i -= 1;
54 } else if (part === "..") {
55 if (i === 1 && (name[2] === '..' || name[0] === '..')) {
56 //End of the line. Keep at least one non-dot
57 //path segment at the front so it can be mapped
58 //correctly to disk. Otherwise, there is likely
59 //no path mapping for a path starting with '..'.
60 //This can still fail, but catches the most reasonable
61 //uses of ..
62 return true;
63 } else if (i > 0) {
64 name.splice(i - 1, 2);
65 i -= 2;
66 }
67 }
68 }
69 //end trimDots
70
71 name = name.join("/");
72 }
73 }
74
75 //Apply map config if available.
76 if ((baseParts || starMap) && map) {
77 nameParts = name.split('/');
78
79 for (i = nameParts.length; i > 0; i -= 1) {
80 nameSegment = nameParts.slice(0, i).join("/");
81
82 if (baseParts) {
83 //Find the longest baseName segment match in the config.
84 //So, do joins on the biggest to smallest lengths of baseParts.
85 for (j = baseParts.length; j > 0; j -= 1) {
86 mapValue = map[baseParts.slice(0, j).join('/')];
87
88 //baseName segment has config, find if it has one for
89 //this name.
90 if (mapValue) {
91 mapValue = mapValue[nameSegment];
92 if (mapValue) {
93 //Match, update name to the new value.
94 foundMap = mapValue;
95 break;
96 }
97 }
98 }
99 }
100
101 foundMap = foundMap || starMap[nameSegment];
102
103 if (foundMap) {
104 nameParts.splice(0, i, foundMap);
105 name = nameParts.join('/');
106 break;
107 }
108 }
109 }
110
111 return name;
112 }
113
114 function makeRequire(relName, forceSync) {
115 return function () {
116 //A version of a require function that passes a moduleName
117 //value for items that may need to
118 //look up paths relative to the moduleName
119 return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
120 };
121 }
122
123 function makeNormalize(relName) {
124 return function (name) {
125 return normalize(name, relName);
126 };
127 }
128
129 function makeLoad(depName) {
130 return function (value) {
131 defined[depName] = value;
132 };
133 }
134
135 function callDep(name) {
136 if (waiting.hasOwnProperty(name)) {
137 var args = waiting[name];
138 delete waiting[name];
139 defining[name] = true;
140 main.apply(undef, args);
141 }
142
143 if (!defined.hasOwnProperty(name)) {
144 throw new Error('No ' + name);
145 }
146 return defined[name];
147 }
148
149 /**
150 * Makes a name map, normalizing the name, and using a plugin
151 * for normalization if necessary. Grabs a ref to plugin
152 * too, as an optimization.
153 */
154 function makeMap(name, relName) {
155 var prefix, plugin,
156 index = name.indexOf('!');
157
158 if (index !== -1) {
159 prefix = normalize(name.slice(0, index), relName);
160 name = name.slice(index + 1);
161 plugin = callDep(prefix);
162
163 //Normalize according
164 if (plugin && plugin.normalize) {
165 name = plugin.normalize(name, makeNormalize(relName));
166 } else {
167 name = normalize(name, relName);
168 }
169 } else {
170 name = normalize(name, relName);
171 }
172
173 //Using ridiculous property names for space reasons
174 return {
175 f: prefix ? prefix + '!' + name : name, //fullName
176 n: name,
177 p: plugin
178 };
179 }
180
181 function makeConfig(name) {
182 return function () {
183 return (config && config.config && config.config[name]) || {};
184 };
185 }
186
187 main = function (name, deps, callback, relName) {
188 var args = [],
189 usingExports,
190 cjsModule, depName, ret, map, i;
191
192 //Use name if no relName
193 relName = relName || name;
194
195 //Call the callback to define the module, if necessary.
196 if (typeof callback === 'function') {
197
198 //Pull out the defined dependencies and pass the ordered
199 //values to the callback.
200 //Default to [require, exports, module] if no deps
201 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
202 for (i = 0; i < deps.length; i++) {
203 map = makeMap(deps[i], relName);
204 depName = map.f;
205
206 //Fast path CommonJS standard dependencies.
207 if (depName === "require") {
208 args[i] = makeRequire(name);
209 } else if (depName === "exports") {
210 //CommonJS module spec 1.1
211 args[i] = defined[name] = {};
212 usingExports = true;
213 } else if (depName === "module") {
214 //CommonJS module spec 1.1
215 cjsModule = args[i] = {
216 id: name,
217 uri: '',
218 exports: defined[name],
219 config: makeConfig(name)
220 };
221 } else if (defined.hasOwnProperty(depName) || waiting.hasOwnProperty(depName)) {
222 args[i] = callDep(depName);
223 } else if (map.p) {
224 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
225 args[i] = defined[depName];
226 } else if (!defining[depName]) {
227 throw new Error(name + ' missing ' + depName);
228 }
229 }
230
231 ret = callback.apply(defined[name], args);
232
233 if (name) {
234 //If setting exports via "module" is in play,
235 //favor that over return value and exports. After that,
236 //favor a non-undefined return value over exports use.
237 if (cjsModule && cjsModule.exports !== undef &&
238 cjsModule.exports !== defined[name]) {
239 defined[name] = cjsModule.exports;
240 } else if (ret !== undef || !usingExports) {
241 //Use the return value from the function.
242 defined[name] = ret;
243 }
244 }
245 } else if (name) {
246 //May just be an object definition for the module. Only
247 //worry about defining if have a module name.
248 defined[name] = callback;
249 }
250 };
251
252 requirejs = require = req = function (deps, callback, relName, forceSync) {
253 if (typeof deps === "string") {
254 //Just return the module wanted. In this scenario, the
255 //deps arg is the module name, and second arg (if passed)
256 //is just the relName.
257 //Normalize module name, if it contains . or ..
258 return callDep(makeMap(deps, callback).f);
259 } else if (!deps.splice) {
260 //deps is a config object, not an array.
261 config = deps;
262 if (callback.splice) {
263 //callback is an array, which means it is a dependency list.
264 //Adjust args if there are dependencies
265 deps = callback;
266 callback = relName;
267 relName = null;
268 } else {
269 deps = undef;
270 }
271 }
272
273 //Support require(['a'])
274 callback = callback || function () {};
275
276 //Simulate async callback;
277 if (forceSync) {
278 main(undef, deps, callback, relName);
279 } else {
280 setTimeout(function () {
281 main(undef, deps, callback, relName);
282 }, 15);
283 }
284
285 return req;
286 };
287
288 /**
289 * Just drops the config on the floor, but returns req in case
290 * the config return value is used.
291 */
292 req.config = function (cfg) {
293 config = cfg;
294 return req;
295 };
296
297 define = function (name, deps, callback) {
298
299 //This module may not have dependencies
300 if (!deps.splice) {
301 //deps is not an array, so probably means
302 //an object literal or factory function for
303 //the value. Adjust args.
304 callback = deps;
305 deps = [];
306 }
307
308 waiting[name] = [name, deps, callback];
309 };
310
311 define.amd = {
312 jQuery: true
313 };
314}());