kojac 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +158 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +27 -0
  8. data/app/assets/javascripts/can_extensions.js +45 -0
  9. data/app/assets/javascripts/kojac.js +1230 -0
  10. data/app/assets/javascripts/kojac_canjs.js +191 -0
  11. data/app/assets/javascripts/kojac_ember.js +463 -0
  12. data/app/controllers/kojac_controller.rb +70 -0
  13. data/app/serializers/default_kojac_serializer.rb +10 -0
  14. data/diagram.odg +0 -0
  15. data/kojac.gemspec +36 -0
  16. data/lib/kojac/app_serialize.rb +29 -0
  17. data/lib/kojac/kojac_rails.rb +432 -0
  18. data/lib/kojac/ring_strong_parameters.rb +195 -0
  19. data/lib/kojac/version.rb +3 -0
  20. data/lib/kojac.rb +8 -0
  21. data/lib/tasks/kojac_tasks.rake +4 -0
  22. data/notes.txt +48 -0
  23. data/spec/.DS_Store +0 -0
  24. data/spec/can_cache_spec.js +87 -0
  25. data/spec/can_factory_spec.js +144 -0
  26. data/spec/can_model_spec.js +127 -0
  27. data/spec/demo/README.rdoc +261 -0
  28. data/spec/demo/Rakefile +7 -0
  29. data/spec/demo/app/assets/javascripts/application.js +15 -0
  30. data/spec/demo/app/assets/stylesheets/application.css +13 -0
  31. data/spec/demo/app/controllers/application_controller.rb +3 -0
  32. data/spec/demo/app/helpers/application_helper.rb +2 -0
  33. data/spec/demo/app/mailers/.gitkeep +0 -0
  34. data/spec/demo/app/models/.gitkeep +0 -0
  35. data/spec/demo/app/views/layouts/application.html.erb +14 -0
  36. data/spec/demo/config/application.rb +65 -0
  37. data/spec/demo/config/boot.rb +10 -0
  38. data/spec/demo/config/database.yml +25 -0
  39. data/spec/demo/config/environment.rb +5 -0
  40. data/spec/demo/config/environments/development.rb +37 -0
  41. data/spec/demo/config/environments/production.rb +67 -0
  42. data/spec/demo/config/environments/test.rb +37 -0
  43. data/spec/demo/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/demo/config/initializers/inflections.rb +15 -0
  45. data/spec/demo/config/initializers/mime_types.rb +5 -0
  46. data/spec/demo/config/initializers/secret_token.rb +7 -0
  47. data/spec/demo/config/initializers/session_store.rb +8 -0
  48. data/spec/demo/config/initializers/wrap_parameters.rb +14 -0
  49. data/spec/demo/config/locales/en.yml +5 -0
  50. data/spec/demo/config/routes.rb +58 -0
  51. data/spec/demo/config.ru +4 -0
  52. data/spec/demo/lib/assets/.gitkeep +0 -0
  53. data/spec/demo/log/.gitkeep +0 -0
  54. data/spec/demo/public/404.html +26 -0
  55. data/spec/demo/public/422.html +26 -0
  56. data/spec/demo/public/500.html +25 -0
  57. data/spec/demo/public/favicon.ico +0 -0
  58. data/spec/demo/script/rails +6 -0
  59. data/spec/ember_factory_spec.js +157 -0
  60. data/spec/ember_model_spec.js +179 -0
  61. data/spec/external/.DS_Store +0 -0
  62. data/spec/external/ember/.DS_Store +0 -0
  63. data/spec/external/ember/ember-1.0.0-rc.6.js +30970 -0
  64. data/spec/external/ember/handlebars-1.0.0-rc.4.js +2239 -0
  65. data/spec/external/jasmine/MIT.LICENSE +20 -0
  66. data/spec/external/jasmine/jasmine-html.js +616 -0
  67. data/spec/external/jasmine/jasmine.css +81 -0
  68. data/spec/external/jasmine/jasmine.js +2529 -0
  69. data/spec/external/jasmine.async.js +51 -0
  70. data/spec/external/jquery/jquery-1.7.2.js +9404 -0
  71. data/spec/external/jquery/jquery-1.7.2.min.js +4 -0
  72. data/spec/external/jquery/jquery-1.9.1.js +9597 -0
  73. data/spec/external/json2.js +480 -0
  74. data/spec/external/steal/steal-121115.js +2747 -0
  75. data/spec/external/steal/steal-3.2.3.js +2098 -0
  76. data/spec/external/steal/steal-3.2.3.min.js +27 -0
  77. data/spec/external/steal/steal.js +2466 -0
  78. data/spec/external/steal/steal.min.js +32 -0
  79. data/spec/external/steal/stealconfig.js +19 -0
  80. data/spec/external/underscore.js +1223 -0
  81. data/spec/external/underscore_plus.js +261 -0
  82. data/spec/external.zip +0 -0
  83. data/spec/handler_stack_spec.js +143 -0
  84. data/spec/helpers/SpecHelper.js +10 -0
  85. data/spec/kojac_caching_spec.js +105 -0
  86. data/spec/kojac_mock_spec.js +230 -0
  87. data/spec/kojac_model_spec.js +126 -0
  88. data/spec/kojac_object_spec.js +171 -0
  89. data/spec/kojac_operations_spec.js +41 -0
  90. data/spec/mockjson/order_item.js +37 -0
  91. data/spec/mockjson/order_item__49.js +15 -0
  92. data/spec/mockjson/order_item__50.js +15 -0
  93. data/spec/mockjson/order_item__51.js +15 -0
  94. data/spec/mockjson/product.js +82 -0
  95. data/spec/mockjson/product__3.js +22 -0
  96. data/spec/model_ring_spec.rb +52 -0
  97. data/spec/operation_include_spec.js +77 -0
  98. data/spec/run.html +81 -0
  99. data/spec/spec.js +2 -0
  100. data/spec/support/jasmine.yml +86 -0
  101. metadata +380 -0
@@ -0,0 +1,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
+ })()