kojac 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +158 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +27 -0
  8. data/app/assets/javascripts/can_extensions.js +45 -0
  9. data/app/assets/javascripts/kojac.js +1230 -0
  10. data/app/assets/javascripts/kojac_canjs.js +191 -0
  11. data/app/assets/javascripts/kojac_ember.js +463 -0
  12. data/app/controllers/kojac_controller.rb +70 -0
  13. data/app/serializers/default_kojac_serializer.rb +10 -0
  14. data/diagram.odg +0 -0
  15. data/kojac.gemspec +36 -0
  16. data/lib/kojac/app_serialize.rb +29 -0
  17. data/lib/kojac/kojac_rails.rb +432 -0
  18. data/lib/kojac/ring_strong_parameters.rb +195 -0
  19. data/lib/kojac/version.rb +3 -0
  20. data/lib/kojac.rb +8 -0
  21. data/lib/tasks/kojac_tasks.rake +4 -0
  22. data/notes.txt +48 -0
  23. data/spec/.DS_Store +0 -0
  24. data/spec/can_cache_spec.js +87 -0
  25. data/spec/can_factory_spec.js +144 -0
  26. data/spec/can_model_spec.js +127 -0
  27. data/spec/demo/README.rdoc +261 -0
  28. data/spec/demo/Rakefile +7 -0
  29. data/spec/demo/app/assets/javascripts/application.js +15 -0
  30. data/spec/demo/app/assets/stylesheets/application.css +13 -0
  31. data/spec/demo/app/controllers/application_controller.rb +3 -0
  32. data/spec/demo/app/helpers/application_helper.rb +2 -0
  33. data/spec/demo/app/mailers/.gitkeep +0 -0
  34. data/spec/demo/app/models/.gitkeep +0 -0
  35. data/spec/demo/app/views/layouts/application.html.erb +14 -0
  36. data/spec/demo/config/application.rb +65 -0
  37. data/spec/demo/config/boot.rb +10 -0
  38. data/spec/demo/config/database.yml +25 -0
  39. data/spec/demo/config/environment.rb +5 -0
  40. data/spec/demo/config/environments/development.rb +37 -0
  41. data/spec/demo/config/environments/production.rb +67 -0
  42. data/spec/demo/config/environments/test.rb +37 -0
  43. data/spec/demo/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/demo/config/initializers/inflections.rb +15 -0
  45. data/spec/demo/config/initializers/mime_types.rb +5 -0
  46. data/spec/demo/config/initializers/secret_token.rb +7 -0
  47. data/spec/demo/config/initializers/session_store.rb +8 -0
  48. data/spec/demo/config/initializers/wrap_parameters.rb +14 -0
  49. data/spec/demo/config/locales/en.yml +5 -0
  50. data/spec/demo/config/routes.rb +58 -0
  51. data/spec/demo/config.ru +4 -0
  52. data/spec/demo/lib/assets/.gitkeep +0 -0
  53. data/spec/demo/log/.gitkeep +0 -0
  54. data/spec/demo/public/404.html +26 -0
  55. data/spec/demo/public/422.html +26 -0
  56. data/spec/demo/public/500.html +25 -0
  57. data/spec/demo/public/favicon.ico +0 -0
  58. data/spec/demo/script/rails +6 -0
  59. data/spec/ember_factory_spec.js +157 -0
  60. data/spec/ember_model_spec.js +179 -0
  61. data/spec/external/.DS_Store +0 -0
  62. data/spec/external/ember/.DS_Store +0 -0
  63. data/spec/external/ember/ember-1.0.0-rc.6.js +30970 -0
  64. data/spec/external/ember/handlebars-1.0.0-rc.4.js +2239 -0
  65. data/spec/external/jasmine/MIT.LICENSE +20 -0
  66. data/spec/external/jasmine/jasmine-html.js +616 -0
  67. data/spec/external/jasmine/jasmine.css +81 -0
  68. data/spec/external/jasmine/jasmine.js +2529 -0
  69. data/spec/external/jasmine.async.js +51 -0
  70. data/spec/external/jquery/jquery-1.7.2.js +9404 -0
  71. data/spec/external/jquery/jquery-1.7.2.min.js +4 -0
  72. data/spec/external/jquery/jquery-1.9.1.js +9597 -0
  73. data/spec/external/json2.js +480 -0
  74. data/spec/external/steal/steal-121115.js +2747 -0
  75. data/spec/external/steal/steal-3.2.3.js +2098 -0
  76. data/spec/external/steal/steal-3.2.3.min.js +27 -0
  77. data/spec/external/steal/steal.js +2466 -0
  78. data/spec/external/steal/steal.min.js +32 -0
  79. data/spec/external/steal/stealconfig.js +19 -0
  80. data/spec/external/underscore.js +1223 -0
  81. data/spec/external/underscore_plus.js +261 -0
  82. data/spec/external.zip +0 -0
  83. data/spec/handler_stack_spec.js +143 -0
  84. data/spec/helpers/SpecHelper.js +10 -0
  85. data/spec/kojac_caching_spec.js +105 -0
  86. data/spec/kojac_mock_spec.js +230 -0
  87. data/spec/kojac_model_spec.js +126 -0
  88. data/spec/kojac_object_spec.js +171 -0
  89. data/spec/kojac_operations_spec.js +41 -0
  90. data/spec/mockjson/order_item.js +37 -0
  91. data/spec/mockjson/order_item__49.js +15 -0
  92. data/spec/mockjson/order_item__50.js +15 -0
  93. data/spec/mockjson/order_item__51.js +15 -0
  94. data/spec/mockjson/product.js +82 -0
  95. data/spec/mockjson/product__3.js +22 -0
  96. data/spec/model_ring_spec.rb +52 -0
  97. data/spec/operation_include_spec.js +77 -0
  98. data/spec/run.html +81 -0
  99. data/spec/spec.js +2 -0
  100. data/spec/support/jasmine.yml +86 -0
  101. metadata +380 -0
@@ -0,0 +1,2466 @@
1
+ // steal is a resource loader for JavaScript. It is broken into the following parts:
2
+ //
3
+ // - Helpers - basic utility methods used internally
4
+ // - AOP - aspect oriented code helpers
5
+ // - Deferred - a minimal deferred implementation
6
+ // - Uri - methods for dealing with urls
7
+ // - Api - steal's API
8
+ // - Resource - an object that represents a resource that is loaded and run and has dependencies.
9
+ // - Type - a type systems used to load and run different types of resources
10
+ // - Packages - used to define packages
11
+ // - Extensions - makes steal pre-load a type based on an extension (ex: .coffee)
12
+ // - Mapping - configures steal to load resources in a different location
13
+ // - Startup - startup code
14
+ // - jQuery - code to make jQuery's readWait work
15
+ // - Error Handling - detect scripts failing to load
16
+ // - Has option - used to specify that one resources contains multiple other resources
17
+ // - Window Load - API for knowing when the window has loaded and all scripts have loaded
18
+ // - Interactive - Code for IE
19
+ // - Options -
20
+ (function( win, undefined ) {
21
+
22
+ // ## Helpers ##
23
+ // The following are a list of helper methods used internally to steal
24
+ var
25
+ // check that we have a document
26
+ doc = win.document,
27
+ docEl = doc && doc.documentElement,
28
+ // a jQuery-like $.each
29
+ each = function( o, cb ) {
30
+ var i, len;
31
+
32
+ // weak array detection, but we only use this internally so don't
33
+ // pass it weird stuff
34
+ if ( typeof o.length == 'number' ) {
35
+ for ( i = 0, len = o.length; i < len; i++ ) {
36
+ cb.call(o[i], i, o[i], o)
37
+ }
38
+ } else {
39
+ for ( i in o ) {
40
+ cb.call(o[i], i, o[i], o)
41
+ }
42
+ }
43
+ return o;
44
+ },
45
+ // adds the item to the array only if it doesn't currently exist
46
+ uniquePush = function(arr, item){
47
+ for(var i=0; i < arr.length; i++){
48
+ if(arr[i] == item){
49
+ return;
50
+ }
51
+ }
52
+ arr.push(item)
53
+ },
54
+ // if o is a string
55
+ isString = function( o ) {
56
+ return typeof o == "string";
57
+ },
58
+ // if o is a function
59
+ isFn = function( o ) {
60
+ return typeof o == "function";
61
+ },
62
+ // dummy function
63
+ noop = function() {},
64
+ // creates an element
65
+ createElement = function( nodeName ) {
66
+ return doc.createElement(nodeName)
67
+ },
68
+ // creates a script tag
69
+ scriptTag = function() {
70
+ var start = createElement("script");
71
+ start.type = "text/javascript";
72
+ return start;
73
+ },
74
+ // minify-able verstion of getElementsByTagName
75
+ getElementsByTagName = function( tag ) {
76
+ return doc.getElementsByTagName(tag);
77
+ },
78
+ // A function that returns the head element
79
+ // creates and caches the lookup for faster
80
+ // performance.
81
+ head = function() {
82
+ var hd = getElementsByTagName("head")[0];
83
+ if (!hd ) {
84
+ hd = createElement("head");
85
+ docEl.insertBefore(hd, docEl.firstChild);
86
+ }
87
+ // replace head so it runs fast next time.
88
+ head = function() {
89
+ return hd;
90
+ }
91
+ return hd;
92
+ },
93
+ // extends one object with another
94
+ extend = function( d, s ) {
95
+ // only extend if we have something to extend
96
+ s && each(s, function( k ) {
97
+ d[k] = s[k];
98
+ });
99
+ return d;
100
+ },
101
+ // makes an array of things, or a mapping of things
102
+ map = function( args, cb ) {
103
+ var arr = [];
104
+ each(args, function( i, str ) {
105
+ arr.push(cb ? (isString(cb) ? str[cb] : cb.call(str, str)) : str)
106
+ });
107
+ return arr;
108
+ },
109
+ // testing support for various browser behaviors
110
+ support = {
111
+ // does onerror work in script tags?
112
+ error: doc && (function() {
113
+ var script = scriptTag();
114
+ script.onerror = noop;
115
+ return isFn(script.onerror) || "onerror" in script
116
+ })(),
117
+ // If scripts support interactive ready state.
118
+ // This is tested later.
119
+ interactive: false,
120
+ // use attachEvent for event listening (IE)
121
+ attachEvent: doc && scriptTag().attachEvent
122
+ },
123
+ // a startup function that will be called when steal is ready
124
+ startup = noop,
125
+ // if oldsteal is an object
126
+ // we use it as options to configure steal
127
+ opts = typeof win.steal == "object" ? win.steal : {},
128
+ // adds a suffix to the url for cache busting
129
+ addSuffix = function( str ) {
130
+ if ( opts.suffix ) {
131
+ str = (str + '').indexOf('?') > -1 ? str + "&" + opts.suffix : str + "?" + opts.suffix;
132
+ }
133
+ return str;
134
+ },
135
+ endsInSlashRegex = /\/$/;
136
+
137
+
138
+ // ## AOP ##
139
+ // Aspect oriented programming helper methods are used to
140
+ // weave in functionality into steal's API.
141
+ // calls `before` before `f` is called.
142
+ // steal.complete = before(steal.complete, f)
143
+ // `changeArgs=true` makes before return the same args
144
+ function before(f, before, changeArgs) {
145
+ return changeArgs ?
146
+ function before_changeArgs() {
147
+ return f.apply(this, before.apply(this, arguments));
148
+ } : function before_args() {
149
+ before.apply(this, arguments);
150
+ return f.apply(this, arguments);
151
+ }
152
+ }
153
+ // returns a function that calls `after`
154
+ // after `f`
155
+ function after(f, after, changeRet) {
156
+ return changeRet ?
157
+ function after_CRet() {
158
+ return after.apply(this, [f.apply(this, arguments)].concat(map(arguments)));
159
+ } : function after_Ret() {
160
+ var ret = f.apply(this, arguments);
161
+ after.apply(this, arguments);
162
+ return ret;
163
+ }
164
+ }
165
+
166
+
167
+ // ## Deferred .63
168
+ var Deferred = function( func ) {
169
+ if (!(this instanceof Deferred)) return new Deferred();
170
+
171
+ this.doneFuncs = [];
172
+ this.failFuncs = [];
173
+ this.resultArgs = null;
174
+ this.status = "";
175
+
176
+ // check for option function: call it with this as context and as first
177
+ // parameter, as specified in jQuery api
178
+ func && func.call(this, this);
179
+ }
180
+
181
+ Deferred.when = function() {
182
+ var args = map(arguments);
183
+ if ( args.length < 2 ) {
184
+ var obj = args[0];
185
+ if ( obj && (isFn(obj.isResolved) && isFn(obj.isRejected)) ) {
186
+ return obj;
187
+ } else {
188
+ return Deferred().resolve(obj);
189
+ }
190
+ } else {
191
+
192
+ var df = Deferred(),
193
+ done = 0,
194
+ // resolve params: params of each resolve, we need to track down
195
+ // them to be able to pass them in the correct order if the master
196
+ // needs to be resolved
197
+ rp = [];
198
+
199
+ each(args, function( j, arg ) {
200
+ arg.done(function() {
201
+ rp[j] = (arguments.length < 2) ? arguments[0] : arguments;
202
+ if (++done == args.length ) {
203
+ df.resolve.apply(df, rp);
204
+ }
205
+ }).fail(function() {
206
+ df.reject(arguments);
207
+ });
208
+ });
209
+
210
+ return df;
211
+
212
+ }
213
+ }
214
+
215
+ var resolveFunc = function( type, status ) {
216
+ return function( context ) {
217
+ var args = this.resultArgs = (arguments.length > 1) ? arguments[1] : [];
218
+ return this.exec(context, this[type], args, status);
219
+ }
220
+ },
221
+ doneFunc = function( type, status ) {
222
+ return function() {
223
+ var self = this;
224
+ each(arguments, function( i, v, args ) {
225
+ if (!v ) return;
226
+ if ( v.constructor === Array ) {
227
+ args.callee.apply(self, v)
228
+ } else {
229
+ // immediately call the function if the deferred has been resolved
230
+ if ( self.status === status ) v.apply(this, self.resultArgs || []);
231
+
232
+ self[type].push(v);
233
+ }
234
+ });
235
+ return this;
236
+ }
237
+ };
238
+
239
+ extend(Deferred.prototype, {
240
+ resolveWith: resolveFunc("doneFuncs", "rs"),
241
+ rejectWith: resolveFunc("failFuncs", "rj"),
242
+ done: doneFunc("doneFuncs", "rs"),
243
+ fail: doneFunc("failFuncs", "rj"),
244
+ always: function() {
245
+ var args = map(arguments);
246
+ if ( args.length && args[0] ) this.done(args[0]).fail(args[0]);
247
+
248
+ return this;
249
+ },
250
+ then: function() {
251
+ var args = map(arguments);
252
+ // fail function(s)
253
+ if ( args.length > 1 && args[1] ) this.fail(args[1]);
254
+
255
+ // done function(s)
256
+ if ( args.length && args[0] ) this.done(args[0]);
257
+
258
+ return this;
259
+ },
260
+ isResolved: function() {
261
+ return this.status === "rs";
262
+ },
263
+ isRejected: function() {
264
+ return this.status === "rj";
265
+ },
266
+ reject: function() {
267
+ return this.rejectWith(this, arguments);
268
+ },
269
+ resolve: function() {
270
+ return this.resolveWith(this, arguments);
271
+ },
272
+ exec: function( context, dst, args, st ) {
273
+ if ( this.status !== "" ) return this;
274
+
275
+ this.status = st;
276
+
277
+ each(dst, function( i, d ) {
278
+ d.apply(context, args);
279
+ });
280
+
281
+ return this;
282
+ }
283
+ });
284
+ // ## HELPER METHODS FOR DEFERREDS
285
+ // Used to call a method on an object or resolve a
286
+ // deferred on it when a group of deferreds is resolved.
287
+ //
288
+ // whenEach(resources,"complete",resource,"execute")
289
+ var whenEach = function( arr, func, obj, func2 ) {
290
+
291
+ var deferreds = map(arr, func)
292
+ return Deferred.when.apply(Deferred, deferreds).then(function() {
293
+ if ( isFn(obj[func2]) ) {
294
+ obj[func2]()
295
+ } else {
296
+ obj[func2].resolve();
297
+ }
298
+
299
+ });
300
+ };
301
+
302
+ // ## URI ##
303
+ /**
304
+ * @class steal.URI
305
+ * A URL / URI helper for getting information from a URL.
306
+ *
307
+ * var uri = URI( "http://stealjs.com/index.html" )
308
+ * uri.path //-> "/index.html"
309
+ */
310
+ var URI = function( url ) {
311
+ if ( this.constructor !== URI ) {
312
+ return new URI(url);
313
+ }
314
+ extend(this, URI.parse("" + url));
315
+ };
316
+ // the current url (relative to root, which is relative from page)
317
+ // normalize joins from this
318
+ //
319
+ extend(URI, {
320
+ // parses a URI into it's basic parts
321
+ parse: function( string ) {
322
+ var uriParts = string.split("?"),
323
+ uri = uriParts.shift(),
324
+ queryParts = uriParts.join("").split("#"),
325
+ protoParts = uri.split("://"),
326
+ parts = {
327
+ query: queryParts.shift(),
328
+ fragment: queryParts.join("#")
329
+ },
330
+ pathParts;
331
+
332
+ if ( protoParts[1] ) {
333
+ parts.protocol = protoParts.shift();
334
+ pathParts = protoParts[0].split("/");
335
+ parts.host = pathParts.shift();
336
+ parts.path = "/" + pathParts.join("/");
337
+ } else {
338
+ parts.path = protoParts[0];
339
+ }
340
+ return parts;
341
+ }
342
+ });
343
+ /**
344
+ * @attribute page
345
+ * The location of the page as a URI.
346
+ *
347
+ * steal.URI.page.protocol //-> "http"
348
+ */
349
+ URI.page = URI(win.location && location.href);
350
+ /**
351
+ * @attribute cur
352
+ *
353
+ * The current working directory / path. Anything
354
+ * loaded relative will be loaded relative to this.
355
+ */
356
+ URI.cur = URI();
357
+
358
+ /**
359
+ * @prototype
360
+ */
361
+ extend(URI.prototype, {
362
+ dir: function() {
363
+ var parts = this.path.split("/");
364
+ parts.pop();
365
+ return URI(this.domain() + parts.join("/"))
366
+ },
367
+ filename: function() {
368
+ return this.path.split("/").pop();
369
+ },
370
+ ext: function() {
371
+ var filename = this.filename();
372
+ return~filename.indexOf(".") ? filename.split(".").pop() : "";
373
+ },
374
+ domain: function() {
375
+ return this.protocol ? this.protocol + "://" + this.host : "";
376
+ },
377
+ isCrossDomain: function( uri ) {
378
+ uri = URI(uri || win.location.href);
379
+ var domain = this.domain(),
380
+ uriDomain = uri.domain()
381
+ return (domain && uriDomain && domain != uriDomain) || this.protocol === "file" || (domain && !uriDomain);
382
+ },
383
+ isRelativeToDomain: function() {
384
+ return !this.path.indexOf("/");
385
+ },
386
+ hash: function() {
387
+ return this.fragment ? "#" + this.fragment : ""
388
+ },
389
+ search: function() {
390
+ return this.query ? "?" + this.query : ""
391
+ },
392
+ // like join, but returns a string
393
+ add: function( uri ) {
394
+ return this.join(uri) + '';
395
+ },
396
+ join: function( uri, min ) {
397
+ uri = URI(uri);
398
+ if ( uri.isCrossDomain(this) ) {
399
+ return uri;
400
+ }
401
+ if ( uri.isRelativeToDomain() ) {
402
+ return URI(this.domain() + uri)
403
+ }
404
+ // at this point we either
405
+ // - have the same domain
406
+ // - this has a domain but uri does not
407
+ // - both don't have domains
408
+ var left = this.path ? this.path.split("/") : [],
409
+ right = uri.path.split("/"),
410
+ part = right[0];
411
+ //if we are joining from a folder like cookbook/, remove the last empty part
412
+ if ( this.path.match(/\/$/) ) {
413
+ left.pop();
414
+ }
415
+ while ( part == ".." && left.length ) {
416
+ // if we've emptied out, folders, just break
417
+ // leaving any additional ../s
418
+ if (!left.pop() ) {
419
+ break;
420
+ }
421
+ right.shift();
422
+
423
+ part = right[0];
424
+ }
425
+ return extend(URI(this.domain() + left.concat(right).join("/")), {
426
+ query: uri.query
427
+ });
428
+ },
429
+ /**
430
+ * For a given path, a given working directory, and file location, update the
431
+ * path so it points to a location relative to steal's root.
432
+ *
433
+ * We want everything relative to steal's root so the same app can work in
434
+ * multiple pages.
435
+ *
436
+ * ./files/a.js = steals a.js
437
+ * ./files/a = a/a.js
438
+ * files/a = //files/a/a.js
439
+ * files/a.js = loads //files/a.js
440
+ */
441
+ normalize: function( cur ) {
442
+ cur = cur ? cur.dir() : URI.cur.dir();
443
+ var path = this.path,
444
+ res = URI(path);
445
+ //if path is rooted from steal's root (DEPRECATED)
446
+ if (!path.indexOf("//") ) {
447
+ res = URI(path.substr(2));
448
+ } else if (!path.indexOf("./") ) { // should be relative
449
+ res = cur.join(path.substr(2));
450
+ }
451
+ // only if we start with ./ or have a /foo should we join from cur
452
+ else if ( this.isRelative() ) {
453
+ res = cur.join(this.domain() + path)
454
+ }
455
+ res.query = this.query;
456
+ return res;
457
+ },
458
+ isRelative: function() {
459
+ return /^[\.|\/]/.test(this.path)
460
+ },
461
+ // a min path from 2 urls that share the same domain
462
+ pathTo: function( uri ) {
463
+ uri = URI(uri);
464
+ var uriParts = uri.path.split("/"),
465
+ thisParts = this.path.split("/"),
466
+ result = [];
467
+ while ( uriParts.length && thisParts.length && uriParts[0] == thisParts[0] ) {
468
+ uriParts.shift();
469
+ thisParts.shift();
470
+ }
471
+ each(thisParts, function() {
472
+ result.push("../")
473
+ })
474
+ return URI(result.join("") + uriParts.join("/"));
475
+ },
476
+ mapJoin: function( url ) {
477
+ return this.join(URI(url).insertMapping());
478
+ },
479
+ // helper to go from jquery to jquery/jquery.js
480
+ addJS: function() {
481
+ var ext = this.ext();
482
+ if (!ext ) {
483
+ // if first character of path is a . or /, just load this file
484
+ if (!this.isRelative() ) {
485
+ this.path += "/" + this.filename();
486
+ }
487
+ this.path += ".js"
488
+ }
489
+ return this;
490
+ }
491
+ });
492
+
493
+ // create the steal function now to use as a namespace.
494
+
495
+
496
+ function steal() {
497
+ // convert arguments into an array
498
+ var args = map(arguments);
499
+ if ( args.length ) {
500
+ pending.push.apply(pending, args);
501
+ // steal.after is called everytime steal is called
502
+ // it kicks off loading these files
503
+ steal.after(args);
504
+ // return steal for chaining
505
+ }
506
+ return steal;
507
+ };
508
+ steal._id = Math.floor(1000 * Math.random());
509
+ // ## CONFIG ##
510
+
511
+ // stores the current config settings
512
+ var stealConfig = {
513
+ types: {},
514
+ ext: {},
515
+ env: "development",
516
+ loadProduction: true,
517
+ logLevel: 0
518
+ },
519
+
520
+ matchesId = function( loc, id ) {
521
+ if ( loc === "*" ) {
522
+ return true;
523
+ } else if ( id.indexOf(loc) === 0 ) {
524
+ return true;
525
+ }
526
+ };
527
+ /**
528
+ * `steal.config(config)` configures steal. Typically it it used
529
+ * in __stealconfig.js__. The available options are:
530
+ *
531
+ * - map - map an id to another id
532
+ * - paths - maps an id to a file
533
+ * - root - the path to the "root" folder
534
+ * - env - `"development"` or `"production"`
535
+ * - types - processor rules for various types
536
+ * - ext - behavior rules for extensions
537
+ * - urlArgs - extra queryString arguments
538
+ * - startFile - the file to load
539
+ *
540
+ * ## map
541
+ *
542
+ * Maps an id to another id with a certain scope of other ids. This can be
543
+ * used to use different modules within the same id or map ids to another id.
544
+ * Example:
545
+ *
546
+ * steal.config({
547
+ * map: {
548
+ * "*": {
549
+ * "jquery/jquery.js": "jquery"
550
+ * },
551
+ * "compontent1":{
552
+ * "underscore" : "underscore1.2"
553
+ * },
554
+ * "component2":{
555
+ * "underscore" : "underscore1.1"
556
+ * }
557
+ * }
558
+ * })
559
+ *
560
+ * ## paths
561
+ *
562
+ * Maps an id or matching ids to a url. Each mapping is specified
563
+ * by an id or part of the id to match and what that
564
+ * part should be replaced with.
565
+ *
566
+ * steal.config({
567
+ * paths: {
568
+ * // maps everything in a jquery folder like: `jquery/controller`
569
+ * // to http://cdn.com/jquery/controller/controller.com
570
+ * "jquery/" : "http://cdn.com/jquery/"
571
+ *
572
+ * // if path does not end with /, it matches only that id
573
+ * "jquery" : "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"
574
+ * }
575
+ * })
576
+ *
577
+ * ## root
578
+ * ## env
579
+ *
580
+ * If production, does not load "ignored" scripts and loads production script. If development gives more warnings / errors.
581
+ *
582
+ * ## types
583
+ *
584
+ * The types option can specify how a type is loaded.
585
+ *
586
+ * ## ext
587
+ *
588
+ * The ext option specifies the default behavior if file is loaded with the
589
+ * specified extension. For a given extension, a file that configures the type can be given or
590
+ * an existing type. For example, for ejs:
591
+ *
592
+ * steal.config({ext: {"ejs": "can/view/ejs/ejs.js"}})
593
+ *
594
+ * This tells steal to make sure `can/view/ejs/ejs.js` is executed before any file with
595
+ * ".ejs" is executed.
596
+ *
597
+ * ## startFile
598
+ */
599
+ steal.config = function( config ) {
600
+ if(!config){ // called as a getter, so just return
601
+ return stealConfig;
602
+ }
603
+ if(arguments.length === 1 && typeof config === "string"){ // called as a getter, so just return
604
+ return stealConfig[config];
605
+ }
606
+ for(var prop in config){
607
+ var value = config[prop]
608
+ // if it's a special function
609
+ steal.config[prop] ?
610
+ // run it
611
+ steal.config[prop](value) :
612
+ // otherwise set or extend
613
+ (typeof value == "object" && stealConfig[prop] ?
614
+ // extend
615
+ extend( stealConfig[prop], value) :
616
+ // set
617
+ stealConfig[prop] = value);
618
+
619
+ }
620
+ // redo all resources
621
+ each(resources, function( id, resource ) {
622
+ if ( resource.options.type != "fn" ) {
623
+ // TODO this is terrible
624
+ var buildType = resource.options.buildType;
625
+ resource.setOptions(resource.orig);
626
+ var newId = resource.options.id;
627
+ // this mapping is to move a config'd key
628
+ if ( id !== newId ) {
629
+ resources[newId] = resource;
630
+ // TODO: remove the old one ....
631
+ }
632
+ resource.options.buildType = buildType;
633
+ }
634
+ })
635
+ return stealConfig;
636
+ };
637
+ steal.config.startFile = function(startFile){
638
+ // make sure startFile and production look right
639
+ stealConfig.startFile = "" + URI(startFile).addJS()
640
+ if (!stealConfig.production ) {
641
+ stealConfig.production = URI(stealConfig.startFile).dir() + "/production.js";
642
+ }
643
+
644
+ }
645
+ /**
646
+ * Read or define the path relative URI's should be referenced from.
647
+ *
648
+ * window.location //-> "http://foo.com/site/index.html"
649
+ * steal.URI.root("http://foo.com/app/files/")
650
+ * steal.root.toString() //-> "../../app/files/"
651
+ */
652
+ steal.config.root = function( relativeURI ) {
653
+ if ( relativeURI !== undefined ) {
654
+ var root = URI(relativeURI);
655
+
656
+ // the current folder-location of the page http://foo.com/bar/card
657
+ var cleaned = URI.page,
658
+ // the absolute location or root
659
+ loc = cleaned.join(relativeURI);
660
+
661
+ // cur now points to the 'root' location, but from the page
662
+ URI.cur = loc.pathTo(cleaned)
663
+ stealConfig.root = root;
664
+ return;
665
+ }
666
+ stealConfig.root = root || URI("");
667
+ }
668
+ steal.config.root("");
669
+ /**
670
+ * @function steal.id
671
+ *
672
+ * Given a resource id passed to `steal( resourceID, currentWorkingId )`, this function converts it to the
673
+ * final, unique id. This function can be overwritten
674
+ * to change how unique ids are defined, for example, to be more AMD-like.
675
+ *
676
+ * The following are the default rules.
677
+ *
678
+ * Given an ID:
679
+ *
680
+ * 1. Check the id has an extension like _.js_ or _.customext_. If it doesn't:
681
+ * 1. Check if the id is relative, meaning it starts with _../_ or _./_. If it is not, add
682
+ * "/" plus everything after the last "/". So `foo/bar` becomes `foo/bar/bar`
683
+ * 2. Add .js to the id.
684
+ * 2. Check if the id is relative, meaning it starts with _../_ or _./_. If it is relative,
685
+ * set the id to the id joined from the currentWorkingId.
686
+ * 3. Check the
687
+ *
688
+ *
689
+ * `steal.id()`
690
+ */
691
+ // returns the "rootSrc" id, something that looks like requireJS
692
+ // for a given id/path, what is the "REAL" id that should be used
693
+ // this is where substituation can happen
694
+ steal.id = function( id, currentWorkingId, type ) {
695
+ // id should be like
696
+ var uri = URI(id);
697
+ uri = uri.addJS().normalize(currentWorkingId ? new URI(currentWorkingId) : null)
698
+ // check foo/bar
699
+ if (!type ) {
700
+ type = "js"
701
+ }
702
+ if ( type == "js" ) {
703
+ // if it ends with .js remove it ...
704
+ // if it ends
705
+ }
706
+ // check map config
707
+ var map = stealConfig.map || {};
708
+ // always run past
709
+ each(map, function( loc, maps ) {
710
+ // is the current working id matching loc
711
+ if ( matchesId(loc, currentWorkingId) ) {
712
+ // run maps
713
+ each(maps, function( part, replaceWith ) {
714
+ if (("" + uri).indexOf(part) == 0 ) {
715
+ uri = URI(("" + uri).replace(part, replaceWith))
716
+ }
717
+ })
718
+ }
719
+ })
720
+
721
+ return uri;
722
+ }
723
+
724
+ steal.amdToId = function(id, currentWorkingId, type){
725
+ var uri = URI(id);
726
+ uri = uri.normalize(currentWorkingId ? new URI(currentWorkingId) : null)
727
+ // check foo/bar
728
+ if (!type ) {
729
+ type = "js"
730
+ }
731
+ if ( type == "js" ) {
732
+ // if it ends with .js remove it ...
733
+ // if it ends
734
+ }
735
+ // check map config
736
+ var map = stealConfig.map || {};
737
+ // always run past
738
+ each(map, function( loc, maps ) {
739
+ // is the current working id matching loc
740
+ if ( matchesId(loc, currentWorkingId) ) {
741
+ // run maps
742
+ each(maps, function( part, replaceWith ) {
743
+ if (("" + uri).indexOf(part) == 0 ) {
744
+ uri = URI(("" + uri).replace(part, replaceWith))
745
+ }
746
+ })
747
+ }
748
+ })
749
+ return uri;
750
+ }
751
+
752
+
753
+ steal.config.shim = function(shims){
754
+
755
+ for(var id in shims){
756
+ var resource = Resource.make(id);
757
+ if(typeof shims[id] === "object"){
758
+ var needs = shims[id].deps || []
759
+ if(typeof shims[id].exports === "string"){
760
+ var exports = (function(_exports){
761
+ return function(){
762
+ return win[_exports];
763
+ }
764
+ })(shims[id].exports)
765
+ } else {
766
+ exports = shims[id].exports;
767
+ }
768
+ } else {
769
+ needs = shims[id];
770
+ }
771
+ (function(_resource, _needs){
772
+ _resource.options.needs = _needs;
773
+ })(resource, needs)
774
+
775
+ if(exports){
776
+ resource.exports = (function(_resource, _exports, _needs){
777
+ return function(){
778
+ var args = _needs.map(function(id){
779
+ return Resource.make(id).value;
780
+ })
781
+ _resource.value = _exports.apply(null, args)
782
+ return _resource.value
783
+ }
784
+ })(resource, exports, needs)
785
+ }
786
+ }
787
+
788
+ }
789
+
790
+ // for a given ID, where should I find this resource
791
+ /**
792
+ * `steal.idToUri( id, noJoin )` takes an id and returns a URI that
793
+ * is the location of the file. It uses the paths option of [steal.config].
794
+ * Passing true for `noJoin` does not join from the root URI.
795
+ */
796
+ steal.idToUri = function( id, noJoin ) {
797
+ // this is normalize
798
+ var paths = stealConfig.paths || {},
799
+ path;
800
+ // always run past
801
+ each(paths, function( part, replaceWith ) {
802
+ path = ""+id;
803
+ // if path ends in / only check first part of id
804
+ if((endsInSlashRegex.test(part) && path.indexOf(part) == 0) ||
805
+ // or check if its a full match only
806
+ path === part){
807
+ id = URI(path.replace(part, replaceWith));
808
+ }
809
+ })
810
+
811
+ return noJoin ? id : stealConfig.root.join(id)
812
+ }
813
+ steal.amdIdToUri = function( id, noJoin ){
814
+ // this is normalize
815
+ var paths = stealConfig.paths || {},
816
+ path;
817
+ // always run past
818
+ each(paths, function( part, replaceWith ) {
819
+ path = ""+id;
820
+ // if path ends in / only check first part of id
821
+ if((endsInSlashRegex.test(part) && path.indexOf(part) == 0) ||
822
+ // or check if its a full match only
823
+ path === part){
824
+ id = URI(path.replace(part, replaceWith));
825
+ }
826
+ })
827
+ if( /(^|\/)[^\/\.]+$/.test(id) ){
828
+ id= URI(id+".js")
829
+ }
830
+ return id //noJoin ? id : stealConfig.root.join(id)
831
+ }
832
+
833
+
834
+
835
+ // This can't be added to the prototype using extend because
836
+ // then for some reason IE < 9 won't recognize it.
837
+ URI.prototype.toString = function() {
838
+ return this.domain() + this.path + this.search() + this.hash();
839
+ };
840
+
841
+ // temp add steal.File for backward compat
842
+ steal.File = steal.URI = URI;
843
+ // --- END URI
844
+ var pending = [],
845
+ s = steal,
846
+ id = 0;
847
+
848
+
849
+ /**
850
+ * @add steal
851
+ */
852
+ // =============================== STATIC API ===============================
853
+ var page;
854
+
855
+ extend(steal, {
856
+ each: each,
857
+ extend: extend,
858
+ Deferred: Deferred,
859
+ // Currently used a few places
860
+ isRhino: win.load && win.readUrl && win.readFile,
861
+ /**
862
+ * @hide
863
+ * Makes options
864
+ * @param {Object} options
865
+ */
866
+ makeOptions: function( options, curId ) {
867
+ // convert it to a uri
868
+ if (!options.id ) {
869
+ throw {
870
+ message: "no id",
871
+ options: options
872
+ }
873
+ }
874
+ options.id = options.toId ? options.toId(options.id, curId) : steal.id(options.id, curId);
875
+ // set the ext
876
+ options.ext = options.id.ext();
877
+
878
+ // Check if it's a configured needs
879
+ var configedExt = stealConfig.ext[options.ext];
880
+ // if we have something, but it's not a type
881
+ if ( configedExt && ! stealConfig.types[configedExt] ) {
882
+ if (!options.needs ) {
883
+ options.needs = [];
884
+ }
885
+
886
+ options.needs.push(configedExt);
887
+ }
888
+
889
+ return options;
890
+ },
891
+ /**
892
+ * Calls steal, but waits until all previous steals
893
+ * have completed loading until loading the
894
+ * files passed to the arguments.
895
+ */
896
+ then: function() {
897
+ var args = map(arguments);
898
+ args.unshift(null)
899
+ return steal.apply(win, args);
900
+ },
901
+ /**
902
+ * `steal.bind( event, handler(eventData...) )` listens to
903
+ * events on steal. Typically these are used by various build processes
904
+ * to know when steal starts and finish loading resources and their
905
+ * dependencies. Listen to an event like:
906
+ *
907
+ * steal.bind('end', function(rootResource){
908
+ * rootResource.dependencies // the first stolen resources.
909
+ * })
910
+ *
911
+ * Steal supports the following events:
912
+ *
913
+ * - __start__ - steal has started loading a group of resources and their dependencies.
914
+ * - __end__ - steal has finished loading a group of resources and their dependencies.
915
+ * - __done__ - steal has finished loading the first set of resources and their dependencies.
916
+ * - __ready__ - after both steal's "done" event and the `window`'s onload event have fired.
917
+ *
918
+ * For example, the following html:
919
+ *
920
+ * @codestart html
921
+ * &lt;script src='steal/steal.js'>&lt;/script>
922
+ * &lt;script>
923
+ * steal('can/control', function(){
924
+ * setTimeout(function(){
925
+ * steal('can/model')
926
+ * },200)
927
+ * })
928
+ * &lt;/script>
929
+ * @codeend
930
+ *
931
+ * Would fire:
932
+ *
933
+ * - __start__ - immediately after `steal('can/control')` is called
934
+ * - __end__ - after 'can/control', all of it's dependencies, and the callback function have executed and completed.
935
+ * - __done__ - fired after the first 'end' event.
936
+ * - __ready__ - fired after window.onload and the 'done' event
937
+ * - __start__ - immediately after `steal('can/model')` is called
938
+ * - __end__ - fired after 'can/model' and all of it's dependencies have fired.
939
+ *
940
+ *
941
+ *
942
+ * @param {String} event
943
+ * @param {Function} listener
944
+ */
945
+ bind: function( event, listener ) {
946
+ if (!events[event] ) {
947
+ events[event] = []
948
+ }
949
+ var special = steal.events[event]
950
+ if ( special && special.add ) {
951
+ listener = special.add(listener);
952
+ }
953
+ listener && events[event].push(listener);
954
+ return steal;
955
+ },
956
+ /**
957
+ * `steal.one(eventName, handler(eventArgs...) )` works just like
958
+ * [steal.bind] but immediately unbinds after `handler` is called.
959
+ */
960
+ one: function( event, listener ) {
961
+ return steal.bind(event, function() {
962
+ listener.apply(this, arguments);
963
+ steal.unbind(event, arguments.callee);
964
+ });
965
+ },
966
+ events: {},
967
+ /**
968
+ * `steal.unbind( eventName, handler )` removes an event listener on steal.
969
+ * @param {String} event
970
+ * @param {Function} listener
971
+ */
972
+ unbind: function( event, listener ) {
973
+ var evs = events[event] || [],
974
+ i = 0;
975
+ while ( i < evs.length ) {
976
+ if ( listener === evs[i] ) {
977
+ evs.splice(i, 1);
978
+ } else {
979
+ i++;
980
+ }
981
+ }
982
+ },
983
+ trigger: function( event, arg ) {
984
+ var arr = events[event] || [];
985
+ // array items might be removed during each iteration (with unbind),
986
+ // so we iterate over a copy
987
+ each(map(arr), function( i, f ) {
988
+ f(arg);
989
+ })
990
+ },
991
+ /**
992
+ * @hide
993
+ * Creates resources and marks them as loading so steal doesn't try
994
+ * to load them.
995
+ *
996
+ * steal.has("foo/bar.js","zed/car.js");
997
+ *
998
+ * This is used when a file has other resources in it.
999
+ */
1000
+ has: function() {
1001
+ // we don't use IE's interactive script functionality while
1002
+ // production scripts are loading
1003
+ support.interactive = false;
1004
+ each(arguments, function( i, arg ) {
1005
+ var stel = Resource.make(arg);
1006
+ stel.loading = stel.executing = true;
1007
+ });
1008
+ },
1009
+
1010
+ // a dummy function to add things to after the stel is created, but before executed is called
1011
+ preexecuted: function() {},
1012
+ /**
1013
+ * @hide
1014
+ * Signals that a resource's JS code has been run. This is used
1015
+ * when a file has other resources in it.
1016
+ *
1017
+ * steal.has("foo/bar.js");
1018
+ *
1019
+ * //start code for foo/bar.js
1020
+ * steal("zed/car.js", function(){ ... });
1021
+ * steal.executed("foo/bar.js");
1022
+ *
1023
+ * When a resource is executed, its dependent resources are loaded and eventually
1024
+ * executed.
1025
+ */
1026
+ // called when a script has loaded via production
1027
+ executed: function( name ) {
1028
+ // create the steal, mark it as loading, then as loaded
1029
+ var resource = Resource.make(name);
1030
+ resource.loading = resource.executing = true;
1031
+ //convert(stel, "complete");
1032
+ steal.preexecuted(resource);
1033
+ resource.executed()
1034
+ return steal;
1035
+ },
1036
+ type: function( type, cb ) {
1037
+ var typs = type.split(" ");
1038
+
1039
+ if (!cb ) {
1040
+ return types[typs.shift()].require
1041
+ }
1042
+
1043
+ types[typs.shift()] = {
1044
+ require: cb,
1045
+ convert: typs
1046
+ };
1047
+ }
1048
+ });
1049
+
1050
+
1051
+ // ============ RESOURCE ================
1052
+ // a map of resources by resourceID
1053
+ var resources = {};
1054
+ // this is for methods on a 'steal instance'. A file can be in one of a few states:
1055
+ // created - the steal instance is created, but we haven't started loading it yet
1056
+ // this happens when thens are used
1057
+ // loading - (loading=true) By calling load, this will tell steal to load a file
1058
+ // loaded - (isLoaded=true) The file has been run, but its dependency files have been completed
1059
+ // complete - all of this files dependencies have loaded and completed.
1060
+
1061
+ // A Resource is almost anything. It is different from a module
1062
+ // as it doesn't represent some unit of functionality, rather
1063
+ // it represents a unit that can have other units "within" it
1064
+ // as dependencies. A resource can:
1065
+ //
1066
+ // - load - load the resource to the client so it is available, but don't run it yet
1067
+ // - run - run the code for the resource
1068
+ // - executed - the code has been run for the resource, but all
1069
+ // dependencies for that resource might not have finished
1070
+ // - completed - all resources within the resource have completed
1071
+ //
1072
+ // __options__
1073
+ // `options` can be a string, function, or object.
1074
+ //
1075
+ // __properties__
1076
+ //
1077
+ // - options - has a number of properties
1078
+ // - src - a URI to this resource that can be loaded from the current page
1079
+ // - rootSrc - a URI to this resource relative to the current root URI.
1080
+ // - type - the type of resource: "fn", "js", "css", etc
1081
+ // - needs - other resources that must be loaded prior to this resource
1082
+ // - fn - a callback function to run when executed
1083
+ // - unique - false if this resource should be loaded each time
1084
+ // - waits - this resource should wait until all prior scripts have completed before running
1085
+ // - loaded - a deferred indicating if this resource has been loaded to the client
1086
+ // - run - a deferred indicating if the the code for this resource run
1087
+ // - completed - a deferred indicating if all of this resources dependencies have
1088
+ // completed
1089
+ // - dependencies - an array of dependencies
1090
+ var Resource = function( options ) {
1091
+ // an array for dependencies, this is the steal calls this resource makes
1092
+ this.dependencies = [];
1093
+
1094
+ // an array of implicit dependencies this steal needs
1095
+ this.needsDependencies = [];
1096
+
1097
+ // id for debugging
1098
+ this.id = (++id);
1099
+ // the original options
1100
+ this.orig = options;
1101
+ // the parent steal's id
1102
+ this.curId = steal.cur && steal.cur.options.id;
1103
+
1104
+ this.setOptions(options);
1105
+ // create the deferreds used to manage state
1106
+ this.loaded = Deferred();
1107
+ this.run = Deferred();
1108
+ this.completed = Deferred();
1109
+ };
1110
+ // `Resource.make` is used to either create
1111
+ // a new resource, or return an existing
1112
+ // resource that matches the options.
1113
+ Resource.make = function( options ) {
1114
+ // create the temporary reasource
1115
+ var resource = new Resource(options),
1116
+ // use `rootSrc` as the definitive ID
1117
+ id = resource.options.id;
1118
+
1119
+ // assuming this resource should not be created again.
1120
+ if ( resource.unique && id ) {
1121
+
1122
+ // Check if we already have a resource for this rootSrc
1123
+ // Also check with a .js ending because we defer 'type'
1124
+ // determination until later
1125
+ if (!resources[id] && !resources[id + ".js"] ) {
1126
+ // If we haven't loaded, cache the resource
1127
+ resources[id] = resource;
1128
+ } else {
1129
+
1130
+ // Otherwise get the cached resource
1131
+ existingResource = resources[id];
1132
+ // If options were passed, copy new properties over.
1133
+ // Don't copy src, etc because those have already
1134
+ // been changed to be the right values;
1135
+ if (!isString(options) ) {
1136
+ // extend everything other than id
1137
+ for(var prop in options){
1138
+ if(prop !== "id") {
1139
+ existingResource.options[prop] = options[prop];
1140
+ }
1141
+ }
1142
+ }
1143
+ return existingResource;
1144
+ }
1145
+ }
1146
+
1147
+ return resource;
1148
+ };
1149
+
1150
+ // updates the paths of things ...
1151
+ // use modules b/c they are more fuzzy
1152
+ // a module's id stays the same, but a path might change
1153
+ //
1154
+ Resource.update = function() {
1155
+ for ( var rootSrc in resources ) {
1156
+ if (!resources[resources].loaded.isResolved() ) {
1157
+
1158
+ }
1159
+ }
1160
+ };
1161
+
1162
+ extend(Resource.prototype, {
1163
+ setOptions: function( options ) {
1164
+ var prevOptions = this.options;
1165
+ // if we have no options, we are the global Resource that
1166
+ // contains all other resources.
1167
+ if (!options ) { //global init cur ...
1168
+ this.options = {};
1169
+ this.waits = false;
1170
+ }
1171
+ //handle callback functions
1172
+ else if ( isFn(options) ) {
1173
+ var uri = URI.cur,
1174
+ self = this,
1175
+ cur = steal.cur;
1176
+ this.options = {
1177
+ fn: function() {
1178
+
1179
+ // Set the URI if there are steals
1180
+ // within the callback.
1181
+ URI.cur = uri;
1182
+
1183
+ // we should get the current "module"
1184
+ // check it's listed dependencies and see
1185
+ // if they have a value
1186
+ var args = [],
1187
+ found = false,
1188
+ dep, value;
1189
+ // iterate backwards through dependencies
1190
+ for ( var i = cur.dependencies.length; i >= 0; i-- ) {
1191
+ dep = cur.dependencies[i];
1192
+
1193
+ if ( found ) {
1194
+ if ( dep === null ) {
1195
+ // //alert("YES")
1196
+ break;
1197
+ }
1198
+ // We need to access the stored modules in this order
1199
+ // - calculated id
1200
+ // - original name
1201
+ // - dependency return value otherwise
1202
+ value = modules[dep.options.id] || modules[dep.orig] || dep.value;
1203
+ args.unshift(value);
1204
+
1205
+ // what does this do?
1206
+
1207
+ }
1208
+
1209
+ if ( dep === self ) {
1210
+ found = true;
1211
+ }
1212
+ }
1213
+
1214
+
1215
+
1216
+ var ret = options.apply(cur, args);
1217
+
1218
+ // if this returns a value, we should register it as a module ...
1219
+ if ( ret ) {
1220
+ // register this module ....
1221
+ cur.value = ret;
1222
+ }
1223
+ return ret;
1224
+ },
1225
+ id: uri,
1226
+ type: "fn"
1227
+ }
1228
+ // this has nothing to do with 'loading' options
1229
+ this.waits = true;
1230
+ this.unique = false;
1231
+ } else {
1232
+ // save the original options
1233
+ this.options = steal.makeOptions(extend({}, isString(options) ? {
1234
+ id: options
1235
+ } : options), this.curId);
1236
+
1237
+ this.waits = this.options.waits || false;
1238
+ this.unique = true;
1239
+ }
1240
+ // if there are other options we haven't already set, reuse the old ones
1241
+ for(opt in prevOptions){
1242
+ if(!this.options[opt]){
1243
+ this.options[opt] = prevOptions[opt];
1244
+ }
1245
+ }
1246
+ },
1247
+
1248
+ // Calling complete indicates that all dependencies have
1249
+ // been completed for this resource
1250
+ complete: function() {
1251
+ this.completed.resolve();
1252
+ },
1253
+ // After the script has been loaded and run
1254
+ // - checks what has been stolen (in pending)
1255
+ // - wires up pendings steal's deferreds to eventually complete this
1256
+ // - this is where all of steal's complexity is
1257
+ executed: function( script ) {
1258
+ var myqueue,
1259
+ stel,
1260
+ src = this.options.src,
1261
+ rootSrc = this.options.rootSrc;
1262
+ // Set this as the current file so any relative urls
1263
+ // will load from it.
1264
+ // rootSrc needs to be the translated path
1265
+ // we need id vs rootSrc ...
1266
+
1267
+ if ( this.options.id ) {
1268
+ URI.cur = URI(this.options.id);
1269
+ }
1270
+ if( this.exports ){
1271
+ this.exports()
1272
+ }
1273
+ // set this as the current resource
1274
+ steal.cur = this;
1275
+
1276
+ // mark yourself as 'loaded'.
1277
+ this.run.resolve();
1278
+
1279
+ // If we are IE, get the queue from interactives.
1280
+ // It in interactives because you can't use onload to know
1281
+ // which script is executing.
1282
+ if ( support.interactive && src ) {
1283
+ myqueue = interactives[src];
1284
+ }
1285
+ // In other browsers, the queue of items to load is
1286
+ // what is in pending
1287
+ if (!myqueue ) {
1288
+ myqueue = pending.slice(0);
1289
+ pending = [];
1290
+ }
1291
+
1292
+ // if we have nothing, mark us as complete
1293
+ if (!myqueue.length ) {
1294
+ this.complete();
1295
+ return;
1296
+ }
1297
+ //print("-setting up "+this.options.id)
1298
+ // now we have to figure out how to wire up our pending steals
1299
+ var self = this,
1300
+ // the current
1301
+ isProduction = stealConfig.env == "production",
1302
+
1303
+ stealInstances = [];
1304
+
1305
+ // iterate through the collection and add all the 'needs'
1306
+ // before fetching...
1307
+ each(myqueue, function( i, item ) {
1308
+ if( item === null){
1309
+ stealInstances.push(null);
1310
+ return
1311
+ }
1312
+
1313
+ if ( (isProduction && item.ignore) || (!isProduction && !steal.isRhino && item.prodonly)) {
1314
+ return;
1315
+ }
1316
+
1317
+ // make a steal object
1318
+ var stel = Resource.make(item);
1319
+ if ( packHash[stel.options.id] && stel.options.type !== 'fn' ) { // if we are production, and this is a package, mark as loading, but steal package?
1320
+ steal.has(""+stel.options.id);
1321
+ stel = Resource.make(packHash[""+stel.options.id]);
1322
+ }
1323
+ // has to happen before 'needs' for when reversed...
1324
+ stealInstances.push(stel);
1325
+ });
1326
+ //print("-instances "+this.options.id)
1327
+ // The set of resources before the previous "wait" resource
1328
+ var priorSet = [],
1329
+ // The current set of resources after and including the
1330
+ // previous "wait" resource
1331
+ set = [],
1332
+ // The first set of resources that we will execute
1333
+ // right away. This should be the first set of dependencies
1334
+ // that we can load in parallel. If something has
1335
+ // a need, the need should be in this set
1336
+ firstSet = [],
1337
+ // Should we be adding resources to the
1338
+ // firstSet
1339
+ setFirstSet = true;
1340
+
1341
+ // Goes through each resource and maintains
1342
+ // a list of the set of resources
1343
+ // that must be complete before the current
1344
+ // resource (`priorSet`).
1345
+ each( stealInstances, function( i, resource ) {
1346
+ // add it as a dependency, circular are not allowed
1347
+ self.dependencies.push(resource);
1348
+
1349
+ // if there's a wait and it's not the first thing
1350
+ if ( (resource === null || resource.waits) && set.length ) {
1351
+ // add the current set to `priorSet`
1352
+ priorSet = priorSet.concat(set);
1353
+ // empty the current set
1354
+ set = [];
1355
+ // we have our firs set of items
1356
+ setFirstSet = false;
1357
+ if(resource === null) {
1358
+ return;
1359
+ }
1360
+
1361
+ }
1362
+ if ( resource === null ) return;
1363
+
1364
+ // when the priorSet is completed, execute this resource
1365
+ // and when it's needs are done
1366
+ var waitsOn = priorSet.slice(0);
1367
+ // if there are needs, this can not be part of the "firstSet"
1368
+ each(resource.options.needs || [], function( i, raw ) {
1369
+
1370
+ var need = Resource.make(raw);
1371
+ // add the need to the resource's dependencies
1372
+ uniquePush(resource.needsDependencies, need);
1373
+ waitsOn.push(need);
1374
+ // add needs to first set to execute
1375
+ firstSet.push(need)
1376
+ });
1377
+ waitsOn.length && whenEach(waitsOn, "completed", resource, "execute");
1378
+
1379
+ // what is this used for?
1380
+ resource.waitedOn = resource.waitedOn ? resource.waitedOn.concat(priorSet) : priorSet.slice(0);
1381
+
1382
+ // add this steal to the current set
1383
+ set.push(resource);
1384
+ // if we are still on the first set, and this has no needs
1385
+ if ( setFirstSet && (resource.options.needs || []).length == 0) {
1386
+ // add this to the first set of things
1387
+ firstSet.push(resource)
1388
+ }
1389
+ // start loading the resource if possible
1390
+ resource.load();
1391
+ });
1392
+
1393
+ // when every thing is complete, mark us as completed
1394
+ priorSet = priorSet.concat(set);
1395
+ whenEach(priorSet, "completed", self, "completed");
1396
+
1397
+ // execute the first set of dependencies
1398
+ each(firstSet, function( i, f ) {
1399
+ f.execute();
1400
+ });
1401
+
1402
+ },
1403
+ /**
1404
+ * Loads this steal
1405
+ */
1406
+ load: function( returnScript ) {
1407
+ // if we are already loading / loaded
1408
+ if ( this.loading || this.loaded.isResolved() ) {
1409
+ return;
1410
+ }
1411
+
1412
+ this.loading = true;
1413
+ this.loaded.resolve();
1414
+ },
1415
+ execute: function() {
1416
+ var self = this;
1417
+ if (!self.loaded.isResolved() ) {
1418
+ self.loaded.resolve();
1419
+ }
1420
+ if (!self.executing ) {
1421
+ self.executing = true;
1422
+
1423
+ steal.require(self.options, function( value ) {
1424
+
1425
+ self.executed( value );
1426
+ }, function( error, src ) {
1427
+ var abortFlag = self.options.abort,
1428
+ errorCb = self.options.error;
1429
+
1430
+ // if an error callback was provided, fire it
1431
+ if ( errorCb ) {
1432
+ errorCb.call(self.options);
1433
+ }
1434
+
1435
+ win.clearTimeout && win.clearTimeout(self.completeTimeout)
1436
+
1437
+ // if abort: false, register the script as loaded, and don't throw
1438
+ if ( abortFlag === false ) {
1439
+ self.executed();
1440
+ return;
1441
+ }
1442
+ throw "steal.js : " + self.options.src + " not completed"
1443
+ });
1444
+ }
1445
+ }
1446
+
1447
+ });
1448
+
1449
+
1450
+ var events = {};
1451
+
1452
+
1453
+
1454
+ // ### TYPES ##
1455
+ var types = stealConfig.types;
1456
+ /**
1457
+ * Registers a type. You define the type of the file, the basic type it
1458
+ * converts to, and a conversion function where you convert the original file
1459
+ * to JS or CSS. This is modeled after the
1460
+ * [http://api.jquery.com/extending-ajax/#Converters AJAX converters] in jQuery.
1461
+ *
1462
+ * Types are designed to make it simple to switch between steal's development
1463
+ * and production modes. In development mode, the types are converted
1464
+ * in the browser to allow devs to see changes as they work. When the app is
1465
+ * built, these converter functions are run by the build process,
1466
+ * and the processed text is inserted into the production script, optimized for
1467
+ * performance.
1468
+ *
1469
+ * Here's an example converting files of type .foo to JavaScript. Foo is a
1470
+ * fake language that saves global variables defined like. A .foo file might
1471
+ * look like this:
1472
+ *
1473
+ * REQUIRED FOO
1474
+ *
1475
+ * To define this type, you'd call steal.type like this:
1476
+ *
1477
+ * steal.type("foo js", function(options, original, success, error){
1478
+ * var parts = options.text.split(" ")
1479
+ * options.text = parts[0]+"='"+parts[1]+"'";
1480
+ * success();
1481
+ * });
1482
+ *
1483
+ * The method we provide is called with the text of .foo files in options.text.
1484
+ * We parse the file, create JavaScript and put it in options.text. Couldn't
1485
+ * be simpler.
1486
+ *
1487
+ * Here's an example,
1488
+ * converting [http://jashkenas.github.com/coffee-script/ coffeescript]
1489
+ * to JavaScript:
1490
+ *
1491
+ * steal.type("coffee js", function(options, original, success, error){
1492
+ * options.text = CoffeeScript.compile(options.text);
1493
+ * success();
1494
+ * });
1495
+ *
1496
+ * In this example, any time steal encounters a file with extension .coffee,
1497
+ * it will call the given converter method. CoffeeScript.compile takes the
1498
+ * text of the file, converts it from coffeescript to javascript, and saves
1499
+ * the JavaScript text in options.text.
1500
+ *
1501
+ * Similarly, languages on top of CSS, like [http://lesscss.org/ LESS], can
1502
+ * be converted to CSS:
1503
+ *
1504
+ * steal.type("less css", function(options, original, success, error){
1505
+ * new (less.Parser)({
1506
+ * optimization: less.optimization,
1507
+ * paths: []
1508
+ * }).parse(options.text, function (e, root) {
1509
+ * options.text = root.toCSS();
1510
+ * success();
1511
+ * });
1512
+ * });
1513
+ *
1514
+ * This simple type system could be used to convert any file type to be used
1515
+ * in your JavaScript app. For example, [http://fdik.org/yml/ yml] could be
1516
+ * used for configuration. jQueryMX uses steal.type to support JS templates,
1517
+ * such as EJS, TMPL, and others.
1518
+ *
1519
+ * @param {String} type A string that defines the new type being defined and
1520
+ * the type being converted to, separated by a space, like "coffee js".
1521
+ *
1522
+ * There can be more than two steps used in conversion, such as "ejs view js".
1523
+ * This will define a method that converts .ejs files to .view files. There
1524
+ * should be another converter for "view js" that makes this final conversion
1525
+ * to JS.
1526
+ *
1527
+ * @param {Function} cb( options, original, success, error ) a callback
1528
+ * function that converts the new file type to a basic type. This method
1529
+ * needs to do two things: 1) save the text of the converted file in
1530
+ * options.text and 2) call success() when the conversion is done (it can work
1531
+ * asynchronously).
1532
+ *
1533
+ * - __options__ - the steal options for this file, including path information
1534
+ * - __original__ - the original argument passed to steal, which might be a
1535
+ * path or a function
1536
+ * - __success__ - a method to call when the file is converted and processed
1537
+ * successfully
1538
+ * - __error__ - a method called if the conversion fails or the file doesn't
1539
+ * exist
1540
+ */
1541
+ steal.config.types = function(types){
1542
+ each(types, steal.type)
1543
+ };
1544
+
1545
+
1546
+
1547
+ // adds a type (js by default) and buildType (css, js)
1548
+ // this should happen right before loading
1549
+ // however, what if urls are different
1550
+ // because one file has JS and another does not?
1551
+ // we could check if it matches something with .js because foo.less.js SHOULD
1552
+ // be rare
1553
+ Resource.prototype.execute = before(Resource.prototype.execute, function() {
1554
+ var raw = this.options;
1555
+
1556
+ // if it's a string, get it's extension and check if
1557
+ // it is a registered type, if it is ... set the type
1558
+ if (!raw.type ) {
1559
+ var ext = URI(raw.id).ext();
1560
+ if (!ext && !types[ext] ) {
1561
+ ext = "js";
1562
+ }
1563
+ raw.type = ext;
1564
+ }
1565
+ if (!types[raw.type] && stealConfig.env == 'development' ) {
1566
+ throw "steal.js - type " + raw.type + " has not been loaded.";
1567
+ } else if (!types[raw.type] && stealConfig.env == 'production' ) {
1568
+ // if we haven't defined EJS yet and we're in production, its ok, just ignore it
1569
+ return;
1570
+ }
1571
+ var converters = types[raw.type].convert;
1572
+ raw.buildType = converters.length ? converters[converters.length - 1] : raw.type;
1573
+ });
1574
+
1575
+ steal.
1576
+ /**
1577
+ * Called for every file that is loaded. It sets up a string of methods called
1578
+ * for each type in the conversion chain and calls each type one by one.
1579
+ *
1580
+ * For example, if the file is a coffeescript file, here's what happens:
1581
+ *
1582
+ * - The "text" type converter is called first. This will perform an AJAX
1583
+ * request for the file and save it in options.text.
1584
+ * - Then the coffee type converter is called (the user provided method).
1585
+ * This converts the text from coffeescript to JavaScript.
1586
+ * - Finally the "js" type converter is called, which inserts the JavaScript
1587
+ * in the page as a script tag that is executed.
1588
+ *
1589
+ * @param {Object} options the steal options for this file, including path information
1590
+ * @param {Function} success a method to call when the file is converted and processed successfully
1591
+ * @param {Function} error a method called if the conversion fails or the file doesn't exist
1592
+ */
1593
+ require = function( options, success, error ) {
1594
+ // add the src option
1595
+ options.src = options.idToUri ? options.idToUri(options.id) : steal.idToUri(options.id);
1596
+
1597
+ // get the type
1598
+ var type = types[options.type],
1599
+ converters;
1600
+
1601
+ // if this has converters, make it get the text first, then pass it to the type
1602
+ if ( type.convert.length ) {
1603
+ converters = type.convert.slice(0);
1604
+ converters.unshift("text", options.type)
1605
+ } else {
1606
+ converters = [options.type]
1607
+ }
1608
+ require(options, converters, success, error)
1609
+ };
1610
+
1611
+ function require(options, converters, success, error) {
1612
+
1613
+ var type = types[converters.shift()];
1614
+
1615
+ type.require(options, function require_continue_check() {
1616
+ // if we have more types to convert
1617
+ if ( converters.length ) {
1618
+ require(options, converters, success, error)
1619
+ } else { // otherwise this is the final
1620
+ success.apply(this, arguments);
1621
+ }
1622
+ }, error)
1623
+ };
1624
+
1625
+
1626
+ // =============================== TYPES ===============================
1627
+ // a clean up script that prevents memory leaks and removes the
1628
+ // script
1629
+ var cleanUp = function( elem ) {
1630
+ elem.onreadystatechange = elem.onload = elem.onerror = null;
1631
+
1632
+ setTimeout(function() {
1633
+ head().removeChild(elem);
1634
+ }, 1);
1635
+ },
1636
+ // the last inserted script, needed for IE
1637
+ lastInserted,
1638
+ // if the state is done
1639
+ stateCheck = /^loade|c|u/;
1640
+
1641
+
1642
+ var cssCount = 0,
1643
+ createSheet = doc && doc.createStyleSheet,
1644
+ lastSheet, lastSheetOptions;
1645
+
1646
+ // Apply all the basic types
1647
+ steal.config({
1648
+ types:{
1649
+ "js": function( options, success, error ) {
1650
+ // create a script tag
1651
+ var script = scriptTag(),
1652
+ callback = function() {
1653
+ if (!script.readyState || stateCheck.test(script.readyState) ) {
1654
+ cleanUp(script);
1655
+ success();
1656
+ }
1657
+ };
1658
+
1659
+ // if we have text, just set and insert text
1660
+ if ( options.text ) {
1661
+ // insert
1662
+ script.text = options.text;
1663
+
1664
+ } else {
1665
+
1666
+ // listen to loaded
1667
+ script.onload = script.onreadystatechange = callback;
1668
+
1669
+ var src = options.src; //steal.idToUri( options.id );
1670
+ // error handling doesn't work on firefox on the filesystem
1671
+ if ( support.error && error && src.protocol !== "file" ) {
1672
+ script.onerror = error;
1673
+ }
1674
+ script.src = "" + src;
1675
+ //script.src = options.src = addSuffix(options.src);
1676
+ //script.async = false;
1677
+ script.onSuccess = success;
1678
+ }
1679
+
1680
+ // insert the script
1681
+ lastInserted = script;
1682
+ head().insertBefore(script, head().firstChild);
1683
+
1684
+ // if text, just call success right away, and clean up
1685
+ if ( options.text ) {
1686
+ callback();
1687
+ }
1688
+ },
1689
+ "fn": function( options, success ) {
1690
+ var ret;
1691
+ if (!options.skipCallbacks ) {
1692
+ ret = options.fn();
1693
+ }
1694
+ success(ret);
1695
+ },
1696
+ "text": function( options, success, error ) {
1697
+ steal.request(options, function( text ) {
1698
+ options.text = text;
1699
+ success(text);
1700
+ }, error)
1701
+ },
1702
+ "css": function( options, success, error ) {
1703
+ if ( options.text ) { // less
1704
+ var css = createElement("style");
1705
+ css.type = "text/css";
1706
+ if ( css.styleSheet ) { // IE
1707
+ css.styleSheet.cssText = options.text;
1708
+ } else {
1709
+ (function( node ) {
1710
+ if ( css.childNodes.length ) {
1711
+ if ( css.firstChild.nodeValue !== node.nodeValue ) {
1712
+ css.replaceChild(node, css.firstChild);
1713
+ }
1714
+ } else {
1715
+ css.appendChild(node);
1716
+ }
1717
+ })(doc.createTextNode(options.text));
1718
+ }
1719
+ head().appendChild(css);
1720
+ } else {
1721
+ if ( createSheet ) {
1722
+ // IE has a 31 sheet and 31 import per sheet limit
1723
+ if (!cssCount++ ) {
1724
+ lastSheet = doc.createStyleSheet(addSuffix(options.src));
1725
+ lastSheetOptions = options;
1726
+ } else {
1727
+ var relative = "" + URI(URI(lastSheetOptions.src).dir()).pathTo(options.src);
1728
+ lastSheet.addImport(addSuffix(relative));
1729
+ if ( cssCount == 30 ) {
1730
+ cssCount = 0;
1731
+ }
1732
+ }
1733
+ success();
1734
+ return;
1735
+ }
1736
+
1737
+ options = options || {};
1738
+ var link = createElement("link");
1739
+ link.rel = options.rel || "stylesheet";
1740
+ link.href = addSuffix(options.src);
1741
+ link.type = "text/css";
1742
+ head().appendChild(link);
1743
+ }
1744
+
1745
+ success();
1746
+ }
1747
+ }
1748
+ });
1749
+
1750
+
1751
+
1752
+ // =============================== HELPERS ===============================
1753
+ var factory = function() {
1754
+ return win.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
1755
+ };
1756
+
1757
+
1758
+ steal.
1759
+ /**
1760
+ * Performs an XHR request
1761
+ * @param {Object} options
1762
+ * @param {Function} success
1763
+ * @param {Function} error
1764
+ */
1765
+ request = function( options, success, error ) {
1766
+ var request = new factory(),
1767
+ contentType = (options.contentType || "application/x-www-form-urlencoded; charset=utf-8"),
1768
+ clean = function() {
1769
+ request = check = clean = null;
1770
+ },
1771
+ check = function() {
1772
+ var status;
1773
+ if ( request && request.readyState === 4 ) {
1774
+ status = request.status;
1775
+ if ( status === 500 || status === 404 || status === 2 || request.status < 0 || (!status && request.responseText === "") ) {
1776
+ error && error(request.status);
1777
+ } else {
1778
+ success(request.responseText);
1779
+ }
1780
+ clean();
1781
+ }
1782
+ };
1783
+ request.open("GET", options.src + '', !(options.async === false));
1784
+ request.setRequestHeader("Content-type", contentType);
1785
+ if ( request.overrideMimeType ) {
1786
+ request.overrideMimeType(contentType);
1787
+ }
1788
+
1789
+ request.onreadystatechange = check;
1790
+ try {
1791
+ request.send(null);
1792
+ }
1793
+ catch (e) {
1794
+ if ( clean ) {
1795
+ console.error(e);
1796
+ error && error();
1797
+ clean();
1798
+ }
1799
+ }
1800
+
1801
+ };
1802
+
1803
+
1804
+ // ============================== Packages ===============================
1805
+ /**
1806
+ * @function steal.packages
1807
+ * `steal.packages( packageIds... )` defines modules for deferred downloading.
1808
+ *
1809
+ * This is used by the build system to build collections of modules that will be downloaded
1810
+ * after initial page load.
1811
+ *
1812
+ * For example, an application that wants to progressively load the contents and
1813
+ * dependencies of _login/login.js_, _filemanager/filemanager.js_, and _contacts/contacts.js_,
1814
+ * while immediately loading the current users's data might look like:
1815
+ *
1816
+ * steal.packages('login','filemanager','contacts')
1817
+ * steal('models/user', function(User){
1818
+ *
1819
+ * // get the current User
1820
+ * User.findOne({id: "current"},
1821
+ *
1822
+ * // success - they logged in
1823
+ * function(user){
1824
+ * if(window.location.hash == "#filemanager"){
1825
+ * steal('filemanager')
1826
+ * }
1827
+ * },
1828
+ * // error - they are logged out
1829
+ * function(){
1830
+ * steal('login', function(){
1831
+ * new Login(document.body);
1832
+ * // preload filemanager
1833
+ *
1834
+ * })
1835
+ * })
1836
+ * })
1837
+ *
1838
+ *
1839
+ * steal.packages('tasks','dashboard','fileman');
1840
+ *
1841
+ */
1842
+ var packs = [],
1843
+ packHash = {};
1844
+ steal.packages = function( map ) {
1845
+
1846
+ if (!arguments.length ) {
1847
+ return packs;
1848
+ } else {
1849
+ if ( typeof map == 'string' ) {
1850
+ packs.push.apply(packs, arguments);
1851
+ } else {
1852
+ packHash = map;
1853
+ }
1854
+
1855
+ return this;
1856
+ }
1857
+ };
1858
+
1859
+
1860
+
1861
+ // =============================== MAPPING ===============================
1862
+ URI.prototype.insertMapping = function() {
1863
+ // go through mappings
1864
+ var orig = "" + this,
1865
+ key, value;
1866
+ for ( key in steal.mappings ) {
1867
+ value = steal.mappings[key]
1868
+ if ( value.test.test(orig) ) {
1869
+ return orig.replace(key, value.path);
1870
+ }
1871
+ }
1872
+ return URI(orig);
1873
+ };
1874
+
1875
+ // =============================== STARTUP ===============================
1876
+ var rootSteal = false;
1877
+
1878
+ // essentially ... we need to know when we are on our first steal
1879
+ // then we need to know when the collection of those steals ends ...
1880
+ // and, it helps if we use a 'collection' steal because of it's natural
1881
+ // use for going through the pending queue
1882
+ //
1883
+ extend(steal, {
1884
+ // modifies src
1885
+ /*makeOptions : after(steal.makeOptions,function(raw){
1886
+ raw.src = URI.root().join(raw.rootSrc = URI( raw.rootSrc ).insertMapping());
1887
+ }),*/
1888
+
1889
+ //root mappings to other locations
1890
+ mappings: {},
1891
+
1892
+ /**
1893
+ * Maps a 'rooted' folder to another location.
1894
+ * @param {String|Object} from the location you want to map from. For example:
1895
+ * 'foo/bar'
1896
+ * @param {String} [to] where you want to map this folder too. Ex: 'http://foo.cdn/bar'
1897
+ * @return {steal}
1898
+ */
1899
+ map: function( from, to ) {
1900
+ if ( isString(from) ) {
1901
+ steal.mappings[from] = {
1902
+ test: new RegExp("^(\/?" + from + ")([/.]|$)"),
1903
+ path: to
1904
+ };
1905
+ each(resources, function( id, resource ) {
1906
+ if ( resource.options.type != "fn" ) {
1907
+ // TODO terrible
1908
+ var buildType = resource.options.buildType;
1909
+ resource.setOptions(resource.orig);
1910
+ resource.options.buildType = buildType;
1911
+ }
1912
+ })
1913
+ } else { // its an object
1914
+ each(from, steal.map);
1915
+ }
1916
+ return this;
1917
+ },
1918
+ // called after steals are added to the pending queue
1919
+ after: function() {
1920
+ // if we don't have a current 'top' steal
1921
+ // we create one and set it up
1922
+ // to start loading its dependencies (the current pending steals)
1923
+ if (!rootSteal ) {
1924
+ rootSteal = new Resource();
1925
+ // keep a reference in case it disappears
1926
+ var cur = rootSteal,
1927
+ // runs when a steal is starting
1928
+ go = function() {
1929
+ // indicates that a collection of steals has started
1930
+ steal.trigger("start", cur);
1931
+ cur.completed.then(function() {
1932
+
1933
+ rootSteal = null;
1934
+ steal.trigger("end", cur);
1935
+
1936
+
1937
+ });
1938
+
1939
+ cur.executed();
1940
+ };
1941
+ // If we are in the browser, wait a
1942
+ // brief timeout before executing the rootResource.
1943
+ // This allows embeded script tags with steal to be part of
1944
+ // the initial set
1945
+ if ( win.setTimeout ) {
1946
+ // we want to insert a "wait" after the current pending
1947
+ steal.pushPending();
1948
+ setTimeout(function() {
1949
+ steal.popPending();
1950
+ go();
1951
+ }, 0)
1952
+ } else {
1953
+ // if we are in rhino, start loading dependencies right away
1954
+ go()
1955
+ }
1956
+ }
1957
+ },
1958
+ _before: before,
1959
+ _after: after
1960
+ });
1961
+
1962
+ (function(){
1963
+ var myPending;
1964
+ steal.pushPending = function(){
1965
+ myPending = pending.slice(0);
1966
+ pending = [];
1967
+ each(myPending, function(i, arg){
1968
+ Resource.make(arg);
1969
+ })
1970
+ }
1971
+ steal.popPending = function(){
1972
+ pending = pending.length ? myPending.concat(null,pending) : myPending;
1973
+ }
1974
+ })();
1975
+
1976
+ // =============================== jQuery ===============================
1977
+ (function() {
1978
+ var jQueryIncremented = false,
1979
+ jQ, ready = false;
1980
+
1981
+ // check if jQuery loaded after every script load ...
1982
+ Resource.prototype.executed = before(Resource.prototype.executed, function() {
1983
+
1984
+ var $ = win.jQuery;
1985
+ if ( $ && "readyWait" in $ ) {
1986
+
1987
+ //Increment jQuery readyWait if ncecessary.
1988
+ if (!jQueryIncremented ) {
1989
+ jQ = $;
1990
+ $.readyWait += 1;
1991
+ jQueryIncremented = true;
1992
+ }
1993
+ }
1994
+ });
1995
+
1996
+ // once the current batch is done, fire ready if it hasn't already been done
1997
+ steal.bind("end", function() {
1998
+ if ( jQueryIncremented && !ready ) {
1999
+ jQ.ready(true);
2000
+ ready = true;
2001
+ }
2002
+ })
2003
+
2004
+
2005
+ })();
2006
+
2007
+ // =============================== ERROR HANDLING ===============================
2008
+ extend(Resource.prototype, {
2009
+ load: after(Resource.prototype.load, function( stel ) {
2010
+ var self = this;
2011
+ if ( doc && !self.completed && !self.completeTimeout && !steal.isRhino && (self.options.src.protocol == "file" || !support.error) ) {
2012
+ self.completeTimeout = setTimeout(function() {
2013
+ throw "steal.js : " + self.options.src + " not completed"
2014
+ }, 5000);
2015
+ }
2016
+ }),
2017
+ complete: after(Resource.prototype.complete, function() {
2018
+ this.completeTimeout && clearTimeout(this.completeTimeout)
2019
+ }),
2020
+
2021
+
2022
+ // if we're about to mark a file as executed, mark its "has" array files as
2023
+ // executed also
2024
+ executed: before(Resource.prototype.executed, function() {
2025
+ if ( this.options.has ) {
2026
+ this.loadHas();
2027
+ }
2028
+ }),
2029
+
2030
+ /**
2031
+ * @hide
2032
+ * Goes through the array of files listed in this.options.has, marks them all as loaded.
2033
+ * This is used for files like production.css, which, once they load, need to mark the files they
2034
+ * contain as loaded.
2035
+ */
2036
+ loadHas: function() {
2037
+ var stel, i, current = URI.cur;
2038
+
2039
+ if ( this.options.buildType == 'js' ) {
2040
+ return;
2041
+ }
2042
+
2043
+ // mark everything in has loaded
2044
+ each(this.options.has, function( i, has ) {
2045
+ // don't want the current file to change, since we're just marking files as loaded
2046
+ URI.cur = URI(current);
2047
+ steal.executed(has);
2048
+ });
2049
+
2050
+ }
2051
+ });
2052
+
2053
+ // =========== HAS ARRAY STUFF ============
2054
+ // Logic that deals with files that have collections of other files within
2055
+ // them. This is usually a production.css file,
2056
+ // which when it loads, needs to mark several CSS and LESS files it represents
2057
+ // as being "loaded". This is done by the production.js file having
2058
+ // steal({src: "production.css", has: ["file1.css", "file2.css"]
2059
+ //
2060
+ // after a steal is created, if its been loaded
2061
+ // already and has a "has", mark those files as loaded
2062
+ Resource.make = after(Resource.make, function( stel ) {
2063
+ // if we have things
2064
+ if ( stel.options.has ) {
2065
+ // if we have loaded this already (and we are adding has's)
2066
+ if ( stel.run.isResolved() ) {
2067
+ stel.loadHas();
2068
+ } else {
2069
+ // have to mark has as loading and executing (so we don't try to get them)
2070
+ steal.has.apply(steal, stel.options.has)
2071
+ }
2072
+ }
2073
+ return stel;
2074
+ }, true);
2075
+
2076
+
2077
+
2078
+
2079
+ // =========== DEBUG =========
2080
+
2081
+
2082
+ /*var name = function(stel){
2083
+ if(stel.options && stel.options.type == "fn"){
2084
+ return stel.orig.name? stel.orig.name : stel.options.id+":fn";//(""+stel.orig).substr(0,10)
2085
+ }
2086
+ return stel.options ? stel.options.id + "": "CONTAINER"
2087
+ }
2088
+
2089
+
2090
+ //Resource.prototype.load = before( Resource.prototype.load, function(){
2091
+ // console.log(" load", name(this), this.loading, steal._id, this.id)
2092
+ //})
2093
+
2094
+ Resource.prototype.executed = before(Resource.prototype.executed, function(){
2095
+ var namer= name(this)
2096
+ console.log(" executed", namer, steal._id, this.id)
2097
+ })
2098
+
2099
+ Resource.prototype.complete = before(Resource.prototype.complete, function(){
2100
+ console.log(" complete", name(this), steal._id, this.id)
2101
+ })*/
2102
+
2103
+
2104
+
2105
+ // ============= WINDOW LOAD ========
2106
+ var addEvent = function( elem, type, fn ) {
2107
+ if ( elem.addEventListener ) {
2108
+ elem.addEventListener(type, fn, false);
2109
+ } else if ( elem.attachEvent ) {
2110
+ elem.attachEvent("on" + type, fn);
2111
+ } else {
2112
+ fn();
2113
+ }
2114
+ },
2115
+ loaded = {
2116
+ load: Deferred(),
2117
+ end: Deferred()
2118
+ },
2119
+ firstEnd = false;
2120
+
2121
+ addEvent(win, "load", function() {
2122
+ loaded.load.resolve();
2123
+ });
2124
+
2125
+ steal.one("end", function( collection ) {
2126
+ loaded.end.resolve(collection);
2127
+ firstEnd = collection;
2128
+ steal.trigger("done", firstEnd)
2129
+ })
2130
+ steal.firstComplete = loaded.end;
2131
+
2132
+ Deferred.when(loaded.load, loaded.end).then(function() {
2133
+ steal.trigger("ready")
2134
+ steal.isReady = true;
2135
+ });
2136
+
2137
+ steal.events.done = {
2138
+ add: function( cb ) {
2139
+ if ( firstEnd ) {
2140
+ cb(firstEnd);
2141
+ return false;
2142
+ } else {
2143
+ return cb;
2144
+ }
2145
+ }
2146
+ };
2147
+
2148
+
2149
+
2150
+ // =========== INTERACTIVE STUFF ===========
2151
+ // Logic that deals with making steal work with IE. IE executes scripts out of order, so in order to tell which scripts are
2152
+ // dependencies of another, steal needs to check which is the currently "interactive" script.
2153
+ var interactiveScript,
2154
+ // key is script name, value is array of pending items
2155
+ interactives = {},
2156
+ getInteractiveScript = function() {
2157
+ var scripts = getElementsByTagName("script"),
2158
+ i = scripts.length;
2159
+ while ( i-- ) {
2160
+ if ( scripts[i].readyState === "interactive" ) {
2161
+ return scripts[i];
2162
+ }
2163
+ }
2164
+ },
2165
+ getCachedInteractiveScript = function() {
2166
+ if ( interactiveScript && interactiveScript.readyState === 'interactive' ) {
2167
+ return interactiveScript;
2168
+ }
2169
+
2170
+ if ( interactiveScript = getInteractiveScript() ) {
2171
+ return interactiveScript;
2172
+ }
2173
+
2174
+ // check last inserted
2175
+ if ( lastInserted && lastInserted.readyState == 'interactive' ) {
2176
+ return lastInserted;
2177
+ }
2178
+
2179
+ return null;
2180
+ };
2181
+
2182
+
2183
+ support.interactive = doc && !! getInteractiveScript();
2184
+
2185
+ if ( support.interactive ) {
2186
+
2187
+ // after steal is called, check which script is "interactive" (for IE)
2188
+ steal.after = after(steal.after, function() {
2189
+
2190
+ // check if disabled by steal.loading()
2191
+ if (!support.interactive ) {
2192
+ return;
2193
+ }
2194
+
2195
+ var interactive = getCachedInteractiveScript();
2196
+ // if no interactive script, this is a steal coming from inside a steal, let complete handle it
2197
+ if (!interactive || !interactive.src || /steal\.(production|production\.[a-zA-Z0-9\-\.\_]*)*js/.test(interactive.src) ) {
2198
+ return;
2199
+ }
2200
+ // get the source of the script
2201
+ var src = interactive.src;
2202
+ // create an array to hold all steal calls for this script
2203
+ if (!interactives[src] ) {
2204
+ interactives[src] = []
2205
+ }
2206
+ // add to the list of steals for this script tag
2207
+ if ( src ) {
2208
+ interactives[src].push.apply(interactives[src], pending);
2209
+ pending = [];
2210
+ }
2211
+ })
2212
+
2213
+ // This is used for packaged scripts. As the packaged script executes, we grab the
2214
+ // dependencies that have come so far and assign them to the loaded script
2215
+ steal.preexecuted = before(steal.preexecuted, function( stel ) {
2216
+ // check if disabled by steal.loading()
2217
+ if (!support.interactive ) {
2218
+ return;
2219
+ }
2220
+
2221
+ // get the src name
2222
+ var src = stel.options.src,
2223
+ // and the src of the current interactive script
2224
+ interactiveSrc = getCachedInteractiveScript().src;
2225
+
2226
+ interactives[src] = interactives[interactiveSrc];
2227
+ interactives[interactiveSrc] = null;
2228
+
2229
+ })
2230
+ }
2231
+
2232
+
2233
+ // ## Config ##
2234
+ var stealCheck = /steal\.(production\.)?js.*/,
2235
+ getStealScriptSrc = function() {
2236
+ if (!doc ) {
2237
+ return;
2238
+ }
2239
+ var scripts = getElementsByTagName("script"),
2240
+ script;
2241
+
2242
+ // find the steal script and setup initial paths.
2243
+ each(scripts, function( i, s ) {
2244
+ if ( stealCheck.test(s.src) ) {
2245
+ script = s;
2246
+ }
2247
+ });
2248
+ return script;
2249
+ };
2250
+
2251
+ steal.getScriptOptions = function( script ) {
2252
+
2253
+ var options = {},
2254
+ parts, src, query, startFile, env;
2255
+
2256
+ script = script || getStealScriptSrc();
2257
+
2258
+ if ( script ) {
2259
+
2260
+ // Split on question mark to get query
2261
+ parts = script.src.split("?");
2262
+ src = parts.shift();
2263
+ query = parts.join("?");
2264
+
2265
+ // Split on comma to get startFile and env
2266
+ parts = query.split(",");
2267
+
2268
+ if ( src.indexOf("steal.production") > -1 ) {
2269
+ options.env = "production";
2270
+ }
2271
+
2272
+ // Grab startFile
2273
+ startFile = parts[0];
2274
+
2275
+ if ( startFile ) {
2276
+ if ( startFile.indexOf(".js") == -1 ) {
2277
+ startFile += "/" + startFile.split("/").pop() + ".js";
2278
+ }
2279
+ options.startFile = startFile;
2280
+ }
2281
+
2282
+ // Grab env
2283
+ env = parts[1];
2284
+
2285
+ if ( env ) {
2286
+ options.env = env;
2287
+ }
2288
+
2289
+ // Split on / to get rootUrl
2290
+ parts = src.split("/")
2291
+ parts.pop();
2292
+ if ( parts[parts.length - 1] == "steal" ) {
2293
+ parts.pop();
2294
+ }
2295
+ options.root = parts.join("/")
2296
+
2297
+ }
2298
+
2299
+ return options;
2300
+ };
2301
+
2302
+ startup = after(startup, function() {
2303
+ // get options from
2304
+ var options = {};
2305
+
2306
+ // A: GET OPTIONS
2307
+ // 1. get script options
2308
+ extend(options, steal.getScriptOptions());
2309
+
2310
+ // 2. options from a steal object that existed before this steal
2311
+ extend(options, opts);
2312
+
2313
+ // 3. if url looks like steal[xyz]=bar, add those to the options
2314
+ // does this ened to be supported anywhere?
2315
+ var search = win.location && decodeURIComponent(win.location.search);
2316
+ search && search.replace(/steal\[([^\]]+)\]=([^&]+)/g, function( whoe, prop, val ) {
2317
+ options[prop] = ~val.indexOf(",") ? val.split(",") : val;
2318
+ });
2319
+
2320
+ // B: DO THINGS WITH OPTIONS
2321
+ // CALCULATE CURRENT LOCATION OF THINGS ...
2322
+ steal.config(options);
2323
+
2324
+
2325
+ // mark things that have already been loaded
2326
+ each(options.executed || [], function( i, stel ) {
2327
+ steal.executed(stel)
2328
+ })
2329
+ // immediate steals we do
2330
+ var steals = [];
2331
+
2332
+ // add start files first
2333
+ if ( options.startFiles ) {
2334
+ /// this can be a string or an array
2335
+ steals.push.apply(steals, isString(options.startFiles) ? [options.startFiles] : options.startFiles)
2336
+ options.startFiles = steals.slice(0)
2337
+ }
2338
+
2339
+ // either instrument is in this page (if we're the window opened from
2340
+ // steal.browser), or its opener has it
2341
+ // try-catching this so we dont have to build up to the iframe
2342
+ // instrumentation check
2343
+ try {
2344
+ // win.top.steal.instrument is for qunit
2345
+ // win.top.opener.steal.instrument is for funcunit
2346
+ if(!options.browser && ((win.top && win.top.steal.instrument) ||
2347
+ (win.top && win.top.opener && win.top.opener.steal && win.top.opener.steal.instrument))) {
2348
+
2349
+ // force startFiles to load before instrument
2350
+ steals.push(noop, {
2351
+ id: "steal/instrument",
2352
+ waits: true
2353
+ });
2354
+ }
2355
+ } catch (e) {
2356
+ // This would throw permission denied if
2357
+ // the child window was from a different domain
2358
+ }
2359
+
2360
+ // we only load things with force = true
2361
+ if ( stealConfig.env == "production" && stealConfig.loadProduction && stealConfig.production ) {
2362
+ steal({
2363
+ id: stealConfig.production,
2364
+ force: true
2365
+ });
2366
+ } else {
2367
+ steals.unshift("stealconfig.js")
2368
+
2369
+ if ( options.loadDev !== false ) {
2370
+ steals.unshift({
2371
+ id: "steal/dev/dev.js",
2372
+ ignore: true
2373
+ });
2374
+ }
2375
+
2376
+ if ( options.startFile ) {
2377
+ steals.push(null,options.startFile)
2378
+ }
2379
+ }
2380
+ if ( steals.length ) {
2381
+ steal.apply(win, steals);
2382
+ }
2383
+ });
2384
+
2385
+
2386
+ // ## AMD ##
2387
+ var modules = {
2388
+
2389
+ };
2390
+
2391
+ // convert resources to modules ...
2392
+ // a function is a module definition piece
2393
+ // you steal(moduleId1, moduleId2, function(module1, module2){});
2394
+ //
2395
+ win.define = function( moduleId, dependencies, method ) {
2396
+ if(typeof moduleId == 'function'){
2397
+ modules[URI.cur+""] = moduleId();
2398
+ } else if(!method && dependencies){
2399
+ if(typeof dependencies == "function"){
2400
+ modules[moduleId] = dependencies();
2401
+ } else {
2402
+ modules[moduleId] = dependencies;
2403
+ }
2404
+
2405
+ } else if (dependencies && method && !dependencies.length ) {
2406
+ modules[moduleId] = method();
2407
+ } else {
2408
+ steal.apply(null, map(dependencies, function(dependency){
2409
+ dependency = typeof dependency === "string" ? {
2410
+ id: dependency
2411
+ } : dependency;
2412
+ dependency.toId = steal.amdToId;
2413
+
2414
+ dependency.idToUri = steal.amdIdToUri;
2415
+ return dependency;
2416
+ }).concat(method) )
2417
+ }
2418
+
2419
+ }
2420
+ win.require = function(dependencies, method){
2421
+ var depends = map(dependencies, function(dependency){
2422
+ dependency = typeof dependency === "string" ? {
2423
+ id: dependency
2424
+ } : dependency;
2425
+ dependency.toId = steal.amdToId;
2426
+
2427
+ dependency.idToUri = steal.amdIdToUri;
2428
+ return dependency;
2429
+ }).concat([method]);
2430
+ console.log("stealing",depends.slice(0))
2431
+ steal.apply(null, depends )
2432
+ }
2433
+ win.define.amd = {
2434
+ jQuery: true
2435
+ }
2436
+
2437
+
2438
+ //steal.when = when;
2439
+ // make steal public
2440
+ win.steal = steal;
2441
+
2442
+
2443
+ // make steal loaded
2444
+ define("steal", [], function() {
2445
+ return steal;
2446
+ });
2447
+
2448
+ define("require",function(){
2449
+ return require;
2450
+ })
2451
+
2452
+ var stealResource = new Resource("steal")
2453
+ stealResource.value = steal;
2454
+ stealResource.loaded.resolve();
2455
+ stealResource.run.resolve();
2456
+ stealResource.executing = true;
2457
+ stealResource.completed.resolve();
2458
+
2459
+ resources[stealResource.options.id] = stealResource;
2460
+
2461
+ startup();
2462
+ //win.steals = steals;
2463
+ win.steal.resources = resources;
2464
+ win.Resource = Resource;
2465
+
2466
+ })(this);