spade 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -12,3 +12,4 @@ packages/sproutcore-runtime/tests/ct-runner.js
12
12
  *.swp
13
13
  .DS_Store
14
14
  *.spd
15
+ node_modules
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spade (0.0.5)
4
+ spade (0.0.7)
5
+ childlabor (~> 0.0.3)
5
6
  eventmachine (~> 0.12.10)
6
7
  gemcutter (~> 0.6.1)
7
8
  highline (~> 1.6.1)
@@ -13,6 +14,7 @@ PATH
13
14
  GEM
14
15
  remote: http://rubygems.org/
15
16
  specs:
17
+ childlabor (0.0.3)
16
18
  diff-lcs (1.1.2)
17
19
  eventmachine (0.12.10)
18
20
  gemcutter (0.6.1)
@@ -27,11 +29,10 @@ GEM
27
29
  rspec-expectations (2.5.0)
28
30
  diff-lcs (~> 1.1.2)
29
31
  rspec-mocks (2.5.0)
30
- therubyracer (0.8.0)
32
+ therubyracer (0.8.1)
31
33
  thor (0.14.6)
32
34
 
33
35
  PLATFORMS
34
- java
35
36
  ruby
36
37
 
37
38
  DEPENDENCIES
@@ -1,9 +1,10 @@
1
1
  require('sproutcore-corefoundation');
2
2
 
3
3
  // Remove loading text
4
- jQuery(document).ready(function() {
5
- $(document.body).html('');
6
- });
4
+ $(document.body).html('');
5
+
6
+ // Trigger onReady handler
7
+ SC.onReady.done();
7
8
 
8
9
  require('./todos');
9
10
  require('./~resources/templates/todos');
@@ -7,7 +7,8 @@
7
7
 
8
8
  var PATH = require('path'),
9
9
  SYS = require('sys'),
10
- FS = require('fs');
10
+ FS = require('fs'),
11
+ SPADE_DIR = '.spade'; // Would be nice if we could share Spade::SPADE_DIR
11
12
 
12
13
  exports.Loader = function() {
13
14
  this.root = process.cwd();
@@ -70,18 +71,19 @@ Lp.packages = function() {
70
71
  if (this._packages) return this._packages;
71
72
  var packages = {};
72
73
  this._packages = packages;
73
-
74
- // add global packages in spade project
75
- var globals = PATH.normalize(PATH.join(__filename, '..', '..', '..', 'packages'));
76
- if (PATH.existsSync(globals) && FS.statSync(globals).isDirectory()) {
77
- FS.readdirSync(globals).forEach(function(name) {
78
- addPackage(packages, globals, name);
74
+
75
+ // add spade install dir
76
+ var spadeDir = PATH.join(process.env.HOME, SPADE_DIR, 'gems');
77
+ if (PATH.existsSync(spadeDir) && FS.statSync(spadeDir).isDirectory()) {
78
+ FS.readdirSync(spadeDir).forEach(function(name){
79
+ addPackage(packages, spadeDir, name);
79
80
  });
80
81
  }
81
-
82
+
83
+ // Add other dirs in reverse order of precedence
82
84
  var dirs = ['.spade/packages', 'node_modules', '.node_modules', 'vendor/cache', 'vendor/packages', 'packages'];
83
85
  var rootdir = PATH.normalize(this.root);
84
-
86
+
85
87
  dirs.forEach(function(dirname) {
86
88
  dirname = dirname.split('/');
87
89
  dirname.unshift(rootdir);
@@ -98,7 +100,6 @@ Lp.packages = function() {
98
100
  return packages;
99
101
  };
100
102
 
101
- // FIXME: This should check to see if the package is already loaded before loading again
102
103
  Lp.loadFactory = function(spade, id, formats, done) {
103
104
  if (!formats) { formats = []; }
104
105
  if (formats.length === 0) { formats.push('js'); }
@@ -31,4 +31,14 @@ Sandbox.prototype.compile = function(code, filename) {
31
31
  return VM.runInContext(code, this.ctx, filename || '(unknown)');
32
32
  };
33
33
 
34
+ Sandbox.prototype.globals = function() {
35
+ var output = {};
36
+ for (var name in this.ctx){
37
+ if (this.ctx.hasOwnProperty(name)) {
38
+ output[name] = this.ctx[name];
39
+ }
40
+ }
41
+ return output;
42
+ };
43
+
34
44
  exports.Sandbox = Sandbox;
@@ -11,22 +11,22 @@
11
11
  Spade 2.0 CommonJS Runtime
12
12
  copyright 2010 Strobe Inc.
13
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
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
19
  Software is furnished to do so, subject to the following conditions:
20
20
 
21
- The above copyright notice and this permission notice shall be included in
21
+ The above copyright notice and this permission notice shall be included in
22
22
  all copies or substantial portions of the Software.
23
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
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
30
  DEALINGS IN THE SOFTWARE.
31
31
 
32
32
  Spade is part of the SproutCore project.
@@ -41,31 +41,31 @@ For more information visit http://www.sproutcore.com/spade
41
41
  // Make this work when loaded from browser or from node.js
42
42
  var spade ;
43
43
  (function() {
44
-
45
- var Spade, Tp, Sandbox, Sp,
44
+
45
+ var Spade, Tp, Sandbox, Sp,
46
46
  Loader, Lp, K, Compiler, Cp;
47
-
47
+
48
48
  // defining these types here will allow the minifier the compact them
49
49
  if ('undefined' !== typeof spade) return ; // nothing to do
50
-
50
+
51
51
  K = function() {}; // noop
52
52
 
53
53
  // assume id is already normalized
54
54
  function packageIdFor(normalizedId) {
55
55
  return normalizedId.slice(0, normalizedId.indexOf('/'));
56
56
  }
57
-
57
+
58
58
  function remap(id, contextPkg) {
59
59
  var mappings = contextPkg ? contextPkg.mappings : null;
60
60
  if (!mappings) return id;
61
-
61
+
62
62
  var packageId = packageIdFor(id);
63
63
  if (mappings[packageId]) {
64
64
  id = mappings[packageId] + id.slice(id.indexOf('/'));
65
65
  }
66
66
  return id;
67
67
  }
68
-
68
+
69
69
  function normalize(id, contextId, contextPkg, _asPackage) {
70
70
  // slice separator off the end since it is not used...
71
71
  if (id[id.length-1]==='/') id = id.slice(0,-1);
@@ -104,7 +104,7 @@ var spade ;
104
104
 
105
105
  // else, just slice off beginning '/' if needed
106
106
  } else if (id[0]==='/') id = id.slice(1);
107
-
107
+
108
108
  // if we end up with no separators, make this a pkg
109
109
  if (id.indexOf('/')<0) id = id+(_asPackage ? '/~package' : '/main');
110
110
  // may need to walk if there is a separator...
@@ -122,9 +122,25 @@ var spade ;
122
122
  return remap(id, contextPkg);
123
123
  }
124
124
 
125
+ function parseIdFormats(id, formats) {
126
+ // Handle requires with extension
127
+ var formatRE = new RegExp("^(.+)\\.("+formats.join('|')+")$"), match, format;
128
+ if (id.substring(0,5) !== "file:" && (match = id.match(formatRE))) {
129
+ id = match[1];
130
+ format = match[2];
131
+ formats = [format];
132
+ }
133
+
134
+ return {
135
+ id: id,
136
+ format: format,
137
+ formats: formats
138
+ };
139
+ }
140
+
125
141
  // ..........................................................
126
142
  // PLATFORM
127
- //
143
+ //
128
144
  // Detect important platform properties. Mostly for determining code
129
145
  // that can't run one way or the other.
130
146
  var SPADE_PLATFORM;
@@ -133,50 +149,51 @@ var spade ;
133
149
  } else {
134
150
  SPADE_PLATFORM = ENV.SPADE_PLATFORM;
135
151
  }
136
-
152
+
137
153
  var LANG;
138
154
  if ('undefined'!==typeof ENV) LANG = ENV.LANG;
139
155
  if (!LANG && 'undefined'!==typeof navigator) LANG = navigator.language;
140
156
  if (!LANG) LANG = 'en-US';
141
-
157
+
142
158
 
143
159
  // ..........................................................
144
160
  // Sandbox - you could make a secure version if you want...
145
- //
146
-
161
+ //
162
+
147
163
  // runs a factory within context and returns exports...
148
164
  function execFactory(id, factory, sandbox, spade) {
149
165
  var require, mod;
150
-
166
+
151
167
  var pkg = spade.package(id),
152
168
  filename = factory.filename,
153
169
  format = factory.format,
154
170
  ARGV = sandbox.ARGV,
155
- ENV = sandbox.ENV;
156
-
171
+ ENV = sandbox.ENV,
172
+ fullId = id+'.'+format;
173
+
157
174
  require = function(moduleId) {
158
175
  return sandbox.require(moduleId, id, pkg);
159
176
  };
160
177
 
161
178
  // make the require 'object' have the same API as sandbox and spade.
162
179
  require.require = require;
163
-
180
+
164
181
  require.exists = function(moduleId) {
165
- return sandbox.exists(normalize(moduleId, id, pkg));
182
+ return sandbox.exists(normalize(moduleId, id, pkg));
166
183
  };
167
-
184
+
168
185
  require.normalize = function(moduleId) {
169
186
  return normalize(moduleId, id, pkg);
170
187
  };
171
-
188
+
172
189
  require.async = function(moduleId, callback) {
173
190
  return sandbox.async(normalize(moduleId, id, pkg), callback);
174
191
  };
175
-
192
+
176
193
  require.sandbox = function(name, isolate) {
177
194
  return spade.sandbox(name, isolate);
178
195
  };
179
-
196
+
180
197
  require.url = function(moduleId, ext) {
181
198
  return sandbox.url(normalize(moduleId, id, pkg), ext);
182
199
  };
@@ -186,26 +203,26 @@ var spade ;
186
203
  };
187
204
 
188
205
  require.id = id; // so you can tell one require from another
189
-
190
- sandbox._modules[id] = mod = {
191
- id: id,
192
- exports: {},
193
- sandbox: sandbox
206
+
207
+ sandbox._modules[id] = mod = {
208
+ id: id,
209
+ exports: {},
210
+ sandbox: sandbox
194
211
  };
195
-
212
+
196
213
  factory = factory.data; // extract the raw module body
197
-
214
+
198
215
  // compile if needed - use cache so we only do it once per sandbox
199
216
  if ('string' === typeof factory) {
200
- if (sandbox._factories[id]) {
201
- factory = sandbox._factories[id];
217
+ if (sandbox._factories[fullId]) {
218
+ factory = sandbox._factories[fullId];
202
219
  } else {
203
- sandbox._loading[id] = true;
220
+ sandbox._loading[id] = sandbox._loading[fullId] = true;
204
221
  factory = sandbox.compileFormat(factory, filename, format, pkg);
205
222
  factory = sandbox.compilePreprocessors(factory, filename, pkg, id);
206
223
  factory = sandbox.compile('(function(require, exports, __module, ARGV, ENV, __filename) {'+factory+';\n}) //@ sourceURL='+filename+'\n', filename);
207
- sandbox._factories[id] = factory;
208
- sandbox._loading[id] = false;
224
+ sandbox._factories[fullId] = factory;
225
+ sandbox._loading[id] = sandbox._loading[fullId] = false;
209
226
  }
210
227
  }
211
228
 
@@ -218,29 +235,29 @@ var spade ;
218
235
 
219
236
  return mod.exports;
220
237
  }
221
-
238
+
222
239
  /**
223
240
  @constructor
224
-
241
+
225
242
  Sandbox provides an isolated context for loading and running modules.
226
- You can create new sandboxes anytime you want. If you pass true for the
227
- isolate flag, then the sandbox will be created in a separate context if
228
- supported on the platform. Otherwise it will share globals with the
243
+ You can create new sandboxes anytime you want. If you pass true for the
244
+ isolate flag, then the sandbox will be created in a separate context if
245
+ supported on the platform. Otherwise it will share globals with the
229
246
  default sandbox context.
230
-
231
- Note that isolated sandboxes are not the same as secure sandboxes. For
232
- example in the browser, a isolated sandbox is created using an iframe
233
- which still exposes access to the DOM and parent environment.
234
-
247
+
248
+ Note that isolated sandboxes are not the same as secure sandboxes. For
249
+ example in the browser, a isolated sandbox is created using an iframe
250
+ which still exposes access to the DOM and parent environment.
251
+
235
252
  Isolated sandboxes are mostly useful for testing and sharing plugin code
236
253
  that might want to use different versions of packages.
237
-
254
+
238
255
  @param {Spade} spade
239
256
  The spade instance
240
-
257
+
241
258
  @param {Boolean} isolate
242
259
  Set to true if you want to isolate it
243
-
260
+
244
261
  @returns {Sandbox} instance
245
262
  */
246
263
  Sandbox = function(spade, name, isolate) {
@@ -248,10 +265,10 @@ var spade ;
248
265
  isolate = name;
249
266
  name = null;
250
267
  }
251
-
268
+
252
269
  if (!name) name = '(anonymous)';
253
-
254
- this.spade = spade;
270
+
271
+ this.spade = spade;
255
272
  this.name = name;
256
273
  this.isIsolated = !!isolate;
257
274
  this._factories = {}; // compiled factories
@@ -262,14 +279,14 @@ var spade ;
262
279
 
263
280
  // alias this to help minifier make the page a big smaller.
264
281
  Sp = Sandbox.prototype;
265
-
282
+
266
283
  Sp.toString = function() {
267
284
  return '[Sandbox '+this.name+']';
268
285
  };
269
286
 
270
287
  /**
271
288
  Evaluate the passed string in the Sandbox context, returning the result.
272
- This is the primitive used to compile string-encoded factories into
289
+ This is the primitive used to compile string-encoded factories into
273
290
  modules that can execute within a specific context.
274
291
  */
275
292
  Sp.compile = function(code, filename) {
@@ -344,7 +361,7 @@ var spade ;
344
361
 
345
362
  var ret = this._modules[id];
346
363
  if (ret) ret = ret.exports;
347
-
364
+
348
365
  if (ret) {
349
366
  if (!this._used[id]) this._used[id] = ret;
350
367
  return ret ;
@@ -356,9 +373,9 @@ var spade ;
356
373
  var spade = this.spade;
357
374
  if (!this.ENV) this.ENV = spade.env(); // get at the last minute
358
375
  if (!this.ARGV) this.ARGV = spade.argv();
359
-
376
+
360
377
  ret = execFactory(id, factory, this, spade);
361
-
378
+
362
379
  // detect circular references...
363
380
  if (this._used[id] && (this._used[id] !== ret)) {
364
381
  throw new Error("Circular require detected for module "+id);
@@ -378,34 +395,34 @@ var spade ;
378
395
  var pkg = callingId ? this.spade.package(callingId) : null;
379
396
  id = normalize(id, callingId, pkg);
380
397
  if (this._modules[id]) return true;
381
- return this.spade.factoryExists(this.spade.resolve(id, this));
398
+ return this.spade.factoryExists(this.spade.resolve(id, this));
382
399
  };
383
-
400
+
384
401
  /**
385
402
  Sandbox-specific async load. This is actually the most primitive form of
386
403
  require.
387
404
  */
388
405
  Sp.async = function(id, callback, callingId) {
389
406
  var spade = this.spade, pkg;
390
-
407
+
391
408
  pkg = callingId ? this.spade.package(callingId) : null;
392
409
  id = spade.resolve(normalize(id, callingId, pkg), this);
393
410
  return spade.loadFactory(id, callback);
394
411
  };
395
-
412
+
396
413
  Sp.url = function(id, ext, callingId) {
397
- var ret, pkg;
398
-
414
+ var ret, pkg;
415
+
399
416
  pkg = callingId ? this.spade.package(callingId) : null;
400
417
  id = normalize(id, callingId, pkg);
401
-
402
- pkg = this.spade.package(id);
418
+
419
+ pkg = this.spade.package(id);
403
420
  if (!pkg) {
404
421
  var packageId = packageIdFor(id)+'/~package';
405
422
  if (this.spade.exists(packageId)) this.spade.require(packageId);
406
423
  pkg = this.spade.package(id);
407
424
  }
408
-
425
+
409
426
  if (!pkg) {
410
427
  throw new Error("Can't get url for non-existent package "+id);
411
428
  }
@@ -413,7 +430,7 @@ var spade ;
413
430
  if (!pkg.root) {
414
431
  throw new Error('Package for '+id+' does not support urls');
415
432
  }
416
-
433
+
417
434
  ret = pkg.root + id.slice(id.indexOf('/'));
418
435
  if (ext) ret = ret+'.'+ext;
419
436
  return ret ;
@@ -452,7 +469,7 @@ var spade ;
452
469
  };
453
470
 
454
471
  Sp.isDestroyed = false;
455
-
472
+
456
473
  Sp.destroy = function() {
457
474
  if (!this.isDestroyed) {
458
475
  this.isDestroyed = true;
@@ -460,16 +477,16 @@ var spade ;
460
477
  }
461
478
  return this;
462
479
  };
463
-
480
+
464
481
  // ..........................................................
465
482
  // LOADER
466
- //
467
-
483
+ //
484
+
468
485
  /**
469
486
  The built-in loader object knows how to load whole packages as long as
470
487
  you have registered an external reference to the package. This is pkg
471
488
  info that contains:
472
-
489
+
473
490
  {
474
491
  extern: true, // this is not a real package yet
475
492
  src: 'http://example.com/bar', // URL to load
@@ -479,12 +496,12 @@ var spade ;
479
496
  Loader = function() {
480
497
  this._loading = {};
481
498
  };
482
-
499
+
483
500
  Lp = Loader.prototype;
484
501
 
485
502
  function syncLoad(spade, id, url, format, force) {
486
503
  if (force) url = url+'?'+Date.now();
487
-
504
+
488
505
  var xhr = new XMLHttpRequest();
489
506
  xhr.open('GET', url, false);
490
507
  try {
@@ -590,21 +607,21 @@ var spade ;
590
607
  if ('undefined'===typeof document) {
591
608
  var err = new Error("Cannot load package "+id+" outside of browser");
592
609
  if (done) done(err);
593
- else throw err;
610
+ else throw err;
594
611
  return false;
595
612
  }
596
-
613
+
597
614
  return true;
598
615
  }
599
-
616
+
600
617
  Lp.loadFactory = function(spade, id, formats, done) {
601
618
 
602
- var url, loaded, packageId,
619
+ var url, loaded, packageId,
603
620
  extern = spade.package(id), that = this;
604
621
 
605
622
  // loader only works for sync requests if the package info permits sync
606
623
  // loading. In production mode, normally it should not.
607
- if (!done && (!extern || !extern.sync)) return this;
624
+ if (!done && (!extern || !extern.sync)) return this;
608
625
 
609
626
  // this loader only works in the browser
610
627
  if (!verifyInBrowser(id, done)) return this;
@@ -617,14 +634,14 @@ var spade ;
617
634
  // not actually loadable
618
635
  } else if (!extern || !extern.extern) {
619
636
  done(new Error('Module '+id+' not found'));
620
-
637
+
621
638
  } else {
622
639
 
623
640
  // now do actual load of src
624
641
  if (!extern.src) {
625
642
  throw new Error("Cannot load package "+id+" without a src URL");
626
643
  }
627
-
644
+
628
645
  // if already loading, just add to queue
629
646
  packageId = packageIdFor(normalize(id));
630
647
  if (this._loading[packageId]) {
@@ -634,34 +651,34 @@ var spade ;
634
651
  this.loadURL(extern.src, function() { that.didLoad(packageId); });
635
652
  // TODO: Load dependencies
636
653
  }
637
- }
654
+ }
638
655
  return this;
639
656
  };
640
657
 
641
658
  Lp.exists = function(spade, id, formats) {
642
659
 
643
660
  var extern = spade.package(id);
644
-
661
+
645
662
  // loader only works for sync requests if the package info permits sync
646
663
  // loading. In production mode, normally it should not.
647
- if (!extern || !extern.sync || !extern.root) return false;
664
+ if (!extern || !extern.sync || !extern.root) return false;
648
665
 
649
666
  // this loader only works in the browser
650
667
  if (!verifyInBrowser(id)) return false;
651
668
  return syncLoadFormats(spade, id, extern, formats, true);
652
669
  };
653
-
670
+
654
671
  Lp.didLoad = function(packageId) {
655
672
  // TODO: verify/load dependencies
656
673
  var callbacks = this._loading[packageId];
657
674
  delete this._loading[packageId];
658
675
  if (callbacks) callbacks.forEach(function(done) { done(); });
659
676
  };
660
-
677
+
661
678
  // actually create a script tag and load it
662
679
  Lp.loadURL = function(url, callback) {
663
680
  var el, head;
664
-
681
+
665
682
  el = document.createElement('script');
666
683
  el.src = url;
667
684
  el.type = 'text/javascript';
@@ -681,7 +698,7 @@ var spade ;
681
698
  if ( document.readyState === "complete" ) return setTimeout(callback, 1);
682
699
 
683
700
  var handler, handled = false;
684
-
701
+
685
702
  // The DOM ready check for Internet Explorer
686
703
  function doScrollCheck() {
687
704
  if (handled) return;
@@ -701,7 +718,7 @@ var spade ;
701
718
 
702
719
  // Mozilla, Opera and webkit nightlies currently support this event
703
720
  if (document.addEventListener) {
704
-
721
+
705
722
  handler = function() {
706
723
  if (handled) return;
707
724
  handled = true;
@@ -709,15 +726,15 @@ var spade ;
709
726
  window.removeEventListener('load', handler, false);
710
727
  callback();
711
728
  };
712
-
729
+
713
730
  document.addEventListener( "DOMContentLoaded", handler, false);
714
-
731
+
715
732
  // A fallback to window.onload, that will always work
716
733
  window.addEventListener( "load", handler, false );
717
734
 
718
735
  // If IE event model is used
719
736
  } else if ( document.attachEvent ) {
720
-
737
+
721
738
  handler = function() {
722
739
  if (!handled && document.readyState === "complete") {
723
740
  handled = true;
@@ -730,7 +747,7 @@ var spade ;
730
747
  // ensure firing before onload,
731
748
  // maybe late but safe also for iframes
732
749
  document.attachEvent("onreadystatechange", handler);
733
-
750
+
734
751
  // A fallback to window.onload, that will always work
735
752
  window.attachEvent( "onload", handler);
736
753
 
@@ -744,88 +761,88 @@ var spade ;
744
761
  if ( document.documentElement.doScroll && toplevel ) doScrollCheck();
745
762
  }
746
763
  };
747
-
764
+
748
765
  // ..........................................................
749
766
  // Compiler Class
750
- //
751
-
767
+ //
768
+
752
769
  Compiler = function() {};
753
770
  Cp = Compiler.prototype;
754
-
771
+
755
772
  Cp.setup = function(sandbox) {
756
773
  if (sandbox.isIsolated) throw new Error("Isolated Sandbox not supported");
757
774
  };
758
-
775
+
759
776
  Cp.compile = function(text, sandbox, filename) {
760
777
  return eval(text);
761
778
  };
762
-
779
+
763
780
  Cp.teardown = function(sandbox) {
764
781
  // noop by default
765
782
  };
766
-
783
+
767
784
  // ..........................................................
768
- // Spade Class - defined so we can recreate
769
- //
770
-
785
+ // Spade Class - defined so we can recreate
786
+ //
787
+
771
788
  Spade = function() {
772
789
  this.loader = new this.Loader(this);
773
790
  this.compiler = new this.Compiler(this);
774
791
  this.defaultSandbox = this.sandbox();
775
- this._factories = {};
792
+ this._factories = {};
776
793
  this._packages = {};
777
-
794
+
778
795
  // register this instance as the result of the spade package.
779
796
  var inst = this;
780
797
  this.register('spade', { "name": "spade", "version": this.VERSION });
781
798
  this.register('spade/main', function(r, e, m) { m.exports = inst; });
782
799
  };
783
-
800
+
784
801
  Tp = Spade.prototype;
785
802
 
786
803
  Tp.VERSION = "0.1.0";
787
-
804
+
788
805
  // expose the classes. We do it this way so that you can create a new
789
806
  // Spade instance and treat it like the spade module
790
807
  Tp.Spade = Spade;
791
808
  Tp.Sandbox = Sandbox;
792
809
  Tp.Loader = Loader;
793
810
  Tp.Compiler = Compiler;
794
-
811
+
795
812
  Tp.env = function() {
796
813
  if (!this.ENV) this.ENV = 'undefined' !== typeof ENV ? ENV : {};
797
814
  if (!this.ENV.SPADE_PLATFORM) this.ENV.SPADE_PLATFORM = SPADE_PLATFORM;
798
815
  if (!this.ENV.LANG) this.ENV.LANG = LANG;
799
816
  return this.ENV;
800
817
  };
801
-
818
+
802
819
  Tp.argv = function() {
803
820
  if (!this.ARGV) this.ARGV = 'undefined' !== typeof ARGV ? ARGV : [];
804
821
  return this.ARGV;
805
822
  };
806
-
823
+
807
824
  /**
808
825
  Expose the spade require methods to the global context. This should allow
809
- your global code to access spade in the same way that normal modules
826
+ your global code to access spade in the same way that normal modules
810
827
  would.
811
828
  */
812
829
  Tp.globalize = function() {
813
830
  var spade = this;
814
-
831
+
815
832
  // save old info for conflict...
816
833
  this._conflict = {
817
834
  require: 'undefined' !== typeof require ? require : undefined,
818
835
  ENV: 'undefined' !== typeof ENV ? ENV : undefined,
819
836
  ARGV: 'undefined' !== typeof ARGV ? ARGV : undefined
820
837
  };
821
-
838
+
822
839
  require = function() { return spade.require.apply(spade,arguments); };
823
840
  ['async', 'sandbox', 'exists', 'url'].forEach(function(key) {
824
841
  require[key] = function() { return spade[key].apply(spade, arguments);};
825
842
  });
826
-
843
+
827
844
  ENV = this.env();
828
- ARGV = this.argv();
845
+ ARGV = this.argv();
829
846
  return this;
830
847
  };
831
848
 
@@ -843,77 +860,89 @@ var spade ;
843
860
  }
844
861
  return this;
845
862
  };
846
-
863
+
847
864
  /**
848
865
  Returns a new sandbox instance attached to the current spade instance.
849
866
  Can isolate if preferred.
850
-
867
+
851
868
  @param {Boolean} isolate
852
- true if you want the sandbox to be isolated. Throws exception if
869
+ true if you want the sandbox to be isolated. Throws exception if
853
870
  platform cannot isolate.
854
-
871
+
855
872
  @returns {Sandbox} sandbox instance
856
873
  */
857
874
  Tp.sandbox = function(name, isolate) {
858
875
  return new this.Sandbox(this, name, isolate);
859
876
  };
860
-
877
+
861
878
  /**
862
879
  Register a module or package information. You can pass one of the
863
880
  following:
864
-
881
+
865
882
  'module/id', 'module body string'
866
883
  'module/id', function() { module func }
867
884
  'module/id', { exports: 'foo' }
868
885
  'module/id' - just register module id and no body to indicate presence
869
-
870
- Note also that if you pass just a packageId, it will be normalized to
886
+
887
+ Note also that if you pass just a packageId, it will be normalized to
871
888
  packageId/~package. This is how you register a package.
872
-
889
+
873
890
  @param {String} id
874
891
  The module or package id
875
-
892
+
876
893
  @param {String|Function|Hash} data
877
894
  A module function, module body (as string), or hash of exports to use.
878
-
895
+
879
896
  @param {String} opts
880
- Additional metadata only if you are registering a module factory. Known
897
+ Additional metadata only if you are registering a module factory. Known
881
898
  keys include 'filename' and 'format' (for compilation of DSLs).
882
-
899
+
883
900
  */
884
901
  Tp.register = function(id, data, opts) {
885
- if (!data) data = K ;
886
- var t = typeof data, isExtern, factory;
902
+ if (!data) data = K ;
903
+ var t = typeof data, isExtern, factory, isPkg;
887
904
 
888
- // register - note packages can only accept hashes
889
905
  id = normalize(id, null, null, true);
890
- if (id.slice(-9) === '/~package' && 'object'!==typeof data) {
906
+ isPkg = id.slice(-9) === '/~package';
907
+
908
+ // register - note packages can only accept hashes
909
+ if (isPkg && 'object'!==typeof data) {
891
910
  throw new Error('You can only register hashes for packages');
892
911
  }
893
-
894
- this._factories[id] = factory = { data: data };
912
+
913
+ factory = { data: data };
895
914
  factory.filename = opts && opts.filename ? opts.filename : id;
896
915
  factory.format = opts && opts.format ? opts.format : 'js';
897
916
 
917
+ // Store with generic id if none, or if JS
918
+ if (!this._factories[id] || factory.format === 'js') {
919
+ this._factories[id] = factory;
920
+ }
921
+
922
+ // Store with format
923
+ if (!isPkg) {
924
+ this._factories[id+'.'+factory.format] = factory;
925
+ }
926
+
898
927
  return this;
899
928
  };
900
929
 
901
930
  /**
902
- Efficient way to register external packages. Pass a hash of packageIds
931
+ Efficient way to register external packages. Pass a hash of packageIds
903
932
  and source URLs. If the package is already registered, the extern will
904
933
  not replace it so this is safe to call multiple times.
905
934
  */
906
935
  Tp.externs = function(externs, extern) {
907
936
  var tmp, packages = this._packages;
908
-
937
+
909
938
  // normalize method call.
910
939
  if ('string' === typeof externs) {
911
- tmp = {};
912
- tmp[externs] = extern;
913
- externs = tmp;
940
+ tmp = {};
941
+ tmp[externs] = extern;
942
+ externs = tmp;
914
943
  extern = null;
915
944
  }
916
-
945
+
917
946
  for(var packageId in externs) {
918
947
  if (!externs.hasOwnProperty(packageId)) continue;
919
948
  if (packages[packageId] && !packages[packageId].extern) continue;
@@ -924,20 +953,20 @@ var spade ;
924
953
  this.register(packageId, extern);
925
954
  }
926
955
  };
927
-
956
+
928
957
  /**
929
958
  Require a module from the default sandbox.
930
-
959
+
931
960
  @param {String} id
932
961
  The module id.
933
-
934
- @returns {Hash} module exports
962
+
963
+ @returns {Hash} module exports
935
964
  */
936
965
  Tp.require = function(id) {
937
966
  return this.defaultSandbox.require(id, this.defaultSandbox.callerId);
938
967
  };
939
968
 
940
-
969
+
941
970
  /**
942
971
  Async load a module if it is not already a registered factory. Invoke
943
972
  the passed callback with an optional error object when the module is
@@ -946,17 +975,17 @@ var spade ;
946
975
  Tp.async = function(id, callback) {
947
976
  return this.defaultSandbox.async(id, callback);
948
977
  };
949
-
978
+
950
979
  Tp.exists = function(id) {
951
980
  return this.defaultSandbox.exists(id);
952
981
  };
953
-
982
+
954
983
  Tp.url = function(id, ext) {
955
984
  return this.defaultSandbox.url(id, ext);
956
985
  };
957
-
986
+
958
987
  function _collectFormats(spade, ret, pkg) {
959
-
988
+
960
989
  function extractFormats(formats) {
961
990
  if (formats) {
962
991
  Object.keys(formats).forEach(function(key) {
@@ -982,29 +1011,36 @@ var spade ;
982
1011
  Called by the sandbox to get a factory object for the named moduleId
983
1012
  */
984
1013
  Tp.loadFactory = function(id, callback) {
985
- var pkg, formats, ret = this._factories[id];
1014
+ var pkg, formats, format, data, ret;
986
1015
 
987
1016
  // find any formats the current package might know about. Note that for
988
- // lazy-loaders this may not be entirely up to date (since not all pkgs
1017
+ // lazy-loaders this may not be entirely up to date (since not all pkgs
989
1018
  // are registered right away)
990
1019
  pkg = this.package(id);
991
1020
  formats = pkg ? _collectFormats(this, ['js'], pkg) : ['js'];
992
-
1021
+
1022
+ data = parseIdFormats(id, formats);
1023
+ id = data.id;
1024
+ format = data.format;
1025
+ formats = data.formats;
1026
+
1027
+ ret = format ? this._factories[id+'.'+format] : this._factories[id];
1028
+
993
1029
  if (callback) {
994
1030
  if (!ret) {
995
1031
  if (this.loader && this.loader.loadFactory) {
996
1032
  this.loader.loadFactory(this, id, formats, callback);
997
1033
  } else callback(new Error('Module '+id+' not found'));
998
1034
  } else callback();
999
-
1035
+
1000
1036
  } else if (!ret && this.loader && this.loader.loadFactory) {
1001
1037
  this.loader.loadFactory(this, id, formats);
1002
- ret = this._factories[id];
1038
+ ret = format ? this._factories[id+'.'+format] : this._factories[id];
1003
1039
  }
1004
1040
 
1005
1041
  return ret ;
1006
1042
  };
1007
-
1043
+
1008
1044
  /**
1009
1045
  Called by the sandbox to determine if the named id exists on the system.
1010
1046
  The id should already be normalized. If the id is not yet registered, the
@@ -1013,13 +1049,14 @@ var spade ;
1013
1049
  Tp.factoryExists = function(id) {
1014
1050
  if (this._factories[id]) return true;
1015
1051
  if (!this.loader || !this.loader.exists) return false;
1016
-
1052
+
1017
1053
  var pkg = this.package(id),
1018
- formats = pkg ? _collectFormats(this, ['js'], pkg) : ['js'];
1019
-
1020
- return this.loader.exists(this, id, formats);
1054
+ formats = pkg ? _collectFormats(this, ['js'], pkg) : ['js'],
1055
+ data = parseIdFormats(id, formats);
1056
+
1057
+ return this.loader.exists(this, data.id, data.formats);
1021
1058
  };
1022
-
1059
+
1023
1060
  /**
1024
1061
  Returns the package info, if any, for the named module or packageId
1025
1062
  */
@@ -1028,7 +1065,7 @@ var spade ;
1028
1065
  var ret = this._factories[id];
1029
1066
  return ret ? ret.data : null;
1030
1067
  };
1031
-
1068
+
1032
1069
  /**
1033
1070
  Normalize a moduleId, expanding it if needed.
1034
1071
  */
@@ -1036,7 +1073,7 @@ var spade ;
1036
1073
  return normalize(id, contextId);
1037
1074
  };
1038
1075
 
1039
- // maps the passed ID to a potentially location specific ID. This gives
1076
+ // maps the passed ID to a potentially location specific ID. This gives
1040
1077
  // the loader a way to vary the factory function returned for a given id
1041
1078
  // per sandbox
1042
1079
  Tp.resolve = function(id, sandbox) {
@@ -1044,19 +1081,19 @@ var spade ;
1044
1081
  return this.loader.resolve(id, sandbox);
1045
1082
  } else return id;
1046
1083
  };
1047
-
1048
- // uses the loader to invoke when the app is ready. For the browser this
1084
+
1085
+ // uses the loader to invoke when the app is ready. For the browser this
1049
1086
  // is on the ready event.
1050
1087
  Tp.ready = function(callback) {
1051
1088
  switch(this.readyState) {
1052
1089
  case 'ready':
1053
1090
  callback();
1054
1091
  break;
1055
-
1092
+
1056
1093
  case 'scheduled':
1057
1094
  this._readyQueue.push(callback);
1058
1095
  break;
1059
-
1096
+
1060
1097
  default:
1061
1098
  this._readyQueue = [callback];
1062
1099
  this.readyState = 'scheduled';
@@ -1068,18 +1105,18 @@ var spade ;
1068
1105
  that.readyState = 'ready';
1069
1106
  for(var idx=0;idx<len;idx++) queue[idx]();
1070
1107
  });
1071
-
1108
+
1072
1109
  } else {
1073
1110
  throw new Error('Loader does not support activate on ready state');
1074
1111
  }
1075
1112
  }
1076
1113
  };
1077
-
1114
+
1078
1115
  // instantiate spade and also attach class for testing
1079
1116
  spade = new Spade();
1080
1117
 
1081
- // in the browser - if ENV and ARGS are not defined, just create some
1082
- // reasonable defaults. We assume that when loading strobe from the CLI
1118
+ // in the browser - if ENV and ARGS are not defined, just create some
1119
+ // reasonable defaults. We assume that when loading strobe from the CLI
1083
1120
  // these will already be setup.
1084
1121
  if (SPADE_PLATFORM.engine === 'browser') spade.globalize();
1085
1122