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