kojac 0.9.0

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