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,2098 @@
1
+ (function(){
2
+
3
+ // Gets the window (even if there is none)
4
+ var win = (function(){return this}).call(null),
5
+ // String constants (for better minification)
6
+ STR_ONLOAD = "onload",
7
+ STR_ONERROR = "onerror",
8
+ STR_ONREADYSTATECHANGE = "onreadystatechange",
9
+ STR_CREATE_ELEMENT = 'createElement',
10
+ STR_GET_BY_TAG = 'getElementsByTagName',
11
+
12
+ // the document ( might not exist in rhino )
13
+ doc = win.document,
14
+
15
+ // creates a script tag
16
+ scriptTag = function() {
17
+ var start = doc[STR_CREATE_ELEMENT]('script');
18
+ start.type = 'text/javascript';
19
+ return start;
20
+ },
21
+ // a function that returns the head element
22
+ // creates and caches the lookup if necessary
23
+ head = function() {
24
+ var d = doc,
25
+ de = d.documentElement,
26
+ heads = d[STR_GET_BY_TAG]("head"),
27
+ hd = heads[0];
28
+ if (! hd ) {
29
+ hd = d[STR_CREATE_ELEMENT]('head');
30
+ de.insertBefore(hd, de.firstChild);
31
+ }
32
+ // replace head so it runs fast next time.
33
+ head = function(){
34
+ return hd;
35
+ }
36
+ return hd;
37
+ },
38
+ // extends one object with another
39
+ extend = function( d, s ) {
40
+ for ( var p in s ) {
41
+ d[p] = s[p];
42
+ }
43
+ return d;
44
+ },
45
+ // a jQuery-like $.each
46
+ each = function(arr, cb){
47
+ for(var i =0, len = arr.length; i <len; i++){
48
+ cb.call(arr[i],i,arr[i])
49
+ }
50
+ return arr;
51
+ },
52
+ // makes an array of things
53
+ makeArray = function(args){
54
+ var arr = [];
55
+ each(args, function(i, str){arr[i] = str});
56
+ return arr;
57
+ },
58
+ // testing support for various browser behaviors
59
+ support = {
60
+ // does onerror work in script tags?
61
+ error: doc && (function(){
62
+ var script = scriptTag();
63
+ script.setAttribute( "onerror", "return;" );
64
+ return typeof script["onerror"] === "function" ?
65
+ true : "onerror" in script
66
+ })(),
67
+ // If scripts support interactive ready state.
68
+ // This is set later.
69
+ interactive: false,
70
+ attachEvent : doc && scriptTag().attachEvent
71
+ },
72
+ // a startup function that will be called when steal is ready
73
+ startup = function(){},
74
+ // the old steal value
75
+ oldsteal = win.steal,
76
+ // if oldsteal is an object
77
+ // we use it as options to configure steal
78
+ opts = typeof oldsteal == 'object' ? oldsteal : {};
79
+
80
+ // =============================== STEAL ===============================
81
+
82
+ /**
83
+ * @class steal
84
+ * @parent stealjs
85
+ *
86
+ * __steal__ is a function that loads scripts, css, and
87
+ * other resources into your application.
88
+ *
89
+ * steal(FILE_or_FUNCTION, ...)
90
+ *
91
+ * ## Quick Walkthrough
92
+ *
93
+ * Add a script tag that loads <code>steal/steal.js</code> and add
94
+ * the path to the first file to load in the query string like:
95
+ *
96
+ * &lt;script type='text/javascript'
97
+ * src='../steal/steal.js?myapp/myapp.js'>
98
+ * &lt;/script>
99
+ *
100
+ * Then, start loading things and using them like:
101
+ *
102
+ * steal('myapp/tabs.js',
103
+ * 'myapp/slider.js',
104
+ * 'myapp/style.css',function(){
105
+ *
106
+ * // tabs and slider have loaded
107
+ * $('#tabs').tabs();
108
+ * $('#slider').slider()
109
+ * })
110
+ *
111
+ * Make sure your widgets load their dependencies too:
112
+ *
113
+ * // myapp/tabs.js
114
+ * steal('jquery', function(){
115
+ * $.fn.tabs = function(){
116
+ * ...
117
+ * }
118
+ * })
119
+ *
120
+ * ## Examples:
121
+ *
122
+ * // Loads ROOT/jquery/controller/controller.js
123
+ * steal('jquery/controller')
124
+ * steal('jquery/controller/controller.js')
125
+ *
126
+ * // Loads coffee script type and a coffee file relative to
127
+ * // the current file
128
+ * steal('steal/coffee').then('./mycoffee.coffee')
129
+ *
130
+ * // Load 2 files and dependencies in parallel and
131
+ * // callback when both have completed
132
+ * steal('jquery/controller','jquery/model', function(){
133
+ * // $.Controller and $.Model are available
134
+ * })
135
+ *
136
+ * // Loads a coffee script with a non-standard extension (cf)
137
+ * // relative to the current page and instructs the build
138
+ * // system to not package it (but it will still be loaded).
139
+ * steal({
140
+ * src: "./foo.cf",
141
+ * packaged: false,
142
+ * type: "coffee"
143
+ * })
144
+ *
145
+ * The following is a longer walkthrough of how to install
146
+ * and use steal:
147
+ *
148
+ * ## Adding steal to a page
149
+ *
150
+ * After installing StealJS (or JavaScriptMVC),
151
+ * find the <code>steal</code> folder with
152
+ * <code>steal/steal.js</code>.
153
+ *
154
+ * To use steal, add a script tag
155
+ * to <code>steal/steal.js</code> to your
156
+ * html pages.
157
+ *
158
+ * This walkthrough assumes you have the steal script
159
+ * in <code>public/steal/steal.js</code> and a directory
160
+ * structure like:
161
+ *
162
+ * @codestart text
163
+ * /public
164
+ * /steal
165
+ * /pages
166
+ * myapp.html
167
+ * /myapp
168
+ * myapp.js
169
+ * jquery.js
170
+ * jquery.ui.tabs.js
171
+ * @codeend
172
+ *
173
+ * To use steal in <code>public/pages/myapp.html</code>,
174
+ * add a script tag in <code>myapp.html</code>:
175
+ *
176
+ * @codestart html
177
+ * &lt;script type='text/javascript'
178
+ * src='../steal/steal.js'>
179
+ * &lt;/script>
180
+ * @codeend
181
+ *
182
+ * <div class='whisper'>PRO TIP: Bottom load your scripts. It
183
+ * will increase your application's percieved response time.</div>
184
+ *
185
+ * ## Loading the First Script
186
+ *
187
+ * Once steal has been added to your page, it's time
188
+ * to load scripts. We want to load <code>myapp.js</code>
189
+ * and have it load <code>jquery.js</code> and
190
+ * <code>jquery.ui.tabs.js</code>.
191
+ *
192
+ * By default, steal likes your scripts
193
+ * to be within in the [steal.static.root steal.root] folder. The [steal.root] the
194
+ * folder contains the <code>steal</code> folder. In this example,
195
+ * it is the <code>public</code> folder.
196
+ *
197
+ * To load <code>myapp/myapp.js</code>, we have two options:
198
+ *
199
+ * #### Add a script tag
200
+ *
201
+ * Add a script tag after the steal
202
+ * script that 'steals' <code>myapp.js</code> like:
203
+ *
204
+ * @codestart html
205
+ * &lt;script type='text/javascript'>
206
+ * steal('myapp/myapp.js')
207
+ * &lt;/script>
208
+ * @codeend
209
+ *
210
+ * #### Add the script parameter
211
+ *
212
+ * The most common (and shortest) way to load <code>myapp.js</code>
213
+ * is to add the script path to the steal script's src after in the
214
+ * query params. So, instead of adding a script, we change
215
+ * the steal script from:
216
+ *
217
+ * @codestart html
218
+ * &lt;script type='text/javascript'
219
+ * src='../steal/steal.js'>
220
+ * &lt;/script>
221
+ * @codeend
222
+ *
223
+ * To
224
+ *
225
+ * @codestart html
226
+ * &lt;script type='text/javascript'
227
+ * src='../steal/steal.js?<b>myapp/myapp.js</b>'>
228
+ * &lt;/script>
229
+ * @codeend
230
+ *
231
+ * <div class='whisper'>PRO TIP: You can also just add
232
+ * <code>?myapp</code> to the query string.</div>
233
+ *
234
+ * ## Loading Scripts
235
+ *
236
+ * We want to load <code>jquery.js</code> and
237
+ * <code>jquery.ui.tabs.js</code> into the page and then
238
+ * add then create a tabs widget. First we need to load
239
+ * <code>jquery.js</code>.
240
+ *
241
+ * By default, steal loads script relative to [steal.root]. To
242
+ * load <code>myapp/jquery.js</code> we can the following to
243
+ * <code>myapp.js</code>:
244
+ *
245
+ * steal('myapp/jquery.js');
246
+ *
247
+ * But, we can also load relative to <code>myapp.js</code> like:
248
+ *
249
+ * steal('./jquery.js');
250
+ *
251
+ * Next, we need to load <code>jquery.ui.tabs.js</code>. You
252
+ * might expect something like:
253
+ *
254
+ * steal('./jquery.js','./jquery.ui.tabs.js')
255
+ *
256
+ * to work. But there are two problems / complications:
257
+ *
258
+ * - steal loads scripts in parallel and runs out of order
259
+ * - <code>jquery.ui.tabs.js</code> depends on jQuery being loaded
260
+ *
261
+ * This means that steal might load <code>jquery.ui.tabs.js</code>
262
+ * before <code>jquery.js</code>. But this is easily fixed.
263
+ *
264
+ * [steal.static.then] waits until all previous scripts have loaded and
265
+ * run before loading scripts after it. We can load <code>jquery.ui.tabs.js</code>
266
+ * after <code>jquery.js</code> like:
267
+ *
268
+ * steal('./jquery.js').then('./jquery.ui.tabs.js')
269
+ *
270
+ * Finally, we need to add tabs to the page after
271
+ * the tabs's widget has loaded. We can add a callback function to
272
+ * steal that will get called when all previous scripts have finished
273
+ * loading:
274
+ *
275
+ * steal('./jquery.js').then('./jquery.ui.tabs.js', function($){
276
+ * $('#tabs').tabs();
277
+ * })
278
+ *
279
+ * ## Other Info
280
+ *
281
+ * ### Exclude Code Blocks From Production
282
+ *
283
+ * To exclude code blocks from being included in
284
+ * production builds, add the following around
285
+ * the code blocks.
286
+ *
287
+ * //!steal-remove-start
288
+ * code to be removed at build
289
+ * //!steal-remove-end
290
+ *
291
+ * ### Lookup Paths
292
+ *
293
+ * By default steal loads resources relative
294
+ * to [steal.static.root steal.root]. For example, the following
295
+ * loads foo.js in <code>steal.root</code>:
296
+ *
297
+ * steal('foo.js'); // loads //foo.js
298
+ *
299
+ * This is the same as writing:
300
+ *
301
+ * steal('//foo.js');
302
+ *
303
+ * Steal uses <code>'//'</code> to designate the [steal.static.root steal.root]
304
+ * folder.
305
+ *
306
+ * To load relative to the current file, add <code>"./"</code> or
307
+ * <code>"../"</code>:
308
+ *
309
+ * steal("./bar.js","../folder/zed.js");
310
+ *
311
+ * Often, scripts can be found in a folder within the same
312
+ * name. For example, [jQuery.Controller $.Controller] is
313
+ * in <code>//jquery/controller/controller.js</code>. For convience,
314
+ * if steal is provided a path without an extension like:
315
+ *
316
+ * steal('FOLDER/PLUGIN');
317
+ *
318
+ * It is the same as writing:
319
+ *
320
+ * steal('FOLDER/PLUGIN/PLUGIN.js')
321
+ *
322
+ * This means that <code>//jquery/controller/controller.js</code>
323
+ * can be loaded like:
324
+ *
325
+ * steal('jquery/controller')
326
+ *
327
+ * ### Types
328
+ *
329
+ * steal can load resources other than JavaScript.
330
+ *
331
+ *
332
+ * @constructor
333
+ *
334
+ * Loads resources specified by each argument. By default, resources
335
+ * are loaded in parallel and run in any order.
336
+ *
337
+ *
338
+ * @param {String|Function|Object} resource...
339
+ *
340
+ * Each argument specifies a resource. Resources can
341
+ * be given as a:
342
+ *
343
+ * ### Object
344
+ *
345
+ * An object that specifies the loading and build
346
+ * behavior of a resource.
347
+ *
348
+ * steal({
349
+ * src: "myfile.cf",
350
+ * type: "coffee",
351
+ * packaged: true,
352
+ * unique: true,
353
+ * ignore: false,
354
+ * waits: false
355
+ * })
356
+ *
357
+ * The available options are:
358
+ *
359
+ * - __src__ {*String*} - the path to the resource.
360
+ *
361
+ * - __waits__ {*Boolean default=false*} - true the resource should wait
362
+ * for prior steals to load and run. False if the resource should load and run in
363
+ * parallel. This defaults to true for functions.
364
+ *
365
+ * - __unique__ {*Boolean default=true*} - true if this is a unique resource
366
+ * that 'owns' this url. This is true for files, false for functions.
367
+ *
368
+ * - __ignore__ {*Boolean default=false*} - true if this resource should
369
+ * not be built into a production file and not loaded in
370
+ * production. This is great for script that should only be available
371
+ * in development mode.
372
+ *
373
+ * - __packaged__ {*Boolean default=true*} - true if the script should be built
374
+ * into the production file. false if the script should not be built
375
+ * into the production file, but still loaded. This is useful for
376
+ * loading 'packages'.
377
+ *
378
+ * - __type__ {*String default="js"*} - the type of the resource. This
379
+ * is typically inferred from the src.
380
+ *
381
+ * ### __String__
382
+ *
383
+ * Specifies src of the resource. For example:
384
+ *
385
+ * steal('./file.js')
386
+ *
387
+ * Is the same as calling:
388
+ *
389
+ * steal({src: './file.js'})
390
+ *
391
+ * ### __Function__
392
+ *
393
+ * A callback function that runs when all previous steals
394
+ * have completed.
395
+ *
396
+ * steal('jquery', 'foo',function(){
397
+ * // jquery and foo have finished loading
398
+ * // and runing
399
+ * })
400
+ *
401
+ * @return {steal} the steal object for chaining
402
+ */
403
+ function steal() {
404
+ // convert arguments into an array
405
+ var args = makeArray(arguments);
406
+ pending.push.apply(pending, args);
407
+ // steal.after is called everytime steal is called
408
+ // it kicks off loading these files
409
+ steal.after(args);
410
+ // return steal for chaining
411
+ return steal;
412
+ };
413
+
414
+
415
+ // =============================== PATHS .8 ============================
416
+
417
+ // things that matter ...
418
+ // - the current file being loaded
419
+ // - the location of the page
420
+ // - the file
421
+
422
+ /**
423
+ * @class
424
+ * Used for getting information out of a path
425
+ * @constructor
426
+ * Takes a path
427
+ * @param {String} path
428
+ */
429
+ steal.File = function( path ) {
430
+ // if new was not used, use it.
431
+ if ( this.constructor != steal.File ) {
432
+ return new steal.File(path);
433
+ }
434
+ // save the path
435
+ this.path = typeof path == 'string'? path : path.path;
436
+ };
437
+ // alias steal.File to File
438
+ var File = steal.File,
439
+
440
+ // a reference to the current file
441
+ curFile;
442
+
443
+ // get and sets the current file
444
+ File.cur = function(newCurFile){
445
+ if(newCurFile !== undefined){
446
+ curFile = File(newCurFile);
447
+ }else{
448
+ return curFile || File("");
449
+ }
450
+ };
451
+
452
+
453
+ extend(File.prototype,
454
+ /**
455
+ * @prototype
456
+ */
457
+ {
458
+ // Removes hash and querystring
459
+ clean: function() {
460
+ return this.path.match(/([^\?#]*)/)[1];
461
+ },
462
+ // gets the files extension
463
+ ext : function(){
464
+ var match = this.clean().match(/\.([\w\d]+)$/)
465
+ return match ? match[1] : "";
466
+ },
467
+ // Returns everything before the last /
468
+ dir: function() {
469
+ // remove any query string
470
+ var cleaned = this.clean(),
471
+ // get the last /
472
+ last = cleaned.lastIndexOf('/'),
473
+ // if there is a last /, get everything up to that point
474
+ dir = (last != -1) ? cleaned.substring(0, last) : '';
475
+ // make sure we aren't left with just https/ or file:/
476
+ return /^(https?:\/|file:\/)$/.test(dir) ? cleaned : dir;
477
+ },
478
+ // Returns everything after the last /
479
+ filename: function() {
480
+ var cleaned = this.clean(),
481
+ last = cleaned.lastIndexOf('/'),
482
+ filename = (last != -1) ? cleaned.substring(last+1, cleaned.length) : cleaned;
483
+ return /^(https?:\/|file:\/)$/.test(filename) ? cleaned : filename;
484
+ },
485
+ // Returns the domain for the current path.
486
+ // Returns null if the domain is a file.
487
+ domain: function() {
488
+ var http = this.path.match(/^(?:https?:\/\/)([^\/]*)/);
489
+ return http ? http[1] : null;
490
+ },
491
+ /**
492
+ * Joins a url onto a path. One way of understanding this is that your File object represents your current location, and calling join() is analogous to "cd" on a command line.
493
+ * @codestart
494
+ * new steal.File("d/e").join("../a/b/c"); // Yields the path "d/a/b/c"
495
+ * @codeend
496
+ * @param {String} url
497
+ */
498
+ join: function( url ) {
499
+ return File(url).joinFrom(this.path);
500
+ },
501
+
502
+ /**
503
+ * Returns the path of this file referenced from another url or path.
504
+ *
505
+ * new steal.File('a/b.c').joinFrom('/d/e')//-> /d/e/a/b.c
506
+ *
507
+ * @param {String} url
508
+ * @param {Boolean} expand if the path should be expanded
509
+ * @return {String}
510
+ */
511
+ joinFrom: function( url, expand ) {
512
+ var u = File(url);
513
+
514
+ // if this.path is absolutely referenced
515
+ if ( this.protocol() ) { //if we are absolutely referenced
516
+
517
+ //try to shorten the path as much as possible:
518
+ var firstDomain = this.domain(),
519
+ secondDomain = u.domain();
520
+
521
+ // if domains are equal, shorten
522
+ if ( firstDomain && firstDomain == secondDomain ) {
523
+
524
+ return this.toReferenceFromSameDomain(url);
525
+ } else {
526
+ // if there is no domain or not equal, use our path
527
+ return this.path;
528
+ }
529
+
530
+ // the path is the same as the folder the page is in
531
+ } else if ( url === steal.pageUrl().dir() && !expand ) {
532
+
533
+ return this.path;
534
+
535
+ } else if ( this.isLocalAbsolute() ) { // we are a path like /page.js
536
+
537
+ return (u.domain() ? u.protocol() + "//" + u.domain() : "" )+ this.path;
538
+
539
+ } else { //we have 2 relative paths, remove folders with every ../
540
+
541
+ if ( url === '' ) {
542
+ return this.path.replace(/\/$/, '');
543
+ }
544
+
545
+ var urls = url.split('/'),
546
+ paths = this.path.split('/'),
547
+ path = paths[0];
548
+
549
+ //if we are joining from a folder like cookbook/, remove the last empty part
550
+ if ( url.match(/\/$/) ) {
551
+ urls.pop();
552
+ }
553
+ // for each .. remove one folder
554
+ while ( path == '..' && paths.length > 0 ) {
555
+ // if we've emptied out, folders, just break
556
+ // leaving any additional ../s
557
+ if(! urls.pop() ){
558
+ break;
559
+ }
560
+ paths.shift();
561
+
562
+ path = paths[0];
563
+ }
564
+ return urls.concat(paths).join('/');
565
+ }
566
+ },
567
+ // Returns true if the file is relative to a domain or a protocol
568
+ relative: function() {
569
+ return this.path.match(/^(https?:|file:|\/)/) === null;
570
+ },
571
+ /**
572
+ * Returns the relative path between two paths with common folders.
573
+ * @codestart
574
+ * new steal.File('a/b/c/x/y').toReferenceFromSameDomain('a/b/c/d/e')//-> ../../x/y
575
+ * @codeend
576
+ * @param {Object} url
577
+ * @return {String}
578
+ */
579
+ toReferenceFromSameDomain: function( url ) {
580
+ var parts = this.path.split('/'),
581
+ other_parts = url.split('/'),
582
+ result = '';
583
+ while ( parts.length > 0 && other_parts.length > 0 && parts[0] == other_parts[0] ) {
584
+ parts.shift();
585
+ other_parts.shift();
586
+ }
587
+ each(other_parts, function(){ result += '../'; })
588
+ return result + parts.join('/');
589
+ },
590
+ /**
591
+ * Is the file on the same domain as our page.
592
+ */
593
+ isCrossDomain: function() {
594
+ return this.isLocalAbsolute() ? false : this.domain() != File(win.location.href).domain();
595
+ },
596
+ isLocalAbsolute: function() {
597
+ return this.path.indexOf('/') === 0;
598
+ },
599
+ protocol: function() {
600
+ var match = this.path.match(/^(https?:|file:)/);
601
+ return match && match[0];
602
+ },
603
+ /**
604
+ * For a given path, a given working directory, and file location, update the path so
605
+ * it points to a location relative to steal's root.
606
+ *
607
+ * We want everything relative to steal's root so the same app can work in multiple pages.
608
+ *
609
+ * ./files/a.js = steals a.js
610
+ * ./files/a = a/a.js
611
+ * files/a = //files/a/a.js
612
+ * files/a.js = loads //files/a.js
613
+ */
614
+ normalize: function() {
615
+
616
+ var current = File.cur().dir(),
617
+ //if you are cross domain from the page, and providing a path that doesn't have an domain
618
+ path = this.path;
619
+ if (/^\/\//.test(this.path)) { //if path is rooted from steal's root (DEPRECATED)
620
+ path = this.path.substr(2);
621
+ }
622
+ else if (/^\.\//.test(this.path)) { // should be relative
623
+ this.path = this.path.substr(2);
624
+ path = this.joinFrom(current);
625
+ this.path = "./" + this.path;
626
+ }
627
+ else if (/^[^\.|\/]/.test(this.path)) {}
628
+ else {
629
+ if (this.relative() ||
630
+ File.cur().isCrossDomain() && //if current file is on another domain and
631
+ !this.protocol()) { //this file doesn't have a protocol
632
+ path = this.joinFrom(current);
633
+ }
634
+ }
635
+
636
+ return path;
637
+ }
638
+ });
639
+
640
+ var pending = [],
641
+ s = steal,
642
+ id = 0,
643
+ steals = {};
644
+
645
+ // this is for methods on a 'steal instance'. A file can be in one of a few states:
646
+ // created - the steal instance is created, but we haven't started loading it yet
647
+ // this happens when thens are used
648
+ // loading - (loading=true) By calling load, this will tell steal to load a file
649
+ // loaded - (isLoaded=true) The file has been run, but its dependency files have been completed
650
+ // complete - all of this files dependencies have loaded and completed.
651
+ steal.p = {
652
+ // adds a new steal and throws an error if the script doesn't load
653
+ // this also checks the steals map
654
+ make: function(options){
655
+
656
+ var stel = new steal.p.init(options),
657
+ rootSrc = stel.options.rootSrc;
658
+
659
+ if(stel.unique && rootSrc){
660
+ // the .js is b/c we are not adding that automatically until
661
+ // load because we defer 'type' determination until then
662
+ if(!steals[rootSrc] && ! steals[rootSrc+".js"]){ //if we haven't loaded it before
663
+ steals[rootSrc] = stel;
664
+ } else{ // already have this steal
665
+ stel = steals[rootSrc];
666
+ // extend the old stolen file with any new options
667
+ extend(stel.options, typeof options === "string" ? {} : options)
668
+ }
669
+ }
670
+
671
+ return stel;
672
+ },
673
+ init: function( options ) {
674
+ this.dependencies = [];
675
+ // id for debugging
676
+ this.id = (++id);
677
+
678
+ // if we have no options, we are the global init ... set ourselves up ...
679
+ if(!options){ //global init cur ...
680
+ this.options = {};
681
+ this.waits = false;
682
+ this.pack = "production.js";
683
+ }
684
+ //handle callback functions
685
+ else if ( typeof options == 'function' ) {
686
+ var path = File.cur().path;
687
+
688
+ this.options = {
689
+ fn : function() {
690
+
691
+ //set the path ..
692
+ File.cur(path);
693
+
694
+ // call the function, someday soon this will be requireJS-like
695
+ options(steal.send || win.jQuery || steal);
696
+ },
697
+ rootSrc: path,
698
+ orig: options,
699
+ type: "fn"
700
+ }
701
+ // this has nothing to do with 'loading' options
702
+ this.waits = true;
703
+ this.unique = false;
704
+ } else {
705
+
706
+ // save the original options
707
+ this.orig = options;
708
+
709
+ this.options = steal.makeOptions(extend({},
710
+ typeof options == 'string' ? { src: options } : options));
711
+
712
+ this.waits = this.options.waits || false;
713
+ this.unique = true;
714
+ }
715
+ },
716
+ complete : function(){
717
+ this.completed = true;
718
+ },
719
+ /**
720
+ * @hide
721
+ * After the script has been loaded and run
722
+ *
723
+ * - check what else has been stolen, load them
724
+ * - mark yourself as complete when everything is completed
725
+ * - this is where all the actions is
726
+ */
727
+
728
+ loaded: function(script){
729
+ var myqueue,
730
+ stel,
731
+ src = (script && script.src) || this.options.src,
732
+ rootSrc = this.options.rootSrc;
733
+
734
+ //set yourself as the current file
735
+ File.cur(rootSrc);
736
+
737
+ // mark yourself as 'loaded'.
738
+ this.isLoaded = true;
739
+
740
+ // If we are IE, get the queue from interactives
741
+ // TODO move this out of this function
742
+ if (support.interactive && src) {
743
+ myqueue = interactives[src];
744
+ }
745
+ // is there a case in IE where, this makes sense?
746
+ // in other browsers, the queue of items to load is
747
+ // what is in pending
748
+ if(!myqueue){
749
+ myqueue = pending.slice(0);
750
+ pending = [];
751
+ }
752
+
753
+ // if we have nothing, mark us as complete (resolve if deferred)
754
+ if(!myqueue.length){
755
+ this.complete();
756
+ return;
757
+ }
758
+
759
+ // now we have to figure out how to wire up our pending steals
760
+ var self = this,
761
+ set = [],
762
+ // the current
763
+ joiner,
764
+ initial = [],
765
+
766
+ isProduction = steal.options.env == 'production',
767
+
768
+ files = [],
769
+
770
+ // a helper that basically does a join
771
+ // when everything in arr's func method is called,
772
+ // call func2 on obj
773
+ whenEach = function(arr, func, obj, func2){
774
+ var big = [obj, func2];
775
+ each(arr, function(i, item){
776
+ big.unshift(item, func)
777
+ });
778
+ when.apply(steal, big);
779
+ },
780
+ // a helper that does the oposite of a join. When
781
+ // obj's func method is called, call func2 on all items.
782
+ whenThe = function(obj, func, items, func2){
783
+ each(items, function(i, item){
784
+ when(obj, func, item, func2)
785
+ })
786
+ };
787
+
788
+
789
+ //now go through what you stole and hook everything up
790
+ //BUT, we go through backwards
791
+ each(myqueue.reverse(), function(i, item){
792
+
793
+ //in production, ignore ignored items (like steal/dev
794
+ if(isProduction && item.ignore){
795
+ return;
796
+ }
797
+
798
+ // make a steal object
799
+ stel = steal.p.make( item );
800
+
801
+ // add it as a dependency, circular are not allowed
802
+ self.dependencies.unshift(stel)
803
+
804
+
805
+ if(stel.waits === false){ // file
806
+ // on the current
807
+ files.push(stel);
808
+
809
+ }else{ // function
810
+
811
+ // essentially have to bind current files to call previous joiner's load
812
+ // and to wait for current stel's complete
813
+
814
+ if(!joiner){ // if no previous joiner, then we are at the start of a file
815
+
816
+ // when they are complete, complete the file
817
+ whenEach( files.concat(stel), "complete", self, "complete");
818
+
819
+ // if there was a function then files, then end, function loads all files
820
+ if(files.length){
821
+ whenThe(stel,"complete", files ,"load")
822
+ }
823
+
824
+ } else { // function, file1, file2, file3, joiner function
825
+
826
+ whenEach(files.concat(stel) , "complete", joiner, "load");
827
+
828
+ // make stel complete load files
829
+ whenThe(stel,"complete", files.length ? files : [joiner] ,"load")
830
+
831
+ }
832
+ // the joiner is the previous thing
833
+ joiner = stel;
834
+ files = [];
835
+
836
+ }
837
+ });
838
+
839
+ // now we should be left with the starting files
840
+ if(files.length){
841
+ // we have initial files
842
+ // if there is a joiner, we need to load it when the initial files are complete
843
+ if(joiner){
844
+ whenEach(files, "complete", joiner, "load");
845
+ } else {
846
+ whenEach(files, "complete", self, "complete");
847
+ }
848
+ // reverse it back and load each initial file
849
+ each(files.reverse(), function(){
850
+ this.load();
851
+ });
852
+ } else if(joiner){
853
+ // we have inital function
854
+ joiner.load()
855
+ } else {
856
+ // we had nothing
857
+ self.complete();
858
+ }
859
+
860
+ },
861
+ /**
862
+ * Loads this steal
863
+ */
864
+ load: function(returnScript) {
865
+ // if we are already loading / loaded
866
+ if(this.loading || this.isLoaded){
867
+ return;
868
+ }
869
+ this.loading = true;
870
+ var self = this;
871
+ // get yourself
872
+ steal.require(this.options, function load_calling_loaded(script){
873
+ self.loaded(script);
874
+ }, function(error, src){
875
+ win.clearTimeout && win.clearTimeout(self.completeTimeout)
876
+ throw "steal.js : "+self.options.src+" not completed"
877
+ });
878
+
879
+ }
880
+
881
+ };
882
+ steal.p.init.prototype = steal.p;
883
+ /**
884
+ * @add steal
885
+ */
886
+ // =============================== STATIC API ===============================
887
+ var page;
888
+ /**
889
+ * @static
890
+ */
891
+ extend(steal,{
892
+ /**
893
+ * @attribute root
894
+ * The location of the steal folder.
895
+ */
896
+ root : File(""),
897
+ /**
898
+ * Gets or sets the path from the current page to
899
+ * steal's (or JavaScriptMVC's) root folder. When passed a src, it sets the root folder.
900
+ * Otherwise, it returns the path to the root folder.
901
+ *
902
+ * This is the path from which
903
+ * all plugins are stolen. When you steal a plugin like steal("jquery/controller"),
904
+ * the plugin path is joined with this rootUrl to create a full path
905
+ * to the controller.js file.
906
+ *
907
+ * By default, the rootUrl is calculated from the
908
+ * steal script and the window location. For example, if the
909
+ * script tag looks like this:
910
+ *
911
+ * <script type='text/javascript' src='../../steal/steal.js?ui/app'></script>
912
+ *
913
+ * rootUrl will be set to "../../".
914
+ * Setting the rootUrl can be useful if you want to have
915
+ * steal.js in a different location.
916
+ *
917
+ * ## Example
918
+ *
919
+ * The following sets steal root to a different folder.
920
+ *
921
+ * steal.rootUrl("../../jmvc/")
922
+ *
923
+ * This appends <code>"../../jmvc"</code> to paths
924
+ * loaded from [steal.static.root]. In some strange cases this might be desirable if
925
+ * plugin folders are in a different location from the steal directory.
926
+ *
927
+ * It also sets the current url to this directory so the first calls to steal work relative to the root JMVC directory.
928
+ *
929
+ * @param {String} [src] a relative path from the current page to the root directory of JMVC, like ../../
930
+ * @return {String} returns the last path passed to rootUrl
931
+ */
932
+ rootUrl : function(src){
933
+ if (src !== undefined) {
934
+ steal.root = File(src);
935
+
936
+ // set cur with the location
937
+ var cleaned = steal.pageUrl(),
938
+ loc = cleaned.join(src);
939
+
940
+ File.cur( cleaned.toReferenceFromSameDomain(loc) );
941
+ return steal;
942
+ } else {
943
+ return steal.root.path;
944
+ }
945
+ },
946
+ extend : extend,
947
+ /**
948
+ * @function pageUrl
949
+ * Gets or sets the location of the page using
950
+ * steal. This defaults to the window's location.
951
+ *
952
+ * However, sometimes it is necessary to
953
+ * have steal believe it is making requests from
954
+ * another page.
955
+ *
956
+ * @param {String} [newPage] a path to the page using steal (probably the windows location)
957
+ * @return {steal.File} returns the last path to a page passed to pageUrl, converted to a steal.File object
958
+ */
959
+ pageUrl : function(newPage){
960
+ if(newPage){
961
+ page = File( File(newPage).clean() );
962
+ return steal;
963
+ } else{
964
+ return page || File("");
965
+ }
966
+ },
967
+ //gets and sets which page steal thinks it's at
968
+ // TODO: make location change-able ...
969
+ /**
970
+ * Gets the currently running script location.
971
+ *
972
+ * @return String
973
+ */
974
+ cur: function( file ) {
975
+ if (file === undefined) {
976
+ return File.cur();
977
+ } else {
978
+ File.cur(file);
979
+ return steal;
980
+ }
981
+ },
982
+ isRhino: win.load && win.readUrl && win.readFile,
983
+ /**
984
+ * @attribute options
985
+ * Configurable options
986
+ */
987
+ options : {
988
+ env : 'development',
989
+ // TODO: document this
990
+ loadProduction : true
991
+ },
992
+ /**
993
+ * @hide
994
+ * when a 'unique' steal gets added ...
995
+ * @param {Object} stel
996
+ */
997
+ add : function(stel){
998
+ steals[stel.rootSrc] = stel;
999
+ },
1000
+ /**
1001
+ * @hide
1002
+ * Makes options
1003
+ * @param {Object} options
1004
+ */
1005
+ makeOptions : function(options){
1006
+
1007
+ var ext = File(options.src).ext();
1008
+ if (!ext) {
1009
+ // if first character of path is a . or /, just load this file
1010
+ if (options.src.indexOf(".") == 0 || options.src.indexOf("/") == 0) {
1011
+ options.src = options.src + ".js"
1012
+ }
1013
+ // else, load as a plugin
1014
+ else {
1015
+ options.src = options.src + "/" + File(options.src).filename() + ".js";
1016
+ }
1017
+ }
1018
+
1019
+ var orig = options.src,
1020
+ // path relative to the current files path
1021
+ // this is done relative to jmvcroot
1022
+ normalized = steal.File(orig).normalize(),
1023
+ protocol = steal.File(options.src).protocol();
1024
+
1025
+ extend(options,{
1026
+ originalSrc : options.src,
1027
+ rootSrc : normalized,
1028
+ // path from the page
1029
+ src : steal.root.join(normalized),
1030
+ // "file:" or "http:" depending on what protocol the request uses
1031
+ protocol: protocol || (doc? location.protocol: "file:")
1032
+ });
1033
+ options.originalSrc = options.src;
1034
+
1035
+ return options;
1036
+ },
1037
+ /**
1038
+ * Calls steal, but waits until all previous steals
1039
+ * have completed loading until loading the
1040
+ * files passed to the arguments.
1041
+ */
1042
+ then : function(){
1043
+ var args = typeof arguments[0] == 'function' ?
1044
+ arguments : [function(){}].concat(makeArray( arguments ) )
1045
+ return steal.apply(win, args );
1046
+ },
1047
+ /**
1048
+ * Listens to events on Steal
1049
+ * @param {String} event
1050
+ * @param {Function} listener
1051
+ */
1052
+ bind: function(event, listener){
1053
+ if(!events[event]){
1054
+ events[event] = []
1055
+ }
1056
+ var special = steal.events[event]
1057
+ if(special && special.add){
1058
+ listener = special.add(listener);
1059
+ }
1060
+ listener && events[event].push(listener);
1061
+ return steal;
1062
+ },
1063
+ one : function(event, listener){
1064
+ steal.bind(event,function(){
1065
+ listener.apply(this, arguments);
1066
+ steal.unbind(event, arguments.callee);
1067
+ });
1068
+ return steal;
1069
+ },
1070
+ events : {},
1071
+ /**
1072
+ * Unbinds an event listener on steal
1073
+ * @param {Object} event
1074
+ * @param {Object} listener
1075
+ */
1076
+ unbind : function(event, listener){
1077
+ var evs = events[event] || [],
1078
+ i = 0;
1079
+ while(i < evs.length){
1080
+ if(listener === evs[i]){
1081
+ evs.splice(i,1);
1082
+ }else{
1083
+ i++;
1084
+ }
1085
+ }
1086
+ },
1087
+ trigger : function(event, arg){
1088
+ var arr = events[event] || [],
1089
+ copy = [];
1090
+ // array items might be removed during each iteration (with unbind), so we iterate over a copy
1091
+ for(var i =0, len = arr.length; i <len; i++){
1092
+ copy[i] = arr[i];
1093
+ }
1094
+ each(copy, function(i,f){
1095
+ f(arg);
1096
+ })
1097
+ },
1098
+ /**
1099
+ * @hide
1100
+ * Used to tell steal that it is loading a number of plugins
1101
+ */
1102
+ loading : function(){
1103
+ // we don't use IE's interactive script functionality while production scripts are loading
1104
+ useInteractive = false;
1105
+ each(arguments, function(i, arg){
1106
+ var stel = steal.p.make( arg );
1107
+ stel.loading = true;
1108
+ });
1109
+ },
1110
+ // a dummy function to add things to after the stel is created, but before
1111
+ // loaded is called
1112
+ preloaded : function(){},
1113
+ // called when a script has loaded via production
1114
+ loaded: function(name){
1115
+ // create the steal, mark it as loading, then as loaded
1116
+ var stel = steal.p.make( name );
1117
+ stel.loading = true;
1118
+ convert(stel, "complete");
1119
+
1120
+ steal.preloaded(stel);
1121
+ stel.loaded()
1122
+ return steal;
1123
+ }
1124
+ });
1125
+
1126
+ var events = {};
1127
+ startup = before(startup, function(){
1128
+
1129
+ steal.pageUrl(win.location ? win.location.href : "");
1130
+
1131
+ })
1132
+
1133
+
1134
+ // =============================== TYPE SYSTEM ===============================
1135
+
1136
+ var types = steal.types = {};
1137
+
1138
+
1139
+ steal.
1140
+ /**
1141
+ * Registers a type. You define the type of the file, the basic type it converts to, and a
1142
+ * conversion function where you convert the original file to JS or CSS. This is modeled after the
1143
+ * [http://api.jquery.com/extending-ajax/#Converters AJAX converters] in jQuery.
1144
+ *
1145
+ * Types are designed to make it simple to switch between steal's development and production modes. In development mode, the types are converted
1146
+ * in the browser to allow devs to see changes as they work. When the app is built, these converter functions are run by the build process,
1147
+ * and the processed text is inserted into the production script, optimized for performance.
1148
+ *
1149
+ * Here's an example converting files of type .foo to JavaScript. Foo is a fake language that saves global variables defined like. A .foo file might
1150
+ * look like this:
1151
+ *
1152
+ * REQUIRED FOO
1153
+ *
1154
+ * To define this type, you'd call steal.type like this:
1155
+ *
1156
+ * steal.type("foo js", function(options, original, success, error){
1157
+ * var parts = options.text.split(" ")
1158
+ * options.text = parts[0]+"='"+parts[1]+"'";
1159
+ * success();
1160
+ * });
1161
+ *
1162
+ * The method we provide is called with the text of .foo files in options.text. We parse the file, create
1163
+ * JavaScript and put it in options.text. Couldn't be simpler.
1164
+ *
1165
+ * Here's an example, converting [http://jashkenas.github.com/coffee-script/ coffeescript] to JavaScript:
1166
+ *
1167
+ * steal.type("coffee js", function(options, original, success, error){
1168
+ * options.text = CoffeeScript.compile(options.text);
1169
+ * success();
1170
+ * });
1171
+ *
1172
+ * In this example, any time steal encounters a file with
1173
+ * extension .coffee, it will call the given
1174
+ * converter method. CoffeeScript.compile takes the text of the file, converts it from coffeescript to javascript,
1175
+ * and saves the JavaScript text in options.text.
1176
+ *
1177
+ * Similarly, languages on top of CSS, like [http://lesscss.org/ LESS], can be converted to CSS:
1178
+ *
1179
+ * steal.type("less css", function(options, original, success, error){
1180
+ * new (less.Parser)({
1181
+ * optimization: less.optimization,
1182
+ * paths: []
1183
+ * }).parse(options.text, function (e, root) {
1184
+ * options.text = root.toCSS();
1185
+ * success();
1186
+ * });
1187
+ * });
1188
+ *
1189
+ * This simple type system could be used to convert any file type to be used in your JavaScript app. For example,
1190
+ * [http://fdik.org/yml/ yml] could be used for configuration. jQueryMX uses steal.type to support JS templates, such as EJS, TMPL, and others.
1191
+ *
1192
+ * @param {String} type A string that defines the new type being defined and the type being converted to,
1193
+ * separated by a space, like "coffee js".
1194
+ *
1195
+ * There can be more than two steps used in conversion, such as "ejs view
1196
+ * js". This will define a method that converts .ejs files to .view files. There should be another converter for "view js"
1197
+ * that makes this final conversion to JS.
1198
+ *
1199
+ * @param {Function} cb( options, original, success, error ) a callback function that converts the new file type
1200
+ * to a basic type. This method needs to do two things: 1) save the text of the converted file in options.text
1201
+ * and 2) call success() when the conversion is done (it can work asynchronously).
1202
+ *
1203
+ * - __options__ - the steal options for this file, including path information
1204
+ * - __original__ - the original argument passed to steal, which might be a path or a function
1205
+ * - __success__ - a method to call when the file is converted and processed successfully
1206
+ * - __error__ - a method called if the conversion fails or the file doesn't exist
1207
+ */
1208
+ type = function(type, cb){
1209
+ var typs = type.split(" ");
1210
+
1211
+ if(!cb){
1212
+ return types[typs.shift()].require
1213
+ }
1214
+
1215
+ types[typs.shift()] = {
1216
+ require : cb,
1217
+ convert: typs
1218
+ };
1219
+ };
1220
+ // adds a type (js by default) and buildType (css, js)
1221
+ // this should happen right before loading
1222
+ // however, what if urls are different
1223
+ // because one file has JS and another does not?
1224
+ // we could check if it matches something with .js because foo.less.js SHOULD
1225
+ // be rare
1226
+ steal.p.load = before(steal.p.load, function(){
1227
+ var raw = this.options;
1228
+
1229
+ // if it's a string, get it's extension and check if
1230
+ // it is a registered type, if it is ... set the type
1231
+ if(!raw.type){
1232
+ var ext = File(raw.src).ext();
1233
+ if(!ext && !types[ext]){
1234
+ ext = "js";
1235
+ }
1236
+ raw.type = ext;
1237
+ }
1238
+ if (!types[raw.type]){
1239
+ throw "steal.js - type " + raw.type + " has not been loaded.";
1240
+ }
1241
+ var converters = types[raw.type].convert;
1242
+ raw.buildType = converters.length ? converters[converters.length - 1] : raw.type;
1243
+ });
1244
+
1245
+ steal.
1246
+ /**
1247
+ * Called for every file that is loaded. It sets up a string of methods called for each type in the conversion chain and calls each type one by one.
1248
+ *
1249
+ * For example, if the file is a coffeescript file, here's what happens:
1250
+ *
1251
+ * - The "text" type converter is called first. This will perform an AJAX request for the file and save it in options.text.
1252
+ * - Then the coffee type converter is called (the user provided method). This converts the text from coffeescript to JavaScript.
1253
+ * - Finally the "js" type converter is called, which inserts the JavaScript in the page as a script tag that is executed.
1254
+ *
1255
+ * @param {Object} options the steal options for this file, including path information
1256
+ * @param {Function} success a method to call when the file is converted and processed successfully
1257
+ * @param {Function} error a method called if the conversion fails or the file doesn't exist
1258
+ */
1259
+ require = function(options, success, error){
1260
+ // get the type
1261
+ var type = types[options.type],
1262
+ converters;
1263
+
1264
+ // if this has converters, make it get the text first, then pass it to the type
1265
+ if(type.convert.length){
1266
+ converters = type.convert.slice(0);
1267
+ converters.unshift('text', options.type)
1268
+ } else {
1269
+ converters = [options.type]
1270
+ }
1271
+ require(options, converters, success, error)
1272
+ };
1273
+ function require(options, converters, success, error){
1274
+
1275
+ var type = types[converters.shift()];
1276
+
1277
+ type.require(options, function require_continue_check(){
1278
+ // if we have more types to convert
1279
+ if(converters.length){
1280
+ require(options, converters, success, error)
1281
+ } else { // otherwise this is the final
1282
+ success.apply(this, arguments);
1283
+ }
1284
+ }, error)
1285
+ };
1286
+
1287
+
1288
+ // =============================== TYPES ===============================
1289
+
1290
+ // a clean up script that prevents memory leaks and removes the
1291
+ // script
1292
+ var cleanUp = function(script) {
1293
+ script[ STR_ONREADYSTATECHANGE ]
1294
+ = script[ STR_ONLOAD ]
1295
+ = script[STR_ONERROR]
1296
+ = null;
1297
+
1298
+ head().removeChild( script );
1299
+ },
1300
+ // the last inserted script, needed for IE
1301
+ lastInserted,
1302
+ // if the state is done
1303
+ stateCheck = /loaded|complete/;
1304
+ steal.type("js", function(options, success, error){
1305
+ // create a script tag
1306
+ var script = scriptTag(),
1307
+ deps;
1308
+ // if we have text, just set and insert text
1309
+ if (options.text) {
1310
+ // insert
1311
+ script.text = options.text;
1312
+
1313
+ }
1314
+ else {
1315
+
1316
+ var callback = function(evt){
1317
+ if (!script.readyState || stateCheck.test(script.readyState)) {
1318
+ cleanUp(script);
1319
+ success(script);
1320
+ }
1321
+ }
1322
+ // listen to loaded
1323
+ if (support.attachEvent) {
1324
+ script.attachEvent(STR_ONREADYSTATECHANGE, callback)
1325
+ } else {
1326
+ script[STR_ONLOAD] = callback;
1327
+ }
1328
+
1329
+ // error handling doesn't work on firefox on the filesystem
1330
+ if (support.error && error && options.protocol !== "file:") {
1331
+ if(support.attachEvent){
1332
+ script.attachEvent(STR_ONERROR, error);
1333
+ } else {
1334
+ script[ STR_ONERROR ] = error;
1335
+ }
1336
+ }
1337
+ script.src = options.src;
1338
+ script.onSuccess = success;
1339
+ }
1340
+
1341
+ // insert the script
1342
+ lastInserted = script;
1343
+ head().insertBefore(script, head().firstChild);
1344
+
1345
+ // if text, just call success right away, and clean up
1346
+ if (options.text) {
1347
+ success();
1348
+ cleanUp(script);
1349
+ }
1350
+ });
1351
+
1352
+ steal.type("fn", function(options, success, error){
1353
+ success(options.fn());
1354
+ });
1355
+ steal.type("text", function(options, success, error){
1356
+ steal.request(options, function(text){
1357
+ options.text = text;
1358
+ success(text);
1359
+ }, error)
1360
+ });
1361
+
1362
+ var cssCount = 0,
1363
+ createSheet = doc && doc.createStyleSheet,
1364
+ lastSheet;
1365
+
1366
+ steal.type("css", function css_type(options, success, error){
1367
+ if(options.text){ // less
1368
+ var css = doc[STR_CREATE_ELEMENT]('style');
1369
+ css.type = 'text/css';
1370
+ if (css.styleSheet) { // IE
1371
+ css.styleSheet.cssText = options.text;
1372
+ } else {
1373
+ (function (node) {
1374
+ if (css.childNodes.length > 0) {
1375
+ if (css.firstChild.nodeValue !== node.nodeValue) {
1376
+ css.replaceChild(node, css.firstChild);
1377
+ }
1378
+ } else {
1379
+ css.appendChild(node);
1380
+ }
1381
+ })(doc.createTextNode(options.text));
1382
+ }
1383
+ head().appendChild(css);
1384
+ } else {
1385
+ if( createSheet ){
1386
+ // IE has a 31 sheet and 31 import per sheet limit
1387
+ if(cssCount == 30 || !lastSheet){
1388
+ lastSheet = document.createStyleSheet();
1389
+ cssCount = 0;
1390
+ }
1391
+
1392
+ lastSheet.addImport(options.src);
1393
+ cssCount++;
1394
+ } else {
1395
+ options = options || {};
1396
+ var link = doc[STR_CREATE_ELEMENT]('link');
1397
+ link.rel = options.rel || "stylesheet";
1398
+ link.href = options.src;
1399
+ link.type = 'text/css';
1400
+ head().appendChild(link);
1401
+ }
1402
+ }
1403
+
1404
+ success();
1405
+ });
1406
+
1407
+ // Overwrite
1408
+ if(opts.types){
1409
+ for(var type in opts.types){
1410
+ steal.type(type, opts.types[type]);
1411
+ }
1412
+ }
1413
+
1414
+
1415
+ // =============================== HELPERS ===============================
1416
+ var factory = function() {
1417
+ return win.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
1418
+ };
1419
+
1420
+
1421
+ steal.
1422
+ /**
1423
+ * Performs an XHR request
1424
+ * @param {Object} options
1425
+ * @param {Function} success
1426
+ * @param {Function} error
1427
+ */
1428
+ request = function(options, success, error){
1429
+ var request = new factory(),
1430
+ contentType = (options.contentType || "application/x-www-form-urlencoded; charset=utf-8"),
1431
+ clean = function(){
1432
+ request = check = clean = null;
1433
+ },
1434
+ check = function(){
1435
+ if ( request.readyState === 4 ) {
1436
+ if ( request.status === 500 || request.status === 404 ||
1437
+ request.status === 2 || request.status < 0 ||
1438
+ (request.status === 0 && request.responseText === '') ) {
1439
+ error && error(request.status);
1440
+ clean();
1441
+ } else {
1442
+ success(request.responseText);
1443
+ clean();
1444
+ }
1445
+ return;
1446
+ }
1447
+ };
1448
+
1449
+ request.open("GET", options.src, options.async === false ? false : true);
1450
+ request.setRequestHeader('Content-type', contentType);
1451
+ if ( request.overrideMimeType ) {
1452
+ request.overrideMimeType(contentType);
1453
+ }
1454
+
1455
+ request.onreadystatechange = function(){
1456
+ check();
1457
+ }
1458
+ try {
1459
+ request.send(null);
1460
+ }
1461
+ catch (e) {
1462
+ if (clean) {
1463
+ console.error(e);
1464
+ error && error();
1465
+ clean();
1466
+ }
1467
+ }
1468
+
1469
+ };
1470
+
1471
+
1472
+
1473
+
1474
+ // =============================== MAPPING ===============================
1475
+ var insertMapping = function(p){
1476
+ // don't worry about // rooted paths
1477
+ var mapName,
1478
+ map;
1479
+
1480
+ // go through mappings
1481
+ for(var mapName in steal.mappings){
1482
+ map = steal.mappings[mapName]
1483
+ if(map.test.test(p)){
1484
+ return p.replace(mapName, map.path);
1485
+ }
1486
+ }
1487
+ return p;
1488
+ };
1489
+ File.prototype.mapJoin = function( url ){
1490
+ url = insertMapping(url);
1491
+ return File(url).joinFrom(this.path);
1492
+ };
1493
+ // modifies src
1494
+ steal.makeOptions = after(steal.makeOptions,function(raw){
1495
+ raw.src = steal.root.join(raw.rootSrc = insertMapping(raw.rootSrc));
1496
+ });
1497
+
1498
+ //root mappings to other locations
1499
+ steal.mappings = {};
1500
+
1501
+ steal.
1502
+ /**
1503
+ * Maps a 'rooted' folder to another location.
1504
+ * @param {String|Object} from the location you want to map from. For example:
1505
+ * 'foo/bar'
1506
+ * @param {String} [to] where you want to map this folder too. Ex: 'http://foo.cdn/bar'
1507
+ * @return {steal}
1508
+ */
1509
+ map = function(from, to){
1510
+ if(typeof from == "string"){
1511
+ steal.mappings[from] = {
1512
+ test : new RegExp("^("+from+")([/.]|$)"),
1513
+ path: to
1514
+ };
1515
+ } else { // its an object
1516
+ for(var key in from){
1517
+ steal.map(key, from[key]);
1518
+ }
1519
+ }
1520
+ return this;
1521
+ }
1522
+
1523
+ // =============================== STARTUP ===============================
1524
+
1525
+
1526
+ var currentCollection;
1527
+
1528
+ // essentially ... we need to know when we are on our first steal
1529
+ // then we need to know when the collection of those steals ends ...
1530
+ // and, it helps if we use a 'collection' steal because of it's natural
1531
+ // use for going through the pending queue
1532
+ //
1533
+ extend(steal,{
1534
+ // called after steals are added to the pending queue
1535
+ after: function(){
1536
+ // if we don't have a current 'top' steal
1537
+ // we create one and set it up
1538
+ // to start loading its dependencies (the current pending steals)
1539
+ if(! currentCollection ){
1540
+ currentCollection = new steal.p.init();
1541
+
1542
+ // keep a reference in case it disappears
1543
+ var cur = currentCollection,
1544
+ // runs when a steal is starting
1545
+ go = function(){
1546
+ // indicates that a collection of steals has started
1547
+ steal.trigger("start", cur);
1548
+ when(cur,"complete", function(){
1549
+ steal.trigger("end", cur);
1550
+ });
1551
+ cur.loaded();
1552
+ };
1553
+ // if we are in rhino, start loading dependencies right away
1554
+ if(!win.setTimeout){
1555
+ go()
1556
+ }else{
1557
+ // otherwise wait a small timeout to make
1558
+ // sure we get all steals in the current file
1559
+ setTimeout(go,0)
1560
+ }
1561
+ }
1562
+ },
1563
+ _before : before,
1564
+ _after: after
1565
+ });
1566
+
1567
+ // this can probably move above
1568
+ steal.p.complete = before(steal.p.complete, function(){
1569
+ if(this === currentCollection){ // this is the last steal
1570
+ currentCollection = null;
1571
+ }
1572
+ });
1573
+
1574
+
1575
+
1576
+ // =============================== jQuery ===============================
1577
+ (function(){
1578
+ var jQueryIncremented = false,
1579
+ jQ,
1580
+ ready = false;
1581
+
1582
+ // check if jQuery loaded after every script load ...
1583
+ steal.p.loaded = before(steal.p.loaded, function(){
1584
+
1585
+ var $ = typeof jQuery !== "undefined" ? jQuery : null;
1586
+ if ($ && "readyWait" in $) {
1587
+
1588
+ //Increment jQuery readyWait if ncecessary.
1589
+ if (!jQueryIncremented) {
1590
+ jQ = $;
1591
+ $.readyWait += 1;
1592
+ jQueryIncremented = true;
1593
+ }
1594
+ }
1595
+ });
1596
+
1597
+ // once the current batch is done, fire ready if it hasn't already been done
1598
+ steal.bind('end', function(){
1599
+ if (jQueryIncremented && !ready) {
1600
+ jQ.ready(true);
1601
+ ready = true;
1602
+ }
1603
+ })
1604
+
1605
+
1606
+ })();
1607
+
1608
+ // =============================== ERROR HANDLING ===============================
1609
+
1610
+ steal.p.load = after(steal.p.load, function(stel){
1611
+ if(win.document && !this.completed && !this.completeTimeout && !steal.isRhino &&
1612
+ (this.options.protocol == "file:" || !support.error)){
1613
+ var self = this;
1614
+ this.completeTimeout = setTimeout(function(){
1615
+ throw "steal.js : "+self.options.src+" not completed"
1616
+ },5000);
1617
+ }
1618
+ });
1619
+ steal.p.complete = after(steal.p.complete, function(){
1620
+ this.completeTimeout && clearTimeout(this.completeTimeout)
1621
+ })
1622
+
1623
+
1624
+ // =============================== AOP ===============================
1625
+ function before(f, before, changeArgs){
1626
+ return changeArgs ?
1627
+ function before_changeArgs(){
1628
+ return f.apply(this,before.apply(this,arguments));
1629
+ }:
1630
+ function before_args(){
1631
+ before.apply(this,arguments);
1632
+ return f.apply(this,arguments);
1633
+ }
1634
+ }
1635
+ /**
1636
+ * Set up a function that runs after the first param.
1637
+ * @param {Object} f
1638
+ * @param {Object} after
1639
+ * @param {Object} changeRet If true, the result of the function will be passed to the after function as the first param. If false, the after function's params are the
1640
+ * same as the original function's params
1641
+ */
1642
+ function after(f, after, changeRet){
1643
+
1644
+ return changeRet ?
1645
+ function after_CRet(){
1646
+ return after.apply(this,[f.apply(this,arguments)].concat(makeArray(arguments)));
1647
+ }:
1648
+ function after_Ret(){
1649
+ var ret = f.apply(this,arguments);
1650
+ after.apply(this,arguments);
1651
+ return ret;
1652
+ }
1653
+ }
1654
+
1655
+ // converts a function to work with when
1656
+ function convert(ob, func){
1657
+
1658
+ var oldFunc = ob[func];
1659
+
1660
+ // if we don't have callbacks
1661
+ if(!ob[func].callbacks){
1662
+ //replace start with a function that will call ob2's method
1663
+ ob[func] = function(){
1664
+ var me = arguments.callee,
1665
+ ret;
1666
+
1667
+ // call the original function
1668
+ ret = oldFunc.apply(ob,arguments);
1669
+
1670
+ var cbs = me.callbacks,
1671
+ len = cbs.length;
1672
+
1673
+ //mark as called so any callees added to this caller will
1674
+ //automatically get called
1675
+ me.called = true;
1676
+ // call other callbacks
1677
+ for(var i =0; i < len; i++){
1678
+ cbs[i].called()
1679
+ }
1680
+ return ret;
1681
+
1682
+ }
1683
+ ob[func].callbacks = [];
1684
+ }
1685
+
1686
+ return ob[func];
1687
+ };
1688
+
1689
+ // maintains
1690
+ function join(obj, meth){
1691
+ this.obj = obj;
1692
+ this.meth = meth;
1693
+ convert(obj, meth);
1694
+ this.calls = 0
1695
+ };
1696
+
1697
+ extend(join.prototype,{
1698
+ called : function(){
1699
+ this.calls--;
1700
+ this.go();
1701
+ },
1702
+ // adds functions that will call this join
1703
+ add : function(obj, meth){
1704
+ // converts the function to be able to call
1705
+ // this join
1706
+ var f = convert(obj, meth);
1707
+ if(!f.called){
1708
+
1709
+ // adds us to the callback ... the callback will call
1710
+ // called
1711
+ f.callbacks.push(this);
1712
+ this.calls++;
1713
+ }
1714
+ },
1715
+ // call go every time the funtion is called
1716
+
1717
+ go : function(){
1718
+ if(this.calls === 0){
1719
+ this.obj[this.meth]()
1720
+ }
1721
+ }
1722
+ })
1723
+ // chains two functions. When the first one is called,
1724
+ // it calls the second function.
1725
+ // If the second function has multiple callers, it waits until all have been called
1726
+ //
1727
+ // when(parent,"start", steal, "start")
1728
+ //
1729
+ function when(){
1730
+ // handle if we get called with a function
1731
+ var args = makeArray(arguments),
1732
+ last = args[args.length -1];
1733
+
1734
+ if(typeof last === 'function' ){
1735
+ args[args.length -1] = {
1736
+ 'fn' : last
1737
+ }
1738
+ args.push("fn");
1739
+ };
1740
+
1741
+ var waitMeth = args.pop(),
1742
+ waitObj = args.pop(),
1743
+ joined = new join(waitObj, waitMeth);
1744
+
1745
+ for(var i =0; i < args.length; i = i+2){
1746
+ joined.add(args[i], args[i+1])
1747
+ }
1748
+
1749
+ // call right away if it should
1750
+ joined.go();
1751
+ }
1752
+
1753
+ // =========== DEBUG =========
1754
+
1755
+ /*var name = function(stel){
1756
+ if(stel.options && stel.options.type == "fn"){
1757
+ return stel.options.orig.toString().substr(0,50)
1758
+ }
1759
+ return stel.options ? stel.options.rootSrc : "CONTAINER"
1760
+ }
1761
+
1762
+
1763
+ steal.p.load = before(steal.p.load, function(){
1764
+ console.log("load", name(this), this.loading, this.id)
1765
+ })
1766
+
1767
+ steal.p.loaded = before(steal.p.loaded, function(){
1768
+ console.log("loaded", name(this), this.id)
1769
+ })
1770
+ steal.p.complete = before(steal.p.complete, function(){
1771
+ console.log("complete", name(this), this.id)
1772
+ })*/
1773
+
1774
+ // ============= WINDOW LOAD ========
1775
+ var addEvent = function(elem, type, fn) {
1776
+ if ( elem.addEventListener ) {
1777
+ elem.addEventListener( type, fn, false );
1778
+ } else if ( elem.attachEvent ) {
1779
+ elem.attachEvent( "on" + type, fn );
1780
+ } else {
1781
+ fn();
1782
+ }
1783
+ };
1784
+ var loaded = {
1785
+ load : function(){},
1786
+ end : function(){}
1787
+ };
1788
+
1789
+ var firstEnd = false;
1790
+ addEvent(win, "load", function(){
1791
+ loaded.load();
1792
+ });
1793
+ steal.one("end", function(collection){
1794
+ loaded.end();
1795
+ firstEnd = collection;
1796
+ steal.trigger("done", firstEnd)
1797
+ })
1798
+ when(loaded,"load",loaded,"end", function(){
1799
+ steal.trigger("ready")
1800
+ steal.isReady = true;
1801
+ });
1802
+
1803
+ steal.events.done = {
1804
+ add : function(cb){
1805
+ if(firstEnd){
1806
+ cb(firstEnd);
1807
+ return false;
1808
+ } else {
1809
+ return cb;
1810
+ }
1811
+ }
1812
+ };
1813
+
1814
+ // =========== HAS ARRAY STUFF ============
1815
+ // Logic that deals with files that have collections of other files within them. This is usually a production.css file,
1816
+ // which when it loads, needs to mark several CSS and LESS files it represents as being "loaded". This is done
1817
+ // by the production.js file having steal({src: "production.css", has: ["file1.css", "file2.css"]
1818
+
1819
+ // after a steal is created, if its been loaded already and has a "has", mark those files as loaded
1820
+ steal.p.make = after(steal.p.make, function(stel){
1821
+ // if we have things
1822
+ if( stel.options.has ) {
1823
+ // if we have loaded this already (and we are adding has's)
1824
+ if( stel.isLoaded ) {
1825
+ stel.loadHas();
1826
+ } else {
1827
+ // have to mark has as loading
1828
+ steal.loading.apply(steal,stel.options.has)
1829
+ }
1830
+ }
1831
+ return stel;
1832
+ }, true)
1833
+
1834
+ // if we're about to mark a file as loaded, mark its "has" array files as loaded also
1835
+ steal.p.loaded = before(steal.p.loaded, function(){
1836
+ if(this.options.has){
1837
+ this.loadHas();
1838
+ }
1839
+ })
1840
+
1841
+ steal.p.
1842
+ /**
1843
+ * @hide
1844
+ * Goes through the array of files listed in this.options.has, marks them all as loaded.
1845
+ * This is used for files like production.css, which, once they load, need to mark the files they
1846
+ * contain as loaded.
1847
+ */
1848
+ loadHas = function(){
1849
+ var stel, i,
1850
+ current = File.cur();
1851
+
1852
+ // mark everything in has loaded
1853
+ for(i=0; i<this.options.has.length; i++){
1854
+ // don't want the current file to change, since we're just marking files as loaded
1855
+ File.cur(current)
1856
+ stel = steal.p.make( this.options.has[i] );
1857
+ // need to set up a "complete" callback for this file, so later waits know its already
1858
+ // been completed
1859
+ convert(stel, "complete")
1860
+ stel.loaded();
1861
+
1862
+ }
1863
+ }
1864
+
1865
+ // =========== INTERACTIVE STUFF ===========
1866
+ // Logic that deals with making steal work with IE. IE executes scripts out of order, so in order to tell which scripts are
1867
+ // dependencies of another, steal needs to check which is the currently "interactive" script.
1868
+
1869
+
1870
+ var interactiveScript,
1871
+ // key is script name, value is array of pending items
1872
+ interactives = {},
1873
+ getInteractiveScript = function(){
1874
+ var i, script,
1875
+ scripts = doc[STR_GET_BY_TAG]('script');
1876
+ for (i = scripts.length - 1; i > -1 && (script = scripts[i]); i--) {
1877
+ if (script.readyState === 'interactive') {
1878
+ return script;
1879
+ }
1880
+ }
1881
+ },
1882
+ getCachedInteractiveScript = function() {
1883
+ var scripts, i, script;
1884
+ if (interactiveScript && interactiveScript.readyState === 'interactive') {
1885
+ return interactiveScript;
1886
+ }
1887
+
1888
+ if(script = getInteractiveScript()){
1889
+ interactiveScript = script;
1890
+ return script;
1891
+ }
1892
+
1893
+ // check last inserted
1894
+ if(lastInserted && lastInserted.readyState == 'interactive'){
1895
+ return lastInserted;
1896
+ }
1897
+
1898
+ return null;
1899
+ };
1900
+
1901
+ support.interactive = doc && !!getInteractiveScript();
1902
+
1903
+
1904
+ if (support.interactive) {
1905
+
1906
+ // after steal is called, check which script is "interactive" (for IE)
1907
+ steal.after = after(steal.after, function(){
1908
+ var interactive = getCachedInteractiveScript();
1909
+ // if no interactive script, this is a steal coming from inside a steal, let complete handle it
1910
+ if (!interactive || !interactive.src || /steal\.(production|production\.[a-zA-Z0-9\-\.\_]*)*js/.test(interactive.src)) {
1911
+ return;
1912
+ }
1913
+ // get the source of the script
1914
+ var src = interactive.src;
1915
+ // create an array to hold all steal calls for this script
1916
+ if (!interactives[src]) {
1917
+ interactives[src] = []
1918
+ }
1919
+ // add to the list of steals for this script tag
1920
+ if (src) {
1921
+ interactives[src].push.apply(interactives[src], pending);
1922
+ pending = [];
1923
+ }
1924
+ })
1925
+
1926
+ // This is used for packaged scripts. As the packaged script executes, we grab the
1927
+ // dependencies that have come so far and assign them to the loaded script
1928
+ steal.preloaded = before(steal.preloaded, function(stel){
1929
+ // get the src name
1930
+ var src = stel.options.src,
1931
+ // and the src of the current interactive script
1932
+ interactiveSrc = getCachedInteractiveScript().src;
1933
+
1934
+
1935
+ interactives[src] = interactives[interactiveSrc];
1936
+ interactives[interactiveSrc] = null;
1937
+ });
1938
+
1939
+ }
1940
+
1941
+ // =========== OPTIONS ==========
1942
+
1943
+ var stealCheck = /steal\.(production\.)?js.*/,
1944
+ getStealScriptSrc = function(){
1945
+ if(!doc){
1946
+ return;
1947
+ }
1948
+ var scripts = doc[STR_GET_BY_TAG]("script"),
1949
+ i = 0,
1950
+ len = scripts.length;
1951
+
1952
+
1953
+ //find the steal script and setup initial paths.
1954
+ for ( ; i < len; i++ ) {
1955
+ var src = scripts[i].src;
1956
+ if ( src && stealCheck.test(src) ) { //if script has steal.js
1957
+ return scripts[i];
1958
+ }
1959
+
1960
+ }
1961
+ return;
1962
+ };
1963
+ steal.getScriptOptions = function(script){
1964
+ var script = script || getStealScriptSrc(),
1965
+ src,
1966
+ scriptOptions,
1967
+ options = {},
1968
+ commaSplit;
1969
+
1970
+ if(script){
1971
+ var src = script.src,
1972
+ start = src.replace(stealCheck,"");
1973
+ if(/steal\/$/.test(start)){
1974
+ options.rootUrl = start.substr(0, start.length - 6);
1975
+ } else {
1976
+ options.rootUrl = start+"../"
1977
+ }
1978
+ if ( /steal\.production\.js/.test(src) ) {
1979
+ options.env = "production";
1980
+ }
1981
+ if ( src.indexOf('?') !== -1 ) {
1982
+
1983
+ scriptOptions = src.split('?')[1];
1984
+ commaSplit = scriptOptions.split(",");
1985
+
1986
+ if ( commaSplit[0] ) {
1987
+ options.startFile = commaSplit[0];
1988
+ }
1989
+ if ( commaSplit[1] && steal.options.env != "production" ) {
1990
+ options.env = commaSplit[1];
1991
+ }
1992
+
1993
+ }
1994
+
1995
+ }
1996
+ return options;
1997
+ };
1998
+
1999
+ startup = after(startup, function(){
2000
+ var options = steal.options,
2001
+ startFiles = [];
2002
+ extend(options, steal.getScriptOptions());
2003
+ // a steal that existed before this steal
2004
+ if(typeof oldsteal == 'object'){
2005
+ extend(options, oldsteal);
2006
+ }
2007
+
2008
+ // if it looks like steal[xyz]=bar, add those to the options
2009
+ var search = win.location && decodeURIComponent(win.location.search);
2010
+ search && search.replace(/steal\[([^\]]+)\]=([^&]+)/g, function( whoe, prop, val ) {
2011
+ // support array like params
2012
+ var commaSeparated = val.split(",");
2013
+ if(commaSeparated.length > 1){
2014
+ val = commaSeparated;
2015
+ }
2016
+ options[prop] = val;
2017
+ });
2018
+
2019
+ // CALCULATE CURRENT LOCATION OF THINGS ...
2020
+ steal.rootUrl(options.rootUrl);
2021
+
2022
+ // CLEAN UP OPTIONS
2023
+ // make startFile have .js ending
2024
+ if(options.startFile && options.startFile.indexOf(".") == '-1'){
2025
+ options.startFile = options.startFile + "/" + options.startFile.match(/[^\/]+$/)[0] + ".js";
2026
+ }
2027
+
2028
+ if(!options.logLevel){
2029
+ options.logLevel = 0;
2030
+ }
2031
+
2032
+ //calculate production location;
2033
+ if (!options.production && options.startFile ) {
2034
+ options.production = File(options.startFile).dir() + '/production.js';
2035
+ }
2036
+ if ( options.production ) {
2037
+ options.production = options.production + (options.production.indexOf('.js') == -1 ? '.js' : '');
2038
+ }
2039
+ each(options.loaded || [], function(i, stel){
2040
+ steal.loaded(stel)
2041
+ })
2042
+
2043
+ if(typeof options.startFiles === "string"){
2044
+ startFiles.push(options.startFiles);
2045
+ }
2046
+ else if(options.startFiles && options.startFiles.length){
2047
+ startFiles = options.startFiles;
2048
+ }
2049
+ var steals = [];
2050
+ // need to load startFiles in dev or production mode (to run funcunit in production)
2051
+ if( startFiles.length ){
2052
+ steal.options.startFiles = startFiles;
2053
+ steals.push.apply(steals, startFiles)
2054
+ }
2055
+ // either instrument is in this page (if we're the window opened from steal.browser), or its opener has it
2056
+ if ( options.instrument || (!options.browser && win.top && win.top.opener &&
2057
+ win.top.opener.steal && win.top.opener.steal.options.instrument) ) {
2058
+ // force startFiles to load before instrument
2059
+ steals.push(function(){}, {
2060
+ src: "steal/instrument",
2061
+ waits: true
2062
+ });
2063
+ }
2064
+ //we only load things with force = true
2065
+ if (options.env == 'production' && options.loadProduction) {
2066
+ if (options.production) {
2067
+ //steal(steal.options.startFile);
2068
+ steal({
2069
+ src: options.production,
2070
+ force: true
2071
+ });
2072
+ }
2073
+ }
2074
+ else {
2075
+ if (options.loadDev !== false) {
2076
+ steals.unshift({
2077
+ src: 'steal/dev/dev.js',
2078
+ ignore: true
2079
+ });
2080
+ }
2081
+
2082
+ if (options.startFile) {
2083
+ steals.push(options.startFile)
2084
+ }
2085
+ }
2086
+ if (steals.length) {
2087
+ steal.apply(null, steals);
2088
+ }
2089
+ });
2090
+
2091
+
2092
+ steal.when = when;
2093
+ // make steal public
2094
+ win.steal = steal;
2095
+
2096
+ startup();
2097
+
2098
+ })()