marionette_dust 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f7b327b6b68458d0476694b8417306ac159c29fd
4
- data.tar.gz: b635707c5699b47414ff67bf85569a3ee9f06763
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTBhMmQyODYzYmM2Njk2MDU5Yjg4M2UyYzQxZDY3NjY3NWY5ZTYxOQ==
5
+ data.tar.gz: !binary |-
6
+ OGUyMDFlM2EwZWE2ZDU4NjJlZmExNGE2OTlmMDIyNTgwMWQyY2UwNA==
5
7
  SHA512:
6
- metadata.gz: cc88109dcbc9b9baef2eab4ecad52d1b346bc45b25a638ed232ef5f2feefbaa4713f3219282f40a1dfd53bd9af03490394a086e9f8689c1569c964d54e6000b6
7
- data.tar.gz: 2cbc754b5333c6e7db22330bbc6da98d6905890d74551bb911666a55e1ba904da9f59cb4fb2f3cb5b925ee46b6f824a6395a7ad3e5854f18b48719d76911c962
8
+ metadata.gz: !binary |-
9
+ MGZiNzVjNWUyOTc5YjNlNDdjZmI1MjM1ZTczM2Y2YTQ4NTFjNmE3ZDI1OTNj
10
+ OTgzNzM4YzRmZDA0YWI0NWQwYzM0MjFlOTRjNjZhNDE0NDY4ODE5MWFmM2Qw
11
+ MTFkMTRiOWY0N2NjYmFhYjk1Y2M2YjIzMTk0MTY2ZTUwNmRmNTc=
12
+ data.tar.gz: !binary |-
13
+ ODE3OGQ2ZjhiZTcwMzgwZDg3MTg2N2QyYTk0ZDRjM2RmZTQ3ZTEzOGVjYjU1
14
+ ZDMxN2JiMzY0ZjhkNGU2ZTI3ZGNlODg5MmFjZGQ3ZjA4ZDJlYzFjNTZhZjg1
15
+ N2IzODEyZjA4MWMwNzJmZTY0ZmNhNjQ5ZmMxYWM3NmY5MGE2NjE=
data/README.md CHANGED
@@ -104,7 +104,7 @@ Also this project is inspired by:
104
104
 
105
105
  [Backbone on rails][7] and [Dust assets][6]
106
106
 
107
-
107
+ Special thanks to [@sauron][8] for making the test suite possible
108
108
 
109
109
  Contributing
110
110
  -------
@@ -115,7 +115,7 @@ Contributing
115
115
  4. Push to the branch (`git push origin my-new-feature`)
116
116
  5. Create new Pull Request
117
117
 
118
- [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/0fcf36aa176a3fc30ebbec87bf7b28d2 "githalytics.com")](http://githalytics.com/RobertoDip/marionette_dust-rails)
118
+ [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/0fcf36aa176a3fc30ebbec87bf7b28d2 "githalytics.com")](http://githalytics.com/roperzh/marionette_dust-rails)
119
119
 
120
120
  [1]: http://marionettejs.com/
121
121
  [2]: http://linkedin.github.io/dustjs/
@@ -124,13 +124,16 @@ Contributing
124
124
  [5]: http://www.backbonerails.com/
125
125
  [6]: https://github.com/hasmanydevelopers/dust_assets
126
126
  [7]: https://github.com/meleyal/backbone-on-rails
127
+ [8]: https://github.com/sauron
127
128
 
128
- [Build Status]: https://travis-ci.org/RobertoDip/marionette_dust-rails
129
- [Code Climate]: https://codeclimate.com/github/RobertoDip/marionette_dust-rails
130
- [Gemnasium]: https://gemnasium.com/RobertoDip/marionette_dust-rails
129
+ [Build Status]: https://travis-ci.org/roperzh/marionette_dust-rails
130
+ [Code Climate]: https://codeclimate.com/github/roperzh/marionette_dust-rails
131
+ [Gemnasium]: https://gemnasium.com/roperzh/marionette_dust-rails
131
132
  [Gem Version]: https://rubygems.org/gems/marionette_dust
132
133
 
133
- [BS img]: https://travis-ci.org/RobertoDip/marionette_dust-rails.png
134
- [CC img]: https://codeclimate.com/github/RobertoDip/marionette_dust-rails.png
135
- [GM img]: https://gemnasium.com/RobertoDip/marionette_dust-rails.png
134
+ [BS img]: https://travis-ci.org/roperzh/marionette_dust-rails.png
135
+ [CC img]: https://codeclimate.com/github/roperzh/marionette_dust-rails.png
136
+ [GM img]: https://gemnasium.com/roperzh/marionette_dust-rails.png
136
137
  [GV img]: https://badge.fury.io/rb/marionette_dust.png
138
+
139
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/roperzh/marionette_dust-rails/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
@@ -8,7 +8,7 @@ module MarionetteDust
8
8
  context.call('dust.compile', src, name)
9
9
  end
10
10
 
11
- private
11
+ private
12
12
 
13
13
  def context
14
14
  @context ||= ExecJS.compile(source)
@@ -1,3 +1,3 @@
1
- <%= app_name %>.module("<%= file_name.capitalize %>.<%= sub_app_scope %>", function (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) {
1
+ <%= app_name %>.module("<%= @parent_name.camelize %>.<%= file_name.camelize %>", function (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) {
2
2
  <%= sub_app_scope %>.Controller = {};
3
3
  });
@@ -1,2 +1,2 @@
1
- <%= app_name %>.module "<%= file_name.capitalize %>.<%= sub_app_scope %>", (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) ->
1
+ <%= app_name %>.module "<%= @parent_name.camelize %>.<%= file_name.camelize %>", (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) ->
2
2
  <%= sub_app_scope %>.Controller = {}
@@ -1,5 +1,5 @@
1
- <%= app_name %>.module("<%= file_name.capitalize %>.<%= sub_app_scope %>", function (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) {
1
+ <%= app_name %>.module("<%= @parent_name.camelize %>.<%= file_name.camelize %>", function (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) {
2
2
  <%= sub_app_scope %>.View = Marionette.ItemView.extend({
3
- template: "templates/<%= @submodule_name %>.jst.dust"
3
+ template: "<%= @submodule_name %>.jst.dust"
4
4
  });
5
5
  });
@@ -1,3 +1,3 @@
1
1
  <%= app_name %>.module "<%= file_name.capitalize %>.<%= sub_app_scope %>", (<%= sub_app_scope %>, <%= app_name %>, Backbone, Marionette, $, _) ->
2
2
 
3
- <%= sub_app_scope %>.View = Marionette.ItemView.extend(template: "templates/<%= @submodule_name %>.jst.dust")
3
+ <%= sub_app_scope %>.View = Marionette.ItemView.extend(template: "<%= @submodule_name %>.jst.dust")
@@ -23,15 +23,15 @@ module MarionetteDust
23
23
  end
24
24
 
25
25
  def singular_file_name
26
- "#{file_name.singularize}#{@ext}"
26
+ "#{file_name.singularize}#{extension}"
27
27
  end
28
28
 
29
29
  def plural_file_name
30
- "#{file_name.pluralize}#{@ext}"
30
+ "#{file_name.pluralize}#{extension}"
31
31
  end
32
32
 
33
33
  def asset_file_name(type)
34
- "#{@submodule_name.downcase.singularize}_#{type}#{@ext}"
34
+ "#{@submodule_name.downcase.singularize}_#{type}#{extension}"
35
35
  end
36
36
 
37
37
  def singular_entity_name
@@ -43,29 +43,33 @@ module MarionetteDust
43
43
  end
44
44
 
45
45
  def sub_app_name
46
- [file_name.pluralize.camelize, "App"].join("")
46
+ [file_name.camelize, "App"].join("")
47
47
  end
48
48
 
49
49
  def sub_app_file_name
50
- [file_name.singularize.downcase, "_app", "#{@ext}"].join("")
50
+ [file_name.singularize.downcase, "_app", "#{extension}"].join("")
51
51
  end
52
52
 
53
53
  def sub_app_scope
54
- @submodule_name.capitalize
54
+ @submodule_name.camelize
55
55
  end
56
56
 
57
- def app_name
58
- rails_app_name.camelize
57
+ def extension
58
+ @ext ||= options.coffeescript ? ".js.coffee" : ".js"
59
59
  end
60
60
 
61
- def app_filename
62
- rails_app_name.underscore
61
+ def app_name
62
+ rails_app_name.camelize
63
63
  end
64
64
 
65
65
  def rails_app_name
66
66
  Rails.application.class.name.split('::').first
67
67
  end
68
68
 
69
+ def trackeable_directory(path)
70
+ empty_directory path
71
+ template ".gitkeep", "#{path}/.gitkeep"
72
+ end
69
73
  end
70
74
  end
71
75
  end
@@ -22,15 +22,13 @@ module Md
22
22
  desc: "Javascript manifest file to modify (or create)"
23
23
 
24
24
  def create_dir_layout
25
- empty_directory entities_path
26
- empty_directory template_path
27
- empty_directory apps_path
25
+ trackeable_directory entities_path
26
+ trackeable_directory template_path
27
+ trackeable_directory apps_path
28
28
  end
29
29
 
30
30
  def create_app_file
31
- coffee = options.coffeescript
32
- ext = coffee ? ".js.coffee" : ".js"
33
- template "app#{ext}", "#{javascript_path}/app#{ext}"
31
+ template "app#{extension}", "#{javascript_path}/app#{extension}"
34
32
  end
35
33
 
36
34
  def inject_required_files
@@ -21,20 +21,15 @@ module Md
21
21
  default: "",
22
22
  desc: ""
23
23
 
24
- def parse_options
25
- coffee = options.coffeescript
26
- @ext = coffee ? ".js.coffee" : ".js"
27
- end
28
-
29
24
  def create_marionette_entity
30
25
  file = File.join(entities_path, singular_file_name)
31
- template "entity#{@ext}", file
26
+ template "entity#{extension}", file
32
27
  end
33
28
 
34
29
  def create_marionette_app
35
30
  empty_directory File.join(apps_path, file_name.downcase)
36
31
  file = File.join(apps_path, file_name.downcase, sub_app_file_name)
37
- template "app#{@ext}", file
32
+ template "app#{extension}", file
38
33
  end
39
34
 
40
35
  def create_subapp
@@ -48,10 +43,10 @@ module Md
48
43
  end
49
44
  end
50
45
 
51
- protected
46
+ protected
52
47
  def create_asset(type)
53
48
  file = File.join(apps_path, file_name.downcase, @submodule_name.downcase, asset_file_name(type))
54
- template "#{type}#{@ext}", file
49
+ template "#{type}#{extension}", file
55
50
  end
56
51
 
57
52
  def create_dust_template
@@ -23,8 +23,6 @@ module Md
23
23
  desc: "Parent app (required)"
24
24
 
25
25
  def parse_options
26
- coffee = options.coffeescript
27
- @ext = coffee ? ".js.coffee" : ".js"
28
26
  @parent_name = options.parent
29
27
  @submodule_name = file_name
30
28
  end
@@ -35,15 +33,21 @@ module Md
35
33
  create_dust_template
36
34
  end
37
35
 
38
- protected
36
+ protected
39
37
  def create_asset(type)
40
- file = File.join(apps_path, @parent_name.downcase, file_name, asset_file_name(type))
41
- template "#{type}#{@ext}", file
38
+ file = File.join(apps_path, @parent_name.underscore, file_name,
39
+ asset_file_name(type))
40
+
41
+ template "#{type}#{extension}", file
42
42
  end
43
43
 
44
44
  def create_dust_template
45
- empty_directory File.join(template_path, @parent_name, @submodule_name)
46
- file = File.join(template_path, @parent_name, @submodule_name, "#{@submodule_name}.jst.dust")
45
+ empty_directory File.join(template_path, @parent_name.underscore,
46
+ @submodule_name.underscore)
47
+
48
+ file = File.join(template_path, @parent_name.underscore, @submodule_name,
49
+ "#{@submodule_name}.jst.dust")
50
+
47
51
  template "template.jst.dust", file
48
52
  end
49
53
  end
@@ -1,3 +1,3 @@
1
1
  module MarionetteDust
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,8 +1,8 @@
1
1
  // MarionetteJS (Backbone.Marionette)
2
2
  // ----------------------------------
3
- // v1.2.2
3
+ // v1.6.2
4
4
  //
5
- // Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
5
+ // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
6
6
  // Distributed under MIT license
7
7
  //
8
8
  // http://marionettejs.com
@@ -33,7 +33,7 @@
33
33
  // shut down child views.
34
34
 
35
35
  Backbone.ChildViewContainer = (function(Backbone, _){
36
-
36
+
37
37
  // Container Constructor
38
38
  // ---------------------
39
39
 
@@ -158,9 +158,9 @@ Backbone.ChildViewContainer = (function(Backbone, _){
158
158
  //
159
159
  // Mix in methods from Underscore, for iteration, and other
160
160
  // collection related features.
161
- var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
162
- 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
163
- 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
161
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
162
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
163
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
164
164
  'last', 'without', 'isEmpty', 'pluck'];
165
165
 
166
166
  _.each(methods, function(method) {
@@ -195,14 +195,14 @@ Backbone.Wreqr = (function(Backbone, Marionette, _){
195
195
 
196
196
  Wreqr.Handlers = (function(Backbone, _){
197
197
  "use strict";
198
-
198
+
199
199
  // Constructor
200
200
  // -----------
201
201
 
202
202
  var Handlers = function(options){
203
203
  this.options = options;
204
204
  this._wreqrHandlers = {};
205
-
205
+
206
206
  if (_.isFunction(this.initialize)){
207
207
  this.initialize(options);
208
208
  }
@@ -308,7 +308,7 @@ Wreqr.CommandStorage = (function(){
308
308
 
309
309
  // build the configuration
310
310
  commands = {
311
- command: commandName,
311
+ command: commandName,
312
312
  instances: []
313
313
  };
314
314
 
@@ -491,16 +491,35 @@ Marionette.getOption = function(target, optionName){
491
491
  return value;
492
492
  };
493
493
 
494
+ // Marionette.normalizeMethods
495
+ // ----------------------
496
+
497
+ // Pass in a mapping of events => functions or function names
498
+ // and return a mapping of events => functions
499
+ Marionette.normalizeMethods = function(hash) {
500
+ var normalizedHash = {}, method;
501
+ _.each(hash, function(fn, name) {
502
+ method = fn;
503
+ if (!_.isFunction(method)) {
504
+ method = this[method];
505
+ }
506
+ if (!method) {
507
+ return;
508
+ }
509
+ normalizedHash[name] = method;
510
+ }, this);
511
+ return normalizedHash;
512
+ };
494
513
  // Trigger an event and/or a corresponding method name. Examples:
495
514
  //
496
515
  // `this.triggerMethod("foo")` will trigger the "foo" event and
497
516
  // call the "onFoo" method.
498
517
  //
499
- // `this.triggerMethod("foo:bar") will trigger the "foo:bar" event and
518
+ // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
500
519
  // call the "onFooBar" method.
501
520
  Marionette.triggerMethod = (function(){
502
521
 
503
- // split the event name on the :
522
+ // split the event name on the ":"
504
523
  var splitter = /(^|:)(\w)/gi;
505
524
 
506
525
  // take the event section ("section1:section2:section3")
@@ -509,7 +528,7 @@ Marionette.triggerMethod = (function(){
509
528
  return eventName.toUpperCase();
510
529
  }
511
530
 
512
- // actual triggerMethod name
531
+ // actual triggerMethod implementation
513
532
  var triggerMethod = function(event) {
514
533
  // get the method name from the event name
515
534
  var methodName = 'on' + event.replace(splitter, getEventName);
@@ -537,7 +556,7 @@ Marionette.triggerMethod = (function(){
537
556
  // in the DOM, trigger a "dom:refresh" event every time it is
538
557
  // re-rendered.
539
558
 
540
- Marionette.MonitorDOMRefresh = (function(){
559
+ Marionette.MonitorDOMRefresh = (function(documentElement){
541
560
  // track when the view has been shown in the DOM,
542
561
  // using a Marionette.Region (or by other means of triggering "show")
543
562
  function handleShow(view){
@@ -553,13 +572,17 @@ Marionette.MonitorDOMRefresh = (function(){
553
572
 
554
573
  // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method
555
574
  function triggerDOMRefresh(view){
556
- if (view._isShown && view._isRendered){
575
+ if (view._isShown && view._isRendered && isInDOM(view)){
557
576
  if (_.isFunction(view.triggerMethod)){
558
577
  view.triggerMethod("dom:refresh");
559
578
  }
560
579
  }
561
580
  }
562
581
 
582
+ function isInDOM(view) {
583
+ return documentElement.contains(view.el);
584
+ }
585
+
563
586
  // Export public API
564
587
  return function(view){
565
588
  view.listenTo(view, "show", function(){
@@ -570,14 +593,14 @@ Marionette.MonitorDOMRefresh = (function(){
570
593
  handleRender(view);
571
594
  });
572
595
  };
573
- })();
596
+ })(document.documentElement);
574
597
 
575
598
 
576
599
  // Marionette.bindEntityEvents & unbindEntityEvents
577
600
  // ---------------------------
578
601
  //
579
- // These methods are used to bind/unbind a backbone "entity" (collection/model)
580
- // to methods on a target object.
602
+ // These methods are used to bind/unbind a backbone "entity" (collection/model)
603
+ // to methods on a target object.
581
604
  //
582
605
  // The first parameter, `target`, must have a `listenTo` method from the
583
606
  // EventBinder object.
@@ -587,7 +610,7 @@ Marionette.MonitorDOMRefresh = (function(){
587
610
  //
588
611
  // The third parameter is a hash of { "event:name": "eventHandler" }
589
612
  // configuration. Multiple handlers can be separated by a space. A
590
- // function can be supplied instead of a string handler name.
613
+ // function can be supplied instead of a string handler name.
591
614
 
592
615
  (function(Marionette){
593
616
  "use strict";
@@ -629,7 +652,7 @@ Marionette.MonitorDOMRefresh = (function(){
629
652
  target.stopListening(entity, evt, method, target);
630
653
  }
631
654
 
632
-
655
+
633
656
  // generic looping function
634
657
  function iterateEvents(target, entity, bindings, functionCallback, stringCallback){
635
658
  if (!entity || !bindings) { return; }
@@ -642,7 +665,7 @@ Marionette.MonitorDOMRefresh = (function(){
642
665
  // iterate the bindings and bind them
643
666
  _.each(bindings, function(methods, evt){
644
667
 
645
- // allow for a function as the handler,
668
+ // allow for a function as the handler,
646
669
  // or a list of event names as a string
647
670
  if (_.isFunction(methods)){
648
671
  functionCallback(target, entity, evt, methods);
@@ -652,7 +675,7 @@ Marionette.MonitorDOMRefresh = (function(){
652
675
 
653
676
  });
654
677
  }
655
-
678
+
656
679
  // Export Public API
657
680
  Marionette.bindEntityEvents = function(target, entity, bindings){
658
681
  iterateEvents(target, entity, bindings, bindToFunction, bindFromStrings);
@@ -679,7 +702,7 @@ Marionette.Callbacks = function(){
679
702
  _.extend(Marionette.Callbacks.prototype, {
680
703
 
681
704
  // Add a callback to be executed. Callbacks added here are
682
- // guaranteed to execute, even if they are added after the
705
+ // guaranteed to execute, even if they are added after the
683
706
  // `run` method is called.
684
707
  add: function(callback, contextOverride){
685
708
  this._callbacks.push({cb: callback, ctx: contextOverride});
@@ -690,8 +713,8 @@ _.extend(Marionette.Callbacks.prototype, {
690
713
  });
691
714
  },
692
715
 
693
- // Run all registered callbacks with the context specified.
694
- // Additional callbacks can be added after this has been run
716
+ // Run all registered callbacks with the context specified.
717
+ // Additional callbacks can be added after this has been run
695
718
  // and they will still be executed.
696
719
  run: function(options, context){
697
720
  this._deferred.resolve(context, options);
@@ -703,7 +726,7 @@ _.extend(Marionette.Callbacks.prototype, {
703
726
  var callbacks = this._callbacks;
704
727
  this._deferred = Marionette.$.Deferred();
705
728
  this._callbacks = [];
706
-
729
+
707
730
  _.each(callbacks, function(cb){
708
731
  this.add(cb.cb, cb.ctx);
709
732
  }, this);
@@ -740,7 +763,7 @@ _.extend(Marionette.Controller.prototype, Backbone.Events, {
740
763
  }
741
764
  });
742
765
 
743
- // Region
766
+ // Region
744
767
  // ------
745
768
  //
746
769
  // Manage the visual regions of your composite application. See
@@ -748,7 +771,6 @@ _.extend(Marionette.Controller.prototype, Backbone.Events, {
748
771
 
749
772
  Marionette.Region = function(options){
750
773
  this.options = options || {};
751
-
752
774
  this.el = Marionette.getOption(this, "el");
753
775
 
754
776
  if (!this.el){
@@ -784,7 +806,6 @@ _.extend(Marionette.Region, {
784
806
  // ```
785
807
  //
786
808
  buildRegion: function(regionConfig, defaultRegionType){
787
-
788
809
  var regionIsString = (typeof regionConfig === "string");
789
810
  var regionSelectorIsString = (typeof regionConfig.selector === "string");
790
811
  var regionTypeIsUndefined = (typeof regionConfig.regionType === "undefined");
@@ -795,19 +816,20 @@ _.extend(Marionette.Region, {
795
816
  }
796
817
 
797
818
  var selector, RegionType;
798
-
819
+
799
820
  // get the selector for the region
800
-
821
+
801
822
  if (regionIsString) {
802
823
  selector = regionConfig;
803
- }
824
+ }
804
825
 
805
826
  if (regionConfig.selector) {
806
827
  selector = regionConfig.selector;
828
+ delete regionConfig.selector;
807
829
  }
808
830
 
809
831
  // get the type for the region
810
-
832
+
811
833
  if (regionIsType){
812
834
  RegionType = regionConfig;
813
835
  }
@@ -818,12 +840,17 @@ _.extend(Marionette.Region, {
818
840
 
819
841
  if (regionConfig.regionType) {
820
842
  RegionType = regionConfig.regionType;
843
+ delete regionConfig.regionType;
844
+ }
845
+
846
+ if (regionIsString || regionIsType) {
847
+ regionConfig = {};
821
848
  }
822
-
849
+
850
+ regionConfig.el = selector;
851
+
823
852
  // build the region instance
824
- var region = new RegionType({
825
- el: selector
826
- });
853
+ var region = new RegionType(regionConfig);
827
854
 
828
855
  // override the `getEl` function if we have a parentEl
829
856
  // this must be overridden to ensure the selector is found
@@ -832,7 +859,6 @@ _.extend(Marionette.Region, {
832
859
  // literal to build the region, the element will not be
833
860
  // guaranteed to be in the DOM already, and will cause problems
834
861
  if (regionConfig.parentEl){
835
-
836
862
  region.getEl = function(selector) {
837
863
  var parentEl = regionConfig.parentEl;
838
864
  if (_.isFunction(parentEl)){
@@ -858,11 +884,9 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
858
884
  // `onShow` and `close` method on your view, just after showing
859
885
  // or just before closing the view, respectively.
860
886
  show: function(view){
861
-
862
887
  this.ensureEl();
863
888
 
864
889
  var isViewClosed = view.isClosed || _.isUndefined(view.$el);
865
-
866
890
  var isDifferentView = view !== this.currentView;
867
891
 
868
892
  if (isDifferentView) {
@@ -874,7 +898,7 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
874
898
  if (isDifferentView || isViewClosed) {
875
899
  this.open(view);
876
900
  }
877
-
901
+
878
902
  this.currentView = view;
879
903
 
880
904
  Marionette.triggerMethod.call(this, "show", view);
@@ -909,13 +933,13 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
909
933
  if (view.close) { view.close(); }
910
934
  else if (view.remove) { view.remove(); }
911
935
 
912
- Marionette.triggerMethod.call(this, "close");
936
+ Marionette.triggerMethod.call(this, "close", view);
913
937
 
914
938
  delete this.currentView;
915
939
  },
916
940
 
917
- // Attach an existing view to the region. This
918
- // will not call `render` or `onShow` for the new view,
941
+ // Attach an existing view to the region. This
942
+ // will not call `render` or `onShow` for the new view,
919
943
  // and will not replace the current HTML for the `el`
920
944
  // of the region.
921
945
  attachView: function(view){
@@ -1052,9 +1076,9 @@ Marionette.RegionManager = (function(Marionette){
1052
1076
  //
1053
1077
  // Mix in methods from Underscore, for iteration, and other
1054
1078
  // collection related features.
1055
- var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
1056
- 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
1057
- 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
1079
+ var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
1080
+ 'select', 'reject', 'every', 'all', 'some', 'any', 'include',
1081
+ 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
1058
1082
  'last', 'without', 'isEmpty', 'pluck'];
1059
1083
 
1060
1084
  _.each(methods, function(method) {
@@ -1079,7 +1103,7 @@ Marionette.TemplateCache = function(templateId){
1079
1103
  };
1080
1104
 
1081
1105
  // TemplateCache object-level methods. Manage the template
1082
- // caches from these method calls instead of creating
1106
+ // caches from these method calls instead of creating
1083
1107
  // your own TemplateCache instances
1084
1108
  _.extend(Marionette.TemplateCache, {
1085
1109
  templateCaches: {},
@@ -1102,7 +1126,7 @@ _.extend(Marionette.TemplateCache, {
1102
1126
  // are specified, clears all templates:
1103
1127
  // `clear()`
1104
1128
  //
1105
- // If arguments are specified, clears each of the
1129
+ // If arguments are specified, clears each of the
1106
1130
  // specified templates from the cache:
1107
1131
  // `clear("#t1", "#t2", "...")`
1108
1132
  clear: function(){
@@ -1142,7 +1166,7 @@ _.extend(Marionette.TemplateCache.prototype, {
1142
1166
  // Load a template from the DOM, by default. Override
1143
1167
  // this method to provide your own template retrieval
1144
1168
  // For asynchronous loading with AMD/RequireJS, consider
1145
- // using a template-loader plugin as described here:
1169
+ // using a template-loader plugin as described here:
1146
1170
  // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
1147
1171
  loadTemplate: function(templateId){
1148
1172
  var template = Marionette.$(templateId).html();
@@ -1211,7 +1235,10 @@ Marionette.View = Backbone.View.extend({
1211
1235
  // this is a backfill since backbone removed the assignment
1212
1236
  // of this.options
1213
1237
  // at some point however this may be removed
1214
- this.options = options || {};
1238
+ this.options = _.extend({}, _.result(this, 'options'), _.isFunction(options) ? options.call(this) : options);
1239
+
1240
+ // parses out the @ui DSL for events
1241
+ this.events = this.normalizeUIKeys(_.result(this, 'events'));
1215
1242
  Backbone.View.prototype.constructor.apply(this, args);
1216
1243
 
1217
1244
  Marionette.MonitorDOMRefresh(this);
@@ -1222,6 +1249,10 @@ Marionette.View = Backbone.View.extend({
1222
1249
  // methods if the method exists
1223
1250
  triggerMethod: Marionette.triggerMethod,
1224
1251
 
1252
+ // Imports the "normalizeMethods" to transform hashes of
1253
+ // events=>function references/names to a hash of events=>function references
1254
+ normalizeMethods: Marionette.normalizeMethods,
1255
+
1225
1256
  // Get the template for this view
1226
1257
  // instance. You can set a `template` attribute in the view
1227
1258
  // definition or pass a `template: "whatever"` parameter in
@@ -1244,6 +1275,25 @@ Marionette.View = Backbone.View.extend({
1244
1275
  return _.extend(target, templateHelpers);
1245
1276
  },
1246
1277
 
1278
+ // allows for the use of the @ui. syntax within
1279
+ // a given key for triggers and events
1280
+ // swaps the @ui with the associated selector
1281
+ normalizeUIKeys: function(hash) {
1282
+ if (typeof(hash) === "undefined") {
1283
+ return;
1284
+ }
1285
+
1286
+ _.each(_.keys(hash), function(v) {
1287
+ var split = v.split("@ui.");
1288
+ if (split.length === 2) {
1289
+ hash[split[0]+this.ui[split[1]]] = hash[v];
1290
+ delete hash[v];
1291
+ }
1292
+ }, this);
1293
+
1294
+ return hash;
1295
+ },
1296
+
1247
1297
  // Configure `triggers` to forward DOM events to view
1248
1298
  // events. `triggers: {"click .foo": "do:foo"}`
1249
1299
  configureTriggers: function(){
@@ -1252,7 +1302,7 @@ Marionette.View = Backbone.View.extend({
1252
1302
  var triggerEvents = {};
1253
1303
 
1254
1304
  // Allow `triggers` to be configured as a function
1255
- var triggers = _.result(this, "triggers");
1305
+ var triggers = this.normalizeUIKeys(_.result(this, "triggers"));
1256
1306
 
1257
1307
  // Configure the triggers, prevent default
1258
1308
  // action and stop propagation of DOM events
@@ -1398,8 +1448,8 @@ Marionette.View = Backbone.View.extend({
1398
1448
  // with underscore.js templates, serializing the view's model or collection,
1399
1449
  // and calling several methods on extended views, such as `onRender`.
1400
1450
  Marionette.ItemView = Marionette.View.extend({
1401
-
1402
- // Setting up the inheritance chain which allows changes to
1451
+
1452
+ // Setting up the inheritance chain which allows changes to
1403
1453
  // Marionette.View.prototype.constructor which allows overriding
1404
1454
  constructor: function(){
1405
1455
  Marionette.View.prototype.constructor.apply(this, slice(arguments));
@@ -1480,11 +1530,40 @@ Marionette.CollectionView = Marionette.View.extend({
1480
1530
  Marionette.View.prototype.constructor.apply(this, slice(arguments));
1481
1531
 
1482
1532
  this._initialEvents();
1533
+ this.initRenderBuffer();
1534
+ },
1535
+
1536
+ // Instead of inserting elements one by one into the page,
1537
+ // it's much more performant to insert elements into a document
1538
+ // fragment and then insert that document fragment into the page
1539
+ initRenderBuffer: function() {
1540
+ this.elBuffer = document.createDocumentFragment();
1541
+ this._bufferedChildren = [];
1542
+ },
1543
+
1544
+ startBuffering: function() {
1545
+ this.initRenderBuffer();
1546
+ this.isBuffering = true;
1547
+ },
1548
+
1549
+ endBuffering: function() {
1550
+ this.isBuffering = false;
1551
+ this.appendBuffer(this, this.elBuffer);
1552
+ this._triggerShowBufferedChildren();
1553
+ this.initRenderBuffer();
1554
+ },
1555
+
1556
+ _triggerShowBufferedChildren: function () {
1557
+ if (this._isShown) {
1558
+ _.each(this._bufferedChildren, function (child) {
1559
+ Marionette.triggerMethod.call(child, "show");
1560
+ });
1561
+ this._bufferedChildren = [];
1562
+ }
1483
1563
  },
1484
1564
 
1485
1565
  // Configured the initial events that the collection view
1486
- // binds to. Override this method to prevent the initial
1487
- // events, or to add your own initial events.
1566
+ // binds to.
1488
1567
  _initialEvents: function(){
1489
1568
  if (this.collection){
1490
1569
  this.listenTo(this.collection, "add", this.addChildView, this);
@@ -1538,14 +1617,18 @@ Marionette.CollectionView = Marionette.View.extend({
1538
1617
  // more control over events being triggered, around the rendering
1539
1618
  // process
1540
1619
  _renderChildren: function(){
1620
+ this.startBuffering();
1621
+
1541
1622
  this.closeEmptyView();
1542
1623
  this.closeChildren();
1543
1624
 
1544
- if (this.collection && this.collection.length > 0) {
1625
+ if (!this.isEmpty(this.collection)) {
1545
1626
  this.showCollection();
1546
1627
  } else {
1547
1628
  this.showEmptyView();
1548
1629
  }
1630
+
1631
+ this.endBuffering();
1549
1632
  },
1550
1633
 
1551
1634
  // Internal method to loop through each item in the
@@ -1608,9 +1691,9 @@ Marionette.CollectionView = Marionette.View.extend({
1608
1691
  itemViewOptions = itemViewOptions.call(this, item, index);
1609
1692
  }
1610
1693
 
1611
- // build the view
1694
+ // build the view
1612
1695
  var view = this.buildItemView(item, ItemView, itemViewOptions);
1613
-
1696
+
1614
1697
  // set up the child view event forwarding
1615
1698
  this.addChildViewEventForwarding(view);
1616
1699
 
@@ -1626,12 +1709,14 @@ Marionette.CollectionView = Marionette.View.extend({
1626
1709
 
1627
1710
  // call the "show" method if the collection view
1628
1711
  // has already been shown
1629
- if (this._isShown){
1712
+ if (this._isShown && !this.isBuffering){
1630
1713
  Marionette.triggerMethod.call(view, "show");
1631
1714
  }
1632
1715
 
1633
1716
  // this view was added
1634
1717
  this.triggerMethod("after:item:added", view);
1718
+
1719
+ return view;
1635
1720
  },
1636
1721
 
1637
1722
  // Set up the child view event forwarding. Uses an "itemview:"
@@ -1643,13 +1728,30 @@ Marionette.CollectionView = Marionette.View.extend({
1643
1728
  // prepending "itemview:" to the event name
1644
1729
  this.listenTo(view, "all", function(){
1645
1730
  var args = slice(arguments);
1646
- args[0] = prefix + ":" + args[0];
1731
+ var rootEvent = args[0];
1732
+ var itemEvents = this.normalizeMethods(this.getItemEvents());
1733
+
1734
+ args[0] = prefix + ":" + rootEvent;
1647
1735
  args.splice(1, 0, view);
1648
1736
 
1737
+ // call collectionView itemEvent if defined
1738
+ if (typeof itemEvents !== "undefined" && _.isFunction(itemEvents[rootEvent])) {
1739
+ itemEvents[rootEvent].apply(this, args);
1740
+ }
1741
+
1649
1742
  Marionette.triggerMethod.apply(this, args);
1650
1743
  }, this);
1651
1744
  },
1652
1745
 
1746
+ // returns the value of itemEvents depending on if a function
1747
+ getItemEvents: function() {
1748
+ if (_.isFunction(this.itemEvents)) {
1749
+ return this.itemEvents.call(this);
1750
+ }
1751
+
1752
+ return this.itemEvents;
1753
+ },
1754
+
1653
1755
  // render the item view
1654
1756
  renderItemView: function(view, index) {
1655
1757
  view.render();
@@ -1687,20 +1789,40 @@ Marionette.CollectionView = Marionette.View.extend({
1687
1789
  this.triggerMethod("item:removed", view);
1688
1790
  },
1689
1791
 
1690
- // helper to show the empty view if the collection is empty
1691
- checkEmpty: function() {
1692
- // check if we're empty now, and if we are, show the
1693
- // empty view
1694
- if (!this.collection || this.collection.length === 0){
1792
+ // helper to check if the collection is empty
1793
+ isEmpty: function(collection){
1794
+ // check if we're empty now
1795
+ return !this.collection || this.collection.length === 0;
1796
+ },
1797
+
1798
+ // If empty, show the empty view
1799
+ checkEmpty: function (){
1800
+ if (this.isEmpty(this.collection)){
1695
1801
  this.showEmptyView();
1696
1802
  }
1697
1803
  },
1698
1804
 
1805
+ // You might need to override this if you've overridden appendHtml
1806
+ appendBuffer: function(collectionView, buffer) {
1807
+ collectionView.$el.append(buffer);
1808
+ },
1809
+
1699
1810
  // Append the HTML to the collection's `el`.
1700
1811
  // Override this method to do something other
1701
1812
  // then `.append`.
1702
1813
  appendHtml: function(collectionView, itemView, index){
1703
- collectionView.$el.append(itemView.el);
1814
+ if (collectionView.isBuffering) {
1815
+ // buffering happens on reset events and initial renders
1816
+ // in order to reduce the number of inserts into the
1817
+ // document, which are expensive.
1818
+ collectionView.elBuffer.appendChild(itemView.el);
1819
+ collectionView._bufferedChildren.push(itemView);
1820
+ }
1821
+ else {
1822
+ // If we've already rendered the main collection, just
1823
+ // append the new items directly into the element.
1824
+ collectionView.$el.append(itemView.el);
1825
+ }
1704
1826
  },
1705
1827
 
1706
1828
  // Internal method to set up the `children` object for
@@ -1750,11 +1872,17 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1750
1872
  // binds to. Override this method to prevent the initial
1751
1873
  // events, or to add your own initial events.
1752
1874
  _initialEvents: function(){
1753
- if (this.collection){
1754
- this.listenTo(this.collection, "add", this.addChildView, this);
1755
- this.listenTo(this.collection, "remove", this.removeItemView, this);
1756
- this.listenTo(this.collection, "reset", this._renderChildren, this);
1757
- }
1875
+
1876
+ // Bind only after composite view in rendered to avoid adding child views
1877
+ // to unexisting itemViewContainer
1878
+ this.once('render', function () {
1879
+ if (this.collection){
1880
+ this.listenTo(this.collection, "add", this.addChildView, this);
1881
+ this.listenTo(this.collection, "remove", this.removeItemView, this);
1882
+ this.listenTo(this.collection, "reset", this._renderChildren, this);
1883
+ }
1884
+ });
1885
+
1758
1886
  },
1759
1887
 
1760
1888
  // Retrieve the `itemView` to be used when rendering each of
@@ -1810,6 +1938,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1810
1938
 
1811
1939
  _renderChildren: function(){
1812
1940
  if (this.isRendered){
1941
+ this.triggerMethod("composite:collection:before:render");
1813
1942
  Marionette.CollectionView.prototype._renderChildren.call(this);
1814
1943
  this.triggerMethod("composite:collection:rendered");
1815
1944
  }
@@ -1827,15 +1956,31 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1827
1956
  return Marionette.Renderer.render(template, data);
1828
1957
  },
1829
1958
 
1959
+
1960
+ // You might need to override this if you've overridden appendHtml
1961
+ appendBuffer: function(compositeView, buffer) {
1962
+ var $container = this.getItemViewContainer(compositeView);
1963
+ $container.append(buffer);
1964
+ },
1965
+
1830
1966
  // Appends the `el` of itemView instances to the specified
1831
1967
  // `itemViewContainer` (a jQuery selector). Override this method to
1832
1968
  // provide custom logic of how the child item view instances have their
1833
1969
  // HTML appended to the composite view instance.
1834
- appendHtml: function(cv, iv, index){
1835
- var $container = this.getItemViewContainer(cv);
1836
- $container.append(iv.el);
1970
+ appendHtml: function(compositeView, itemView, index){
1971
+ if (compositeView.isBuffering) {
1972
+ compositeView.elBuffer.appendChild(itemView.el);
1973
+ compositeView._bufferedChildren.push(itemView);
1974
+ }
1975
+ else {
1976
+ // If we've already rendered the main collection, just
1977
+ // append the new items directly into the element.
1978
+ var $container = this.getItemViewContainer(compositeView);
1979
+ $container.append(itemView.el);
1980
+ }
1837
1981
  },
1838
1982
 
1983
+
1839
1984
  // Internal method to ensure an `$itemViewContainer` exists, for the
1840
1985
  // `appendHtml` method to use.
1841
1986
  getItemViewContainer: function(containerView){
@@ -1847,7 +1992,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1847
1992
  var itemViewContainer = Marionette.getOption(containerView, "itemViewContainer");
1848
1993
  if (itemViewContainer){
1849
1994
 
1850
- var selector = _.isFunction(itemViewContainer) ? itemViewContainer() : itemViewContainer;
1995
+ var selector = _.isFunction(itemViewContainer) ? itemViewContainer.call(this) : itemViewContainer;
1851
1996
  container = containerView.$(selector);
1852
1997
  if (container.length <= 0) {
1853
1998
  throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError");
@@ -1881,7 +2026,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
1881
2026
  // Used for composite view management and sub-application areas.
1882
2027
  Marionette.Layout = Marionette.ItemView.extend({
1883
2028
  regionType: Marionette.Region,
1884
-
2029
+
1885
2030
  // Ensure the regions are available when the `initialize` method
1886
2031
  // is called.
1887
2032
  constructor: function (options) {
@@ -1889,7 +2034,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1889
2034
 
1890
2035
  this._firstRender = true;
1891
2036
  this._initializeRegions(options);
1892
-
2037
+
1893
2038
  Marionette.ItemView.prototype.constructor.call(this, options);
1894
2039
  },
1895
2040
 
@@ -1900,7 +2045,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1900
2045
  render: function(){
1901
2046
 
1902
2047
  if (this.isClosed){
1903
- // a previously closed layout means we need to
2048
+ // a previously closed layout means we need to
1904
2049
  // completely re-initialize the regions
1905
2050
  this._initializeRegions();
1906
2051
  }
@@ -1909,7 +2054,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1909
2054
  // reset the regions
1910
2055
  this._firstRender = false;
1911
2056
  } else if (!this.isClosed){
1912
- // If this is not the first render call, then we need to
2057
+ // If this is not the first render call, then we need to
1913
2058
  // re-initializing the `el` for each region
1914
2059
  this._reInitializeRegions();
1915
2060
  }
@@ -1960,7 +2105,7 @@ Marionette.Layout = Marionette.ItemView.extend({
1960
2105
  },
1961
2106
 
1962
2107
  // Internal method to initialize the regions that have been defined in a
1963
- // `regions` attribute on this layout.
2108
+ // `regions` attribute on this layout.
1964
2109
  _initializeRegions: function (options) {
1965
2110
  var regions;
1966
2111
  this._initRegionManager();
@@ -2011,7 +2156,7 @@ Marionette.Layout = Marionette.ItemView.extend({
2011
2156
  //
2012
2157
  // Configure an AppRouter with `appRoutes`.
2013
2158
  //
2014
- // App routers can only take one `controller` object.
2159
+ // App routers can only take one `controller` object.
2015
2160
  // It is recommended that you divide your controller
2016
2161
  // objects in to smaller pieces of related functionality
2017
2162
  // and have multiple routers / controllers, instead of
@@ -2023,7 +2168,7 @@ Marionette.AppRouter = Backbone.Router.extend({
2023
2168
 
2024
2169
  constructor: function(options){
2025
2170
  Backbone.Router.prototype.constructor.apply(this, slice(arguments));
2026
-
2171
+
2027
2172
  this.options = options || {};
2028
2173
 
2029
2174
  var appRoutes = Marionette.getOption(this, "appRoutes");
@@ -2117,7 +2262,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
2117
2262
  this.triggerMethod("start", options);
2118
2263
  },
2119
2264
 
2120
- // Add regions to your app.
2265
+ // Add regions to your app.
2121
2266
  // Accepts a hash of named strings or Region objects
2122
2267
  // addRegions({something: "#someRegion"})
2123
2268
  // addRegions({something: Region.extend({el: "#someRegion"}) });
@@ -2136,7 +2281,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
2136
2281
  removeRegion: function(region) {
2137
2282
  this._regionManager.removeRegion(region);
2138
2283
  },
2139
-
2284
+
2140
2285
  // Provides alternative access to regions
2141
2286
  // Accepts the region name
2142
2287
  // getRegion('main')
@@ -2146,13 +2291,20 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
2146
2291
 
2147
2292
  // Create a module, attached to the application
2148
2293
  module: function(moduleNames, moduleDefinition){
2294
+ var ModuleClass = Marionette.Module;
2295
+
2296
+ // Overwrite the module class if the user specifies one
2297
+ if (moduleDefinition) {
2298
+ ModuleClass = moduleDefinition.moduleClass || ModuleClass;
2299
+ }
2300
+
2149
2301
  // slice the args, and add this application object as the
2150
2302
  // first argument of the array
2151
2303
  var args = slice(arguments);
2152
2304
  args.unshift(this);
2153
2305
 
2154
2306
  // see the Marionette.Module object for more information
2155
- return Marionette.Module.create.apply(Marionette.Module, args);
2307
+ return ModuleClass.create.apply(ModuleClass, args);
2156
2308
  },
2157
2309
 
2158
2310
  // Internal method to set up the region manager
@@ -2177,8 +2329,10 @@ Marionette.Application.extend = Marionette.extend;
2177
2329
 
2178
2330
  // A simple module system, used to create privacy and encapsulation in
2179
2331
  // Marionette applications
2180
- Marionette.Module = function(moduleName, app){
2332
+ Marionette.Module = function(moduleName, app, options){
2181
2333
  this.moduleName = moduleName;
2334
+ this.options = _.extend({}, this.options, options);
2335
+ this.initialize = options.initialize || this.initialize;
2182
2336
 
2183
2337
  // store sub-modules
2184
2338
  this.submodules = {};
@@ -2190,12 +2344,22 @@ Marionette.Module = function(moduleName, app){
2190
2344
  this.startWithParent = true;
2191
2345
 
2192
2346
  this.triggerMethod = Marionette.triggerMethod;
2347
+
2348
+ if (_.isFunction(this.initialize)){
2349
+ this.initialize(this.options, moduleName, app);
2350
+ }
2193
2351
  };
2194
2352
 
2353
+ Marionette.Module.extend = Marionette.extend;
2354
+
2195
2355
  // Extend the Module prototype with events / listenTo, so that the module
2196
2356
  // can be used as an event aggregator or pub/sub.
2197
2357
  _.extend(Marionette.Module.prototype, Backbone.Events, {
2198
2358
 
2359
+ // Initialize is an empty function by default. Override it with your own
2360
+ // initialization logic when extending Marionette.Module.
2361
+ initialize: function(){},
2362
+
2199
2363
  // Initializer for a specific module. Initializers are run when the
2200
2364
  // module's `start` method is called.
2201
2365
  addInitializer: function(callback){
@@ -2310,7 +2474,7 @@ _.extend(Marionette.Module, {
2310
2474
  // Loop through all the parts of the module definition
2311
2475
  _.each(moduleNames, function(moduleName, i){
2312
2476
  var parentModule = module;
2313
- module = this._getModule(parentModule, moduleName, app);
2477
+ module = this._getModule(parentModule, moduleName, app, moduleDefinition);
2314
2478
  this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
2315
2479
  }, this);
2316
2480
 
@@ -2319,12 +2483,18 @@ _.extend(Marionette.Module, {
2319
2483
  },
2320
2484
 
2321
2485
  _getModule: function(parentModule, moduleName, app, def, args){
2486
+ var ModuleClass = Marionette.Module;
2487
+ var options = _.extend({}, def);
2488
+ if (def) {
2489
+ ModuleClass = def.moduleClass || ModuleClass;
2490
+ }
2491
+
2322
2492
  // Get an existing module of this name if we have one
2323
2493
  var module = parentModule[moduleName];
2324
2494
 
2325
2495
  if (!module){
2326
2496
  // Create a new module if we don't have one
2327
- module = new Marionette.Module(moduleName, app);
2497
+ module = new ModuleClass(moduleName, app, options);
2328
2498
  parentModule[moduleName] = module;
2329
2499
  // store the module on the parent
2330
2500
  parentModule.submodules[moduleName] = module;
@@ -2334,7 +2504,7 @@ _.extend(Marionette.Module, {
2334
2504
  },
2335
2505
 
2336
2506
  _addModuleDefinition: function(parentModule, module, def, args){
2337
- var fn;
2507
+ var fn;
2338
2508
  var startWithParent;
2339
2509
 
2340
2510
  if (_.isFunction(def)){
@@ -2345,8 +2515,8 @@ _.extend(Marionette.Module, {
2345
2515
  } else if (_.isObject(def)){
2346
2516
  // if an object is supplied
2347
2517
  fn = def.define;
2348
- startWithParent = def.startWithParent;
2349
-
2518
+ startWithParent = (typeof def.startWithParent !== 'undefined') ? def.startWithParent : true;
2519
+
2350
2520
  } else {
2351
2521
  // if nothing is supplied
2352
2522
  startWithParent = true;