marionette_dust 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;