spade 0.1.0 → 0.1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/.gitignore +0 -11
  2. data/.gitmodules +3 -6
  3. data/Gemfile +10 -0
  4. data/bin/spade +3 -1
  5. data/lib/spade.rb +70 -0
  6. data/lib/spade/bundle.rb +180 -0
  7. data/lib/spade/cli.rb +1 -17
  8. data/lib/spade/cli/base.rb +182 -0
  9. data/lib/spade/console.rb +39 -0
  10. data/lib/spade/context.rb +107 -0
  11. data/lib/spade/evaluator.rb +34 -0
  12. data/lib/spade/exports.rb +70 -0
  13. data/lib/spade/loader.rb +208 -0
  14. data/lib/spade/package/.gitignore +1 -0
  15. data/lib/spade/package/Gemfile +15 -0
  16. data/lib/spade/package/lib/spade.js +1283 -0
  17. data/lib/spade/package/lib/wrapper.js +15 -0
  18. data/lib/spade/package/package.json +17 -0
  19. data/lib/spade/package/spec/javascript/async-test.js +123 -0
  20. data/lib/spade/package/spec/javascript/compiler/javascript.js +13 -0
  21. data/lib/spade/package/spec/javascript/compiler/ruby.js +14 -0
  22. data/lib/spade/package/spec/javascript/loader-test.js +64 -0
  23. data/lib/spade/package/spec/javascript/normalize-test.js +73 -0
  24. data/lib/spade/package/spec/javascript/packages-test.js +50 -0
  25. data/lib/spade/package/spec/javascript/relative-require-test.js +72 -0
  26. data/lib/spade/package/spec/javascript/require-test.js +117 -0
  27. data/lib/spade/package/spec/javascript/sandbox/creation.js +44 -0
  28. data/lib/spade/package/spec/javascript/sandbox/evaluate.js +37 -0
  29. data/lib/spade/package/spec/javascript/sandbox/format.js +79 -0
  30. data/lib/spade/package/spec/javascript/sandbox/misc.js +58 -0
  31. data/lib/spade/package/spec/javascript/sandbox/preprocessor.js +81 -0
  32. data/lib/spade/package/spec/javascript/sandbox/require.js +48 -0
  33. data/lib/spade/package/spec/javascript/sandbox/run-command.js +21 -0
  34. data/lib/spade/package/spec/javascript/spade/externs.js +14 -0
  35. data/lib/spade/package/spec/javascript/spade/load-factory.js +15 -0
  36. data/lib/spade/package/spec/javascript/spade/misc.js +23 -0
  37. data/lib/spade/package/spec/javascript/spade/ready.js +12 -0
  38. data/lib/spade/package/spec/javascript/spade/register.js +13 -0
  39. data/lib/spade/package/spec/javascript_spec.rb +7 -0
  40. data/lib/spade/package/spec/spec_helper.rb +3 -0
  41. data/lib/spade/package/spec/support/core_test.rb +67 -0
  42. data/lib/spade/reactor.rb +159 -0
  43. data/lib/spade/server.rb +66 -0
  44. data/lib/spade/shell.rb +85 -0
  45. data/lib/spade/version.rb +1 -1
  46. data/spade.gemspec +15 -4
  47. data/spec/cli/update_spec.rb +65 -0
  48. data/spec/spec_helper.rb +22 -0
  49. data/spec/support/cli.rb +103 -0
  50. data/spec/support/matchers.rb +12 -0
  51. data/spec/support/path.rb +66 -0
  52. metadata +146 -78
  53. data/.rspec +0 -1
  54. data/Buildfile +0 -18
  55. data/README.md +0 -152
  56. data/Rakefile +0 -9
  57. data/examples/format-app/lib/hello.coffee +0 -1
  58. data/examples/format-app/lib/main.js +0 -6
  59. data/examples/format-app/package.json +0 -10
  60. data/examples/format-app/resources/README.txt +0 -1
  61. data/examples/format-app/resources/config.json +0 -3
  62. data/examples/path-test/lib/hello.js +0 -1
  63. data/examples/path-test/lib/main.js +0 -1
  64. data/examples/path-test/package.json +0 -5
  65. data/examples/sc-app/index.html +0 -13
  66. data/examples/sc-app/lib/main.js +0 -24
  67. data/examples/sc-app/package.json +0 -8
  68. data/examples/single-file.js +0 -22
  69. data/examples/todos/index.html +0 -11
  70. data/examples/todos/lib/main.js +0 -11
  71. data/examples/todos/lib/todos.js +0 -93
  72. data/examples/todos/package.json +0 -10
  73. data/examples/todos/resources/stylesheets/todos.css +0 -162
  74. data/examples/todos/resources/templates/todos.handlebars +0 -31
  75. data/examples/web-app/README.md +0 -83
  76. data/examples/web-app/index.html +0 -12
  77. data/examples/web-app/lib/main.js +0 -3
  78. data/examples/web-app/package.json +0 -6
  79. data/examples/web-app/tests.html +0 -12
  80. data/examples/web-app/tests/ct-example-test.js +0 -39
  81. data/examples/web-app/tests/qunit-test.js +0 -23
@@ -0,0 +1 @@
1
+ Gemfile.lock
@@ -0,0 +1,15 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem "rspec"
4
+
5
+ if ENV["BPM_PATH"]
6
+ gem 'bpm', :path => ENV["BPM_PATH"]
7
+ else
8
+ gem 'bpm', :git => "git://github.com/sproutcore/bpm"
9
+ end
10
+
11
+ if ENV["SPADE_PATH"]
12
+ gem 'spade', :path => ENV["SPADE_PATH"]
13
+ else
14
+ gem 'spade', :git => "git://github.com/sproutcore/spade-ruby"
15
+ end
@@ -0,0 +1,1283 @@
1
+ // ==========================================================================
2
+ // Project: Spade - CommonJS Runtime
3
+ // Copyright: ©2010 Strobe Inc. All rights reserved.
4
+ // License: Licened under MIT license (see __preamble__.js)
5
+ // ==========================================================================
6
+ /*jslint evil:true */
7
+ /*globals ARGS ARGV ENV __module */
8
+
9
+ /*! @license
10
+ ==========================================================================
11
+ Spade 2.0 CommonJS Runtime
12
+ copyright 2010 Strobe Inc.
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a
15
+ copy of this software and associated documentation files (the "Software"),
16
+ to deal in the Software without restriction, including without limitation
17
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
18
+ and/or sell copies of the Software, and to permit persons to whom the
19
+ Software is furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in
22
+ all copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30
+ DEALINGS IN THE SOFTWARE.
31
+
32
+ Spade is part of the SproutCore project.
33
+
34
+ SproutCore and the SproutCore logo are trademarks of Sprout Systems, Inc.
35
+
36
+ For more information visit http://www.sproutcore.com/spade
37
+
38
+ ==========================================================================
39
+ @license */
40
+
41
+
42
+ // Compatibility backports
43
+
44
+ if (!Array.prototype.some) {
45
+ Array.prototype.some = function(fun /*, thisp*/) {
46
+ var len = this.length;
47
+ if (typeof fun != "function") { throw new TypeError(); }
48
+
49
+ var thisp = arguments[1];
50
+ for (var i = 0; i < len; i++) {
51
+ if (i in this && fun.call(thisp, this[i], i, this)) { return true; }
52
+ }
53
+ return false;
54
+ };
55
+ }
56
+
57
+ if (!Array.prototype.indexOf) {
58
+ Array.prototype.indexOf = function (obj, fromIndex) {
59
+ if (fromIndex == null) { fromIndex = 0; }
60
+ else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); }
61
+ for (var i = fromIndex, j = this.length; i < j; i++) {
62
+ if (this[i] === obj) { return i; }
63
+ }
64
+ return -1;
65
+ };
66
+ }
67
+
68
+
69
+
70
+ // Make this work when loaded from browser or from node.js
71
+ var spade ;
72
+ (function() {
73
+
74
+ var Spade, Tp, Sandbox, Sp,
75
+ Loader, Lp, K, Evaluator, Ep, Compiler, Cp;
76
+
77
+ // defining these types here will allow the minifier the compact them
78
+ if ('undefined' !== typeof spade) { return; } // nothing to do
79
+
80
+ K = function() {}; // noop
81
+
82
+ // assume id is already normalized
83
+ function packageIdFor(normalizedId) {
84
+ return normalizedId.slice(0, normalizedId.indexOf('/'));
85
+ }
86
+
87
+ function remap(id, contextPkg) {
88
+ var mappings = contextPkg ? contextPkg.mappings : null;
89
+ if (!mappings) { return id; }
90
+
91
+ var packageId = packageIdFor(id);
92
+ if (mappings[packageId]) {
93
+ id = mappings[packageId] + id.slice(id.indexOf('/'));
94
+ }
95
+ return id;
96
+ }
97
+
98
+ function normalize(id, contextId, contextPkg, _asPackage) {
99
+ var idx, len;
100
+
101
+ // slice separator off the end since it is not used...
102
+ if (id[id.length-1]==='/') { id = id.slice(0,-1); }
103
+
104
+ // need to walk if there is a .
105
+ if (id.indexOf('.')>=0) {
106
+ var parts = contextId && (id.charAt(0) ==='.') ? contextId.split('/') : [],
107
+ part, next,
108
+ packageName = parts[0],
109
+ needsCleanup = false;
110
+
111
+ idx = 0;
112
+ len = id.length;
113
+
114
+ if (contextPkg && contextPkg.main && contextId === packageName+'/main') {
115
+ // If we're requiring from main we need to handle relative requires specially
116
+ needsCleanup = true;
117
+ parts = contextPkg.main.replace(/^\.?\//, '').split('/');
118
+ }
119
+
120
+ parts.pop(); // get rid of the last path element since it is a module.
121
+
122
+ while(idx<len) {
123
+ next = id.indexOf('/', idx);
124
+ if (next<0) { next = len; }
125
+ part = id.slice(idx, next);
126
+ if (part==='..') { parts.pop(); }
127
+ else if (part!=='.' && part!=='' && part!==null) { parts.push(part); }
128
+ // skip .., empty, and null.
129
+ idx = next+1;
130
+ }
131
+
132
+ id = parts.join('/');
133
+
134
+ if (needsCleanup) {
135
+ var libPaths = contextPkg.directories.lib;
136
+ for (idx=0,len=libPaths.length; idx<len; idx++){
137
+ id = id.replace(libPaths[idx].replace(/^\.?\//, '')+'/', '');
138
+ }
139
+ id = packageName+'/'+id;
140
+ }
141
+
142
+ // else, just slice off beginning '/' if needed
143
+ } else if (id[0]==='/') { id = id.slice(1); }
144
+
145
+ // if we end up with no separators, make this a pkg
146
+ if (id.indexOf('/')<0) { id = id+(_asPackage ? '/~package' : '/main'); }
147
+
148
+ // slice separators off begin and end
149
+ if (id[0]==='/') { id = id.slice(1); }
150
+
151
+ // Remove unnecessary ~lib references
152
+ id = id.replace('~lib/', '');
153
+
154
+ return remap(id, contextPkg);
155
+ }
156
+
157
+ function parseIdFormats(id, formats) {
158
+ // Handle requires with extension
159
+ var formatRE = new RegExp("^(.+)\\.("+formats.join('|')+")$"), match, format;
160
+ if (id.substring(0,5) !== "file:" && (match = id.match(formatRE))) {
161
+ id = match[1];
162
+ format = match[2];
163
+ formats = [format];
164
+ }
165
+
166
+ return {
167
+ id: id,
168
+ format: format,
169
+ formats: formats
170
+ };
171
+ }
172
+
173
+ // ..........................................................
174
+ // PLATFORM
175
+ //
176
+ // Detect important platform properties. Mostly for determining code
177
+ // that can't run one way or the other.
178
+ var SPADE_PLATFORM;
179
+ if (('undefined'===typeof ENV) || !ENV.SPADE_PLATFORM) {
180
+ SPADE_PLATFORM = { ENGINE: 'browser' };
181
+ } else {
182
+ SPADE_PLATFORM = ENV.SPADE_PLATFORM;
183
+ }
184
+
185
+ var LANG;
186
+ if ('undefined'!==typeof ENV) { LANG = ENV.LANG; }
187
+ if (!LANG && 'undefined'!==typeof navigator) { LANG = navigator.language; }
188
+ if (!LANG) { LANG = 'en-US'; }
189
+
190
+
191
+ function processFactory(id, factory, sandbox) {
192
+ var factoryData = factory.data,
193
+ spade = sandbox.spade,
194
+ pkg = spade.package(id);
195
+ if (typeof factoryData === 'string') {
196
+ factoryData = sandbox.compileFormat(factoryData, factory.filename, factory.format, pkg);
197
+ factoryData = sandbox.compilePreprocessors(factoryData, factory.filename, pkg, id);
198
+ }
199
+ return factoryData;
200
+ }
201
+
202
+ // ..........................................................
203
+ // Sandbox - you could make a secure version if you want...
204
+ //
205
+
206
+ // runs a factory within context and returns exports...
207
+ function execFactory(id, factory, sandbox, spade) {
208
+ var require, mod;
209
+
210
+ var pkg = spade.package(id),
211
+ filename = factory.filename,
212
+ format = factory.format,
213
+ skipPreprocess = factory.skipPreprocess,
214
+ ARGV = sandbox.ARGV,
215
+ ENV = sandbox.ENV,
216
+ fullId = id+'.'+format;
217
+
218
+ require = function(moduleId) {
219
+ return sandbox.require(moduleId, id, pkg);
220
+ };
221
+
222
+ // make the require 'object' have the same API as sandbox and spade.
223
+ require.require = require;
224
+
225
+ require.exists = function(moduleId) {
226
+ return sandbox.exists(normalize(moduleId, id, pkg));
227
+ };
228
+
229
+ require.normalize = function(moduleId) {
230
+ return normalize(moduleId, id, pkg);
231
+ };
232
+
233
+ require.async = function(moduleId, callback) {
234
+ return sandbox.async(normalize(moduleId, id, pkg), callback);
235
+ };
236
+
237
+ require.sandbox = function(name, isolate) {
238
+ return spade.sandbox(name, isolate);
239
+ };
240
+
241
+ require.url = function(moduleId, ext) {
242
+ return sandbox.url(normalize(moduleId, id, pkg), ext);
243
+ };
244
+
245
+ require.runCommand = function(command, args){
246
+ return sandbox.runCommand(command, args, id, pkg);
247
+ };
248
+
249
+ require.id = id; // so you can tell one require from another
250
+
251
+ sandbox._modules[id] = mod = {
252
+ id: id,
253
+ exports: {},
254
+ sandbox: sandbox
255
+ };
256
+
257
+ factoryData = factory.data; // extract the raw module body
258
+
259
+ // evaluate if needed - use cache so we only do it once per sandbox
260
+ if ('string' === typeof factoryData) {
261
+ if (sandbox._factories[fullId]) {
262
+ factoryData = sandbox._factories[fullId];
263
+ } else {
264
+ sandbox._loading[id] = sandbox._loading[fullId] = true;
265
+
266
+ if (!skipPreprocess) {
267
+ factoryData = processFactory(id, factory, sandbox);
268
+ }
269
+
270
+ // The __evalFunc keeps IE 9 happy since it doesn't like
271
+ // unassigned anonymous functions
272
+ factoryData = sandbox.evaluate('__evalFunc = function(require, exports, __module, ARGV, ENV, __filename) {'+factoryData+'} //@ sourceURL='+filename+'\n', filename);
273
+ sandbox._factories[fullId] = factoryData;
274
+ sandbox._loading[id] = sandbox._loading[fullId] = false;
275
+ }
276
+ }
277
+
278
+ if ('function' === typeof factoryData) {
279
+ var ret = factoryData(require, mod.exports, mod, ARGV, ENV, filename);
280
+ if (ret !== undefined) { mod.exports = ret; } // allow return exports
281
+ } else {
282
+ mod.exports = factoryData;
283
+ }
284
+
285
+ return mod.exports;
286
+ }
287
+
288
+ /**
289
+ @constructor
290
+
291
+ Sandbox provides an isolated context for loading and running modules.
292
+ You can create new sandboxes anytime you want. If you pass true for the
293
+ isolate flag, then the sandbox will be created in a separate context if
294
+ supported on the platform. Otherwise it will share globals with the
295
+ default sandbox context.
296
+
297
+ Note that isolated sandboxes are not the same as secure sandboxes. For
298
+ example in the browser, a isolated sandbox is created using an iframe
299
+ which still exposes access to the DOM and parent environment.
300
+
301
+ Isolated sandboxes are mostly useful for testing and sharing plugin code
302
+ that might want to use different versions of packages.
303
+
304
+ @param {Spade} spade
305
+ The spade instance
306
+
307
+ @param {Boolean} isolate
308
+ Set to true if you want to isolate it
309
+
310
+ @returns {Sandbox} instance
311
+ */
312
+ Sandbox = function(spade, name, isolate) {
313
+ if (typeof name !== 'string') {
314
+ isolate = name;
315
+ name = null;
316
+ }
317
+
318
+ if (!name) { name = '(anonymous)'; }
319
+
320
+ this.spade = spade;
321
+ this.name = name;
322
+ this.isIsolated = !!isolate;
323
+ this._factories = {}; // evaluated factories
324
+ this._loading = {}; // list of loading modules
325
+ this._modules = {}; // cached export results
326
+ this._used = {}; // to detect circular references
327
+ };
328
+
329
+ // alias this to help minifier make the page a big smaller.
330
+ Sp = Sandbox.prototype;
331
+
332
+ Sp.toString = function() {
333
+ return '[Sandbox '+this.name+']';
334
+ };
335
+
336
+ /**
337
+ Evaluate the passed string in the Sandbox context, returning the result.
338
+ This is the primitive used to evalute string-encoded factories into
339
+ modules that can execute within a specific context.
340
+ */
341
+ Sp.evaluate = function(code, filename) {
342
+ if (this.isDestroyed) { throw new Error("Sandbox destroyed"); }
343
+ if (!this._evaluatorInited) {
344
+ this._evaluatorInited = true;
345
+ this.spade.evaluator.setup(this);
346
+ }
347
+ return this.spade.evaluator.evaluate(code, this, filename);
348
+ };
349
+
350
+ function findPlugins(sandbox, pkg, type, options) {
351
+ if (!options) { options = {}; }
352
+
353
+ var plugins = [], ret = [], found,
354
+ single = options.single,
355
+ key = options.key,
356
+ searchSelf = (options.searchSelf != false) ? true : options.searchSelf;
357
+
358
+ if (searchSelf) {
359
+ found = pkg && pkg['plugin:'+type];
360
+ if (key) { found = found && found[key]; }
361
+ if (found) {
362
+ if (single) { return found; }
363
+ plugins = plugins.concat(found);
364
+ }
365
+ }
366
+
367
+ // look in immediate dependencies only, if we wanted to have formats we would have required them
368
+ var deps = pkg && pkg.dependencies;
369
+ if (deps) {
370
+ for(var packageId in deps) {
371
+ pkg = sandbox.spade.package(packageId);
372
+ found = pkg && pkg['plugin:'+type];
373
+ if (key) { found = found && found[key]; }
374
+ if (found) { plugins = plugins.concat(found); }
375
+ }
376
+ }
377
+
378
+ // Remove duplicates
379
+ for (var i=0,len=plugins.length; i < len; i++) {
380
+ if (ret.indexOf(plugins[i]) < 0) { ret.push(plugins[i]); }
381
+ }
382
+
383
+ if (single) {
384
+ if (ret.length > 1) { console.warn("Found multiple plugins when expecting one for type: '"+type+"' and key: '"+key+"'"); }
385
+ return ret[0] || null;
386
+ } else {
387
+ return ret;
388
+ }
389
+ }
390
+
391
+ Sp.compileFormat = function(code, filename, format, pkg) {
392
+ var plugin = findPlugins(this, pkg, 'formats', { key: format, single: true });
393
+ if (plugin) { plugin = this.require(plugin); }
394
+ return plugin ? plugin.compileFormat(code, this, filename, format, pkg) : code;
395
+ };
396
+
397
+ Sp.compilePreprocessors = function(code, filename, pkg, id) {
398
+ var plugins = findPlugins(this, pkg, 'preprocessors'), plugin;
399
+ for (var i=0, len=plugins.length; i < len; i++) {
400
+ // Avoid trying to process self
401
+ if (plugins[i] === id) { continue; }
402
+ plugin = this.require(plugins[i]);
403
+ if (!this._loading[id] || plugin.compilePreprocessor) {
404
+ code = plugin.compilePreprocessor(code, this, filename, pkg);
405
+ }
406
+ }
407
+ return code;
408
+ };
409
+
410
+ Sp.compileWrapper = function(code, filename, pkg, id) {
411
+ var plugin = findPlugins(this, pkg, 'wrapper', { single: true, searchSelf: false });
412
+ if (plugin) { plugin = this.require(plugin); }
413
+ return plugin ? plugin.compileWrapper(code, this, filename, pkg, id) : code;
414
+ };
415
+
416
+ /**
417
+ Sandbox-specific require. This is actually the most primitive form of
418
+ require.
419
+ */
420
+ Sp.require = function(id, callingId) {
421
+ var pkg = callingId ? this.spade.package(callingId) : null;
422
+ id = normalize(id, callingId, pkg);
423
+
424
+ var ret = this._modules[id];
425
+ if (ret) { ret = ret.exports; }
426
+
427
+ var factory;
428
+ if (ret) {
429
+ if (!this._used[id]) { this._used[id] = ret; }
430
+ return ret ;
431
+
432
+ } else {
433
+ factory = this.spade.loadFactory(this.spade.resolve(id, this));
434
+ if (!factory) { throw new Error('Module '+id+' not found'); }
435
+
436
+ var spade = this.spade;
437
+ if (!this.ENV) { this.ENV = spade.env(); } // get at the last minute
438
+ if (!this.ARGV) { this.ARGV = spade.argv(); }
439
+
440
+ ret = execFactory(id, factory, this, spade);
441
+
442
+ // detect circular references...
443
+ if (this._used[id] && (this._used[id] !== ret)) {
444
+ throw new Error("Circular require detected for module "+id);
445
+ }
446
+ }
447
+
448
+ return ret ;
449
+ };
450
+
451
+ /**
452
+ Sandbox-specific test to determine if the named module exists or not.
453
+ This property only reflects what is immediately available through the
454
+ sync-loader. Using the async loader may change the return value of this
455
+ call.
456
+ */
457
+ Sp.exists = function(id, callingId) {
458
+ var pkg = callingId ? this.spade.package(callingId) : null;
459
+ id = normalize(id, callingId, pkg);
460
+ if (this._modules[id]) { return true; }
461
+ return this.spade.factoryExists(this.spade.resolve(id, this));
462
+ };
463
+
464
+ /**
465
+ Sandbox-specific async load. This is actually the most primitive form of
466
+ require.
467
+ */
468
+ Sp.async = function(id, callback, callingId) {
469
+ var spade = this.spade, pkg;
470
+
471
+ pkg = callingId ? this.spade.package(callingId) : null;
472
+ id = spade.resolve(normalize(id, callingId, pkg), this);
473
+ return spade.loadFactory(id, callback);
474
+ };
475
+
476
+ Sp.url = function(id, ext, callingId) {
477
+ var ret, pkg;
478
+
479
+ pkg = callingId ? this.spade.package(callingId) : null;
480
+ id = normalize(id, callingId, pkg);
481
+
482
+ pkg = this.spade.package(id);
483
+ if (!pkg) {
484
+ var packageId = packageIdFor(id)+'/~package';
485
+ if (this.spade.exists(packageId)) { this.spade.require(packageId); }
486
+ pkg = this.spade.package(id);
487
+ }
488
+
489
+ if (!pkg) {
490
+ throw new Error("Can't get url for non-existent package "+id);
491
+ }
492
+
493
+ if (!pkg.root) {
494
+ throw new Error('Package for '+id+' does not support urls');
495
+ }
496
+
497
+ ret = pkg.root + id.slice(id.indexOf('/'));
498
+ if (ext) { ret = ret+'.'+ext; }
499
+ return ret ;
500
+ };
501
+
502
+ Sp.runCommand = function(command, args, callerId, pkg){
503
+ var xhr;
504
+ if (window.XMLHttpRequest) {
505
+ xhr = new XMLHttpRequest();
506
+ } else { // IE
507
+ xhr = new ActiveXObject("Microsoft.XMLHTTP");
508
+ }
509
+ var url = "_spade/command",
510
+ params = [];
511
+
512
+ args.command = command;
513
+ args.callerId = callerId;
514
+ args.pkgRoot = pkg && pkg.root;
515
+
516
+ for (var arg in args){
517
+ params.push(encodeURIComponent(arg)+"="+encodeURIComponent(args[arg]));
518
+ }
519
+ params = params.join('&');
520
+
521
+ xhr.open('POST', url, false);
522
+
523
+ try {
524
+ xhr.send(params);
525
+ } catch(e) {
526
+ throw new Error('unable to run command `'+command+'`, most likely either this browser or server does not support this');
527
+ }
528
+
529
+ if (xhr.status === 200) {
530
+ return xhr.responseText;
531
+ // Safari returns -1100 for file:// not found
532
+ } else if (xhr.status === 404 || xhr.status === -1100) {
533
+ throw new Error('unable to run command `'+command+'`, most likely you are running without a supported server');
534
+ } else {
535
+ throw new Error('running command `'+command+'` return status '+xhr.status+': '+xhr.responseText);
536
+ }
537
+ };
538
+
539
+ Sp.isDestroyed = false;
540
+
541
+ Sp.destroy = function() {
542
+ if (!this.isDestroyed) {
543
+ this.isDestroyed = true;
544
+ this.spade.evaluator.teardown(this);
545
+ }
546
+ return this;
547
+ };
548
+
549
+ // ..........................................................
550
+ // LOADER
551
+ //
552
+
553
+ /**
554
+ The built-in loader object knows how to load whole packages as long as
555
+ you have registered an external reference to the package. This is pkg
556
+ info that contains:
557
+
558
+ {
559
+ extern: true, // this is not a real package yet
560
+ src: 'http://example.com/bar', // URL to load
561
+ expects: ['foo', 'bar', 'baz'] // optional modules to expect
562
+ }
563
+ */
564
+ Loader = function() {
565
+ this._loading = {};
566
+ };
567
+
568
+ Lp = Loader.prototype;
569
+
570
+ function syncLoad(spade, id, url, format, force) {
571
+ if (force) { url = url+'?'+(+ new Date()); }
572
+
573
+ if (window.XMLHttpRequest) {
574
+ xhr = new XMLHttpRequest();
575
+ } else { // IE
576
+ xhr = new ActiveXObject("Microsoft.XMLHTTP");
577
+ }
578
+
579
+ xhr.open('GET', url, false);
580
+ try {
581
+ xhr.send(null);
582
+ } catch(e) {
583
+ throw new Error('unable to fetch '+url+', most likely this browser is not supported');
584
+ }
585
+
586
+ // successful file:// requests return 0 in Safari and Firefox
587
+ if (xhr.status === 200 || xhr.status === 0) {
588
+ var body = xhr.responseText;
589
+ if (body.slice(0,2) === '#!') { body = body.slice(body.indexOf('\n')); }
590
+
591
+ if (!format) { format = 'js'; }
592
+ spade.register(id, body, { format: format });
593
+ return true;
594
+
595
+ // Safari returns -1100 for file:// not found
596
+ } else if (xhr.status === 404 || xhr.status === -1100) {
597
+ return false;
598
+
599
+ } else {
600
+ throw new Error('fetching '+url+' return status '+xhr.status+': '+xhr.responseText);
601
+ }
602
+ }
603
+
604
+ function resolveUrl(id, extern) {
605
+ var dirname, dir, dirs, idx,
606
+ packageUrl = extern.root,
607
+ url, urls = [];
608
+
609
+ id = normalize(id);
610
+ id = id.slice(id.indexOf('/')+1); // slide off pkg
611
+
612
+ // get directory
613
+ if (id.charAt(0) === '~') {
614
+ idx = id.indexOf('/');
615
+ dirname = idx>=0 ? id.slice(0, idx) : id;
616
+ id = dirname.length>=id.length ? null : id.slice(dirname.length+1);
617
+ dirname = dirname.slice(1); // get rid of ~
618
+ } else { dirname = 'lib'; }
619
+
620
+ // map to directories
621
+ dirs = extern.directories && extern.directories[dirname];
622
+ if (!dirs) {
623
+ throw new Error("Can't require '"+id+"' when '"+dirname+"' is not a known directory.");
624
+ }
625
+ if (typeof dirs === 'string') { dirs = [dirs]; }
626
+
627
+ for(var idx=0,len=dirs.length; idx < len; idx++){
628
+ dir = dirs[idx];
629
+ // combine elements to form URL
630
+ url = packageUrl;
631
+ if (url === '.') { url = null; }
632
+ if (dir && dir !== '.') { url = url ? url+'/'+dir : dir; }
633
+ if (id && id !== '.') { url = url ? url+'/'+id : id; }
634
+
635
+ // Clean up '.' and '..'
636
+ var parts = url.split('/');
637
+ for(var i=0,plen=parts.length; i<plen;) {
638
+ if (parts[i] === '.') {
639
+ parts = parts.slice(0,i).concat(parts.slice(i+1)); // Remove item
640
+ // Retry at same index
641
+ } else if (parts[i] === '..') {
642
+ if (i === 0) {
643
+ console.warn("Can't resolve leading '..'");
644
+ i++; // Go to next
645
+ } else {
646
+ parts = parts.slice(0,i-1).concat(parts.slice(i+1)); // Remove previous and current item
647
+ i--; // Retry at previous index
648
+ }
649
+ } else {
650
+ i++; // Go to next
651
+ }
652
+ }
653
+ url = parts.join('/');
654
+ urls.push(url);
655
+ }
656
+
657
+ return urls;
658
+ }
659
+ Lp.resolveUrl = resolveUrl;
660
+
661
+ function syncLoadFormats(spade, id, extern, formats) {
662
+ var packageUrl = extern.root,
663
+ urls = resolveUrl(id, extern),
664
+ tryUrls = [];
665
+
666
+ for (var urlIdx=0,urlsLen=urls.length; urlIdx<urlsLen; urlIdx++) {
667
+ for (var formatIdx=0,formatsLen=formats.length; formatIdx<formatsLen; formatIdx++) {
668
+ tryUrls.push(urls[urlIdx]+'.'+formats[formatIdx]);
669
+ }
670
+ }
671
+
672
+ // If we know about a file we want to try to load it first
673
+ // We still will try others incase it doesn't exist
674
+ if (extern.files) {
675
+ for (var idx=0,len=tryUrls.length; idx<len; idx++) {
676
+ var relativeUrl = tryUrls[idx].replace(packageUrl+'/', '');
677
+ if (extern.files.indexOf(relativeUrl) > -1) {
678
+ // Move url to the front
679
+ tryUrls = [tryUrls[idx]].concat(tryUrls.slice(0,idx)).concat(tryUrls.slice(idx+1));
680
+ break;
681
+ }
682
+ }
683
+ }
684
+
685
+ function tryUrl(url) {
686
+ var format = url.match(/\.([^\.]+)$/)[1]; // this is a touch hacky
687
+ return syncLoad(spade, id, url, format, true);
688
+ }
689
+
690
+ return !!tryUrls.some(tryUrl);
691
+ }
692
+
693
+ function verifyInBrowser(id, done) {
694
+ if ('undefined'===typeof document) {
695
+ var err = new Error("Cannot load package "+id+" outside of browser");
696
+ if (done) { done(err); }
697
+ else { throw err; }
698
+ return false;
699
+ }
700
+
701
+ return true;
702
+ }
703
+
704
+ Lp.loadFactory = function(spade, id, formats, done) {
705
+
706
+ var url, loaded, packageId,
707
+ extern = spade.package(id),
708
+ that = this;
709
+
710
+ // loader only works for sync requests if the package info permits sync
711
+ // loading. In production mode, normally it should not.
712
+ if (!done && (!extern || !extern.sync)) { return this; }
713
+
714
+ // this loader only works in the browser
715
+ if (!verifyInBrowser(id, done)) { return this; }
716
+
717
+ if (!done) {
718
+ url = extern.src;
719
+ if (!url) { loaded = syncLoadFormats(spade, id, extern, formats); }
720
+ else { loaded = syncLoad(spade, id, url); }
721
+ if (!loaded) { throw new Error('fetching '+id+' not found'); }
722
+ // not actually loadable
723
+ } else if (!extern || !extern.extern) {
724
+ done(new Error('Module '+id+' not found'));
725
+
726
+ } else {
727
+
728
+ // now do actual load of src
729
+ if (!extern.src) {
730
+ throw new Error("Cannot load package "+id+" without a src URL");
731
+ }
732
+
733
+ // if already loading, just add to queue
734
+ packageId = packageIdFor(normalize(id));
735
+ if (this._loading[packageId]) {
736
+ this._loading[packageId].push(done);
737
+ } else {
738
+ this._loading[packageId] = [done];
739
+ this.loadURL(extern.src, function() { that.didLoad(packageId); });
740
+ // TODO: Load dependencies
741
+ }
742
+ }
743
+ return this;
744
+ };
745
+
746
+ Lp.exists = function(spade, id, formats) {
747
+
748
+ var extern = spade.package(id);
749
+
750
+ // loader only works for sync requests if the package info permits sync
751
+ // loading. In production mode, normally it should not.
752
+ if (!extern || !extern.sync || !extern.root) { return false; }
753
+
754
+ // this loader only works in the browser
755
+ if (!verifyInBrowser(id)) { return false; }
756
+ return syncLoadFormats(spade, id, extern, formats, true);
757
+ };
758
+
759
+ Lp.didLoad = function(packageId) {
760
+ // TODO: verify/load dependencies
761
+ var callbacks = this._loading[packageId];
762
+ delete this._loading[packageId];
763
+ if (callbacks) { callbacks.forEach(function(done) { done(); }); }
764
+ };
765
+
766
+ // actually create a script tag and load it
767
+ Lp.loadURL = function(url, callback) {
768
+ var el, head, didCallback = false;
769
+
770
+ el = document.createElement('script');
771
+ el.src = url;
772
+ el.type = 'text/javascript';
773
+
774
+ el.onload = callback;
775
+ // IE doesn't support onload for scripts
776
+ el.onreadystatechange = function(){
777
+ if (el.readyState === 'loaded' || el.readyState === 'complete') {
778
+ callback();
779
+ el = null; // Avoid memory leaks in IE
780
+ }
781
+ };
782
+
783
+ head = document.head || document.body;
784
+ head.appendChild(el);
785
+ head = null; // Avoid memory leaks in IE
786
+ };
787
+
788
+ // NOTE: On ready stuff mostly stolen from jQuery 1.4. Need to incl here
789
+ // because spade will often be used to load jQuery.
790
+ // Will only be invoked once. Just be prepared to call it
791
+ Lp.scheduleReady = function(callback) {
792
+
793
+ // handle case where ready is invoked AFTER the document is already ready
794
+ if ( document.readyState === "complete" ) { return setTimeout(callback, 1); }
795
+
796
+ var handler, handled = false;
797
+
798
+ // The DOM ready check for Internet Explorer
799
+ function doScrollCheck() {
800
+ if (handled) { return; }
801
+
802
+ try {
803
+ // If IE is used, use the trick by Diego Perini
804
+ // http://javascript.nwbox.com/IEContentLoaded/
805
+ document.documentElement.doScroll("left");
806
+ } catch(e) {
807
+ setTimeout( doScrollCheck, 1 );
808
+ return;
809
+ }
810
+
811
+ // and execute any waiting functions
812
+ handler();
813
+ }
814
+
815
+ // Mozilla, Opera and webkit nightlies currently support this event
816
+ if (document.addEventListener) {
817
+
818
+ handler = function() {
819
+ if (handled) { return; }
820
+ handled = true;
821
+ document.removeEventListener("DOMContentLoaded", handler, false);
822
+ window.removeEventListener('load', handler, false);
823
+ callback();
824
+ };
825
+
826
+ document.addEventListener( "DOMContentLoaded", handler, false);
827
+
828
+ // A fallback to window.onload, that will always work
829
+ window.addEventListener( "load", handler, false );
830
+
831
+ // If IE event model is used
832
+ } else if ( document.attachEvent ) {
833
+
834
+ handler = function() {
835
+ if (!handled && document.readyState === "complete") {
836
+ handled = true;
837
+ document.detachEvent( "onreadystatechange", handler );
838
+ window.detachEvent('onload', handler);
839
+ callback();
840
+ }
841
+ };
842
+
843
+ // ensure firing before onload,
844
+ // maybe late but safe also for iframes
845
+ document.attachEvent("onreadystatechange", handler);
846
+
847
+ // A fallback to window.onload, that will always work
848
+ window.attachEvent( "onload", handler);
849
+
850
+ // If IE and not a frame
851
+ // continually check to see if the document is ready
852
+ var toplevel = false;
853
+
854
+ try {
855
+ toplevel = window.frameElement === null;
856
+ } catch(e) {}
857
+ if ( document.documentElement.doScroll && toplevel ) { doScrollCheck(); }
858
+ }
859
+ };
860
+
861
+ // ..........................................................
862
+ // Evaluator Class
863
+ //
864
+
865
+ Evaluator = function() {};
866
+ Ep = Evaluator.prototype;
867
+
868
+ Ep.setup = function(sandbox) {
869
+ if (sandbox.isIsolated) { throw new Error("Isolated Sandbox not supported"); }
870
+ };
871
+
872
+ Ep.evaluate = function(text, sandbox, filename) {
873
+ return eval(text);
874
+ };
875
+
876
+ Ep.teardown = function(sandbox) {
877
+ // noop by default
878
+ };
879
+
880
+ // ..........................................................
881
+ // Compiler Class
882
+ //
883
+
884
+ Compiler = function(spade) {
885
+ this.spade = spade;
886
+ };
887
+ Cp = Compiler.prototype;
888
+
889
+ Cp.compile = function(id, callingId) {
890
+ var pkg = callingId ? this.spade.package(callingId) : null;
891
+ var sandbox = this.spade.defaultSandbox;
892
+ id = normalize(id, callingId, pkg);
893
+
894
+ // Don't show the wrapper
895
+ if ((pkg && pkg['plugin:wrapper']) === id) return null;
896
+
897
+ factory = this.spade.loadFactory(this.spade.resolve(id, sandbox));
898
+ if (!factory) { throw new Error('Module '+id+' not found'); }
899
+
900
+ var ret = processFactory(id, factory, sandbox);
901
+ if (typeof ret !== 'string') { ret = String(ret); }
902
+ ret = sandbox.compileWrapper(ret, factory.filename, pkg, id);
903
+
904
+ return "/* --------------- "+id+" --------------- */\n"+ret;
905
+ };
906
+
907
+ // ..........................................................
908
+ // Spade Class - defined so we can recreate
909
+ //
910
+
911
+ Spade = function() {
912
+ this.loader = new this.Loader(this);
913
+ this.evaluator = new this.Evaluator(this);
914
+ this.defaultSandbox = this.sandbox();
915
+ this.compiler = new this.Compiler(this);
916
+ this._factories = {};
917
+ this._packages = {};
918
+ };
919
+
920
+ Tp = Spade.prototype;
921
+
922
+ Tp.VERSION = "0.1.1";
923
+
924
+ // expose the classes. We do it this way so that you can create a new
925
+ // Spade instance and treat it like the spade module
926
+ Tp.Spade = Spade;
927
+ Tp.Sandbox = Sandbox;
928
+ Tp.Loader = Loader;
929
+ Tp.Evaluator = Evaluator;
930
+ Tp.Compiler = Compiler;
931
+
932
+ Tp.env = function() {
933
+ if (!this.ENV) { this.ENV = 'undefined' !== typeof ENV ? ENV : {}; }
934
+ if (!this.ENV.SPADE_PLATFORM) { this.ENV.SPADE_PLATFORM = SPADE_PLATFORM; }
935
+ if (!this.ENV.LANG) { this.ENV.LANG = LANG; }
936
+ return this.ENV;
937
+ };
938
+
939
+ Tp.argv = function() {
940
+ if (!this.ARGV) { this.ARGV = 'undefined' !== typeof ARGV ? ARGV : []; }
941
+ return this.ARGV;
942
+ };
943
+
944
+ /**
945
+ Expose the spade require methods to the global context. This should allow
946
+ your global code to access spade in the same way that normal modules
947
+ would.
948
+ */
949
+ Tp.globalize = function() {
950
+ var spade = this;
951
+
952
+ // save old info for conflict...
953
+ this._conflict = {
954
+ require: 'undefined' !== typeof require ? require : undefined,
955
+ ENV: 'undefined' !== typeof ENV ? ENV : undefined,
956
+ ARGV: 'undefined' !== typeof ARGV ? ARGV : undefined
957
+ };
958
+
959
+ require = function() { return spade.require.apply(spade,arguments); };
960
+ ['async', 'sandbox', 'exists', 'url'].forEach(function(key) {
961
+ require[key] = function() { return spade[key].apply(spade, arguments);};
962
+ });
963
+
964
+ ENV = this.env();
965
+ ARGV = this.argv();
966
+ return this;
967
+ };
968
+
969
+ /**
970
+ Restores original values after a call to globalize(). If you call this
971
+ method more than once it will have no effect.
972
+ */
973
+ Tp.noConflict = function() {
974
+ var c = this._conflict;
975
+ if (c) {
976
+ delete this._conflict;
977
+ require = c.require;
978
+ ENV = c.ENV;
979
+ ARGV = c.ARGV;
980
+ }
981
+ return this;
982
+ };
983
+
984
+ /**
985
+ Returns a new sandbox instance attached to the current spade instance.
986
+ Can isolate if preferred.
987
+
988
+ @param {Boolean} isolate
989
+ true if you want the sandbox to be isolated. Throws exception if
990
+ platform cannot isolate.
991
+
992
+ @returns {Sandbox} sandbox instance
993
+ */
994
+ Tp.sandbox = function(name, isolate) {
995
+ return new this.Sandbox(this, name, isolate);
996
+ };
997
+
998
+ /**
999
+ Register a module or package information. You can pass one of the
1000
+ following:
1001
+
1002
+ 'module/id', 'module body string'
1003
+ 'module/id', function() { module func }
1004
+ 'module/id', { exports: 'foo' }
1005
+ 'module/id' - just register module id and no body to indicate presence
1006
+
1007
+ Note also that if you pass just a packageId, it will be normalized to
1008
+ packageId/~package. This is how you register a package.
1009
+
1010
+ @param {String} id
1011
+ The module or package id
1012
+
1013
+ @param {String|Function|Hash} data
1014
+ A module function, module body (as string), or hash of exports to use.
1015
+
1016
+ @param {String} opts
1017
+ Additional metadata only if you are registering a module factory. Known
1018
+ keys include 'filename' and 'format' (for compilation of DSLs).
1019
+
1020
+ */
1021
+ Tp.register = function(id, data, opts) {
1022
+ if (!data) { data = K ; }
1023
+ var t = typeof data, isExtern, factory, isPkg;
1024
+
1025
+ id = normalize(id, null, null, true);
1026
+ isPkg = id.slice(-9) === '/~package';
1027
+
1028
+ // register - note packages can only accept hashes
1029
+ if (isPkg && 'object'!==typeof data) {
1030
+ throw new Error('You can only register hashes for packages');
1031
+ }
1032
+
1033
+ // Set some package defaults
1034
+ if (isPkg) {
1035
+ if (!data.directories) { data.directories = {}; }
1036
+ if (!data.directories.lib) {
1037
+ data.directories.lib = ['lib'];
1038
+ } else if (typeof data.directories.lib === 'string') {
1039
+ data.directories.lib = [data.directories.lib];
1040
+ }
1041
+ if (!data.directories.tests) {
1042
+ data.directories.tests = 'tests';
1043
+ }
1044
+ }
1045
+
1046
+ factory = { data: data };
1047
+ factory.filename = opts && opts.filename ? opts.filename : id;
1048
+ factory.format = opts && opts.format ? opts.format : 'js';
1049
+ factory.skipPreprocess = !!(opts && opts.skipPreprocess); // Force boolean
1050
+
1051
+ // Store with generic id if none, or if JS
1052
+ if (!this._factories[id] || factory.format === 'js') {
1053
+ this._factories[id] = factory;
1054
+ }
1055
+
1056
+ // Store with format
1057
+ if (!isPkg) {
1058
+ this._factories[id+'.'+factory.format] = factory;
1059
+ }
1060
+
1061
+ return this;
1062
+ };
1063
+
1064
+ /**
1065
+ Efficient way to register external packages. Pass a hash of packageIds
1066
+ and source URLs. If the package is already registered, the extern will
1067
+ not replace it so this is safe to call multiple times.
1068
+ */
1069
+ Tp.externs = function(externs, extern) {
1070
+ var tmp, packages = this._packages;
1071
+
1072
+ // normalize method call.
1073
+ if ('string' === typeof externs) {
1074
+ tmp = {};
1075
+ tmp[externs] = extern;
1076
+ externs = tmp;
1077
+ extern = null;
1078
+ }
1079
+
1080
+ for(var packageId in externs) {
1081
+ if (!externs.hasOwnProperty(packageId)) { continue; }
1082
+ if (packages[packageId] && !packages[packageId].extern) { continue; }
1083
+
1084
+ extern = externs[packageId];
1085
+ if ('string' === typeof extern) { extern = {name: packageId, src: extern}; }
1086
+ extern.extern = true;
1087
+ this.register(packageId, extern);
1088
+ }
1089
+ };
1090
+
1091
+ /**
1092
+ Require a module from the default sandbox.
1093
+
1094
+ @param {String} id
1095
+ The module id.
1096
+
1097
+ @returns {Hash} module exports
1098
+ */
1099
+ Tp.require = function(id) {
1100
+ return this.defaultSandbox.require(id, this.defaultSandbox.callerId);
1101
+ };
1102
+
1103
+ /**
1104
+ Compile a file.
1105
+
1106
+ @param {String} id
1107
+ The module id
1108
+
1109
+ @returns {String} the compiled file
1110
+ */
1111
+ Tp.compile = function(id, callingId) {
1112
+ return this.compiler.compile(id, callingId || this.defaultSandbox.callerId);
1113
+ };
1114
+
1115
+ /**
1116
+ Async load a module if it is not already a registered factory. Invoke
1117
+ the passed callback with an optional error object when the module is
1118
+ ready to load.
1119
+ */
1120
+ Tp.async = function(id, callback) {
1121
+ return this.defaultSandbox.async(id, callback);
1122
+ };
1123
+
1124
+ Tp.exists = function(id) {
1125
+ return this.defaultSandbox.exists(id);
1126
+ };
1127
+
1128
+ Tp.url = function(id, ext) {
1129
+ return this.defaultSandbox.url(id, ext);
1130
+ };
1131
+
1132
+ function _collectFormats(spade, ret, pkg) {
1133
+
1134
+ function extractFormats(formats) {
1135
+ if (formats) {
1136
+ for (var key in formats) {
1137
+ if (ret.indexOf(key)<0) { ret.unshift(key); } // new formats go first
1138
+ };
1139
+ }
1140
+ }
1141
+
1142
+ extractFormats(pkg['plugin:formats']);
1143
+
1144
+ var deps = pkg.dependencies;
1145
+ if (!deps) { return ret; }
1146
+
1147
+ for(var packageId in deps) {
1148
+ pkg = spade.package(packageId);
1149
+ if (pkg) { extractFormats(pkg['plugin:formats']); }
1150
+ }
1151
+
1152
+ return ret ;
1153
+ }
1154
+
1155
+ /**
1156
+ Gets a list of known formats for a package
1157
+ */
1158
+ Tp.listFormats = function(pkg){
1159
+ if (typeof pkg === 'string') { pkg = this.package(pkg); }
1160
+ return pkg ? _collectFormats(this, ['js'], pkg) : ['js'];
1161
+ }
1162
+
1163
+ /**
1164
+ Called by the sandbox to get a factory object for the named moduleId
1165
+ */
1166
+ Tp.loadFactory = function(id, callback) {
1167
+ var pkg, formats, format, data, ret;
1168
+
1169
+ // find any formats the current package might know about. Note that for
1170
+ // lazy-loaders this may not be entirely up to date (since not all pkgs
1171
+ // are registered right away)
1172
+ pkg = this.package(id);
1173
+ formats = this.listFormats(pkg);
1174
+
1175
+ data = parseIdFormats(id, formats);
1176
+ id = data.id;
1177
+ format = data.format;
1178
+ formats = data.formats;
1179
+
1180
+ ret = format ? this._factories[id+'.'+format] : this._factories[id];
1181
+
1182
+ if (callback) {
1183
+ if (!ret) {
1184
+ if (this.loader && this.loader.loadFactory) {
1185
+ this.loader.loadFactory(this, id, formats, callback);
1186
+ } else { callback(new Error('Module '+id+' not found')); }
1187
+ } else { callback(); }
1188
+
1189
+ } else if (!ret && this.loader && this.loader.loadFactory) {
1190
+ this.loader.loadFactory(this, id, formats);
1191
+ ret = format ? this._factories[id+'.'+format] : this._factories[id];
1192
+ }
1193
+
1194
+ return ret ;
1195
+ };
1196
+
1197
+ /**
1198
+ Called by the sandbox to determine if the named id exists on the system.
1199
+ The id should already be normalized. If the id is not yet registered, the
1200
+ loader will also be consulted.
1201
+ */
1202
+ Tp.factoryExists = function(id) {
1203
+ if (this._factories[id]) { return true; }
1204
+ if (!this.loader || !this.loader.exists) { return false; }
1205
+
1206
+ var pkg = this.package(id),
1207
+ formats = pkg ? _collectFormats(this, ['js'], pkg) : ['js'],
1208
+ data = parseIdFormats(id, formats);
1209
+
1210
+ return this.loader.exists(this, data.id, data.formats);
1211
+ };
1212
+
1213
+ /**
1214
+ Returns the package info, if any, for the named module or packageId
1215
+ */
1216
+ Tp.package = function(id) {
1217
+ id = packageIdFor(normalize(id))+'/~package';
1218
+ var ret = this._factories[id];
1219
+ return ret ? ret.data : null;
1220
+ };
1221
+
1222
+ /**
1223
+ Normalize a moduleId, expanding it if needed.
1224
+ */
1225
+ Tp.normalize = function(id, contextId) {
1226
+ return normalize(id, contextId);
1227
+ };
1228
+
1229
+ // maps the passed ID to a potentially location specific ID. This gives
1230
+ // the loader a way to vary the factory function returned for a given id
1231
+ // per sandbox
1232
+ Tp.resolve = function(id, sandbox) {
1233
+ if (sandbox && this.loader && this.loader.resolve) {
1234
+ return this.loader.resolve(id, sandbox);
1235
+ } else { return id; }
1236
+ };
1237
+
1238
+ // uses the loader to invoke when the app is ready. For the browser this
1239
+ // is on the ready event.
1240
+ Tp.ready = function(callback) {
1241
+ switch(this.readyState) {
1242
+ case 'ready':
1243
+ callback();
1244
+ break;
1245
+
1246
+ case 'scheduled':
1247
+ this._readyQueue.push(callback);
1248
+ break;
1249
+
1250
+ default:
1251
+ this._readyQueue = [callback];
1252
+ this.readyState = 'scheduled';
1253
+ if (this.loader && this.loader.scheduleReady) {
1254
+ var that = this;
1255
+ this.loader.scheduleReady(function() {
1256
+ var queue = that._readyQueue, len = queue ? queue.length : 0;
1257
+ that._readyQueue = null;
1258
+ that.readyState = 'ready';
1259
+ for(var idx=0;idx<len;idx++) { queue[idx](); }
1260
+ });
1261
+
1262
+ } else {
1263
+ throw new Error('Loader does not support activate on ready state');
1264
+ }
1265
+ }
1266
+ };
1267
+
1268
+ // instantiate spade and also attach class for testing
1269
+ spade = new Spade();
1270
+
1271
+ // in the browser - if ENV and ARGS are not defined, just create some
1272
+ // reasonable defaults. We assume that when loading strobe from the CLI
1273
+ // these will already be setup.
1274
+ if (SPADE_PLATFORM.engine === 'browser') { spade.globalize(); }
1275
+
1276
+ // make this work when called as a module
1277
+ if ('undefined' !== typeof require) {
1278
+ if ('undefined' !== typeof __module) { __module.exports = spade; }
1279
+ else if ('undefined' !== typeof module) { module.exports = spade; }
1280
+ }
1281
+
1282
+ })();
1283
+