marionette.modal 1.0.0.6 → 1.0.0.7
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 +4 -4
- data/Gruntfile.coffee +1 -1
- data/examples/vendor/backbone.js +189 -169
- data/examples/vendor/marionette.js +287 -161
- data/examples/vendor/underscore.js +210 -123
- data/lib/marionette.modal/version.rb +1 -1
- data/test/spec/backbone.marionette.modals.spec.js +46 -13
- data/test/src/backbone.marionette.modals.spec.coffee +13 -4
- metadata +1 -1
@@ -1,6 +1,6 @@
|
|
1
1
|
// MarionetteJS (Backbone.Marionette)
|
2
2
|
// ----------------------------------
|
3
|
-
// v1.
|
3
|
+
// v1.4.1
|
4
4
|
//
|
5
5
|
// Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
|
6
6
|
// Distributed under MIT license
|
@@ -19,7 +19,7 @@
|
|
19
19
|
|
20
20
|
// Backbone.BabySitter
|
21
21
|
// -------------------
|
22
|
-
// v0.0.
|
22
|
+
// v0.0.6
|
23
23
|
//
|
24
24
|
// Copyright (c)2013 Derick Bailey, Muted Solutions, LLC.
|
25
25
|
// Distributed under MIT license
|
@@ -37,14 +37,13 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
37
37
|
// Container Constructor
|
38
38
|
// ---------------------
|
39
39
|
|
40
|
-
var Container = function(
|
40
|
+
var Container = function(views){
|
41
41
|
this._views = {};
|
42
42
|
this._indexByModel = {};
|
43
|
-
this._indexByCollection = {};
|
44
43
|
this._indexByCustom = {};
|
45
44
|
this._updateLength();
|
46
45
|
|
47
|
-
this.
|
46
|
+
_.each(views, this.add, this);
|
48
47
|
};
|
49
48
|
|
50
49
|
// Container Methods
|
@@ -54,7 +53,7 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
54
53
|
|
55
54
|
// Add a view to this container. Stores the view
|
56
55
|
// by `cid` and makes it searchable by the model
|
57
|
-
// and
|
56
|
+
// cid (and model itself). Optionally specify
|
58
57
|
// a custom key to store an retrieve the view.
|
59
58
|
add: function(view, customIndex){
|
60
59
|
var viewCid = view.cid;
|
@@ -67,11 +66,6 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
67
66
|
this._indexByModel[view.model.cid] = viewCid;
|
68
67
|
}
|
69
68
|
|
70
|
-
// index it by collection
|
71
|
-
if (view.collection){
|
72
|
-
this._indexByCollection[view.collection.cid] = viewCid;
|
73
|
-
}
|
74
|
-
|
75
69
|
// index by custom
|
76
70
|
if (customIndex){
|
77
71
|
this._indexByCustom[customIndex] = viewCid;
|
@@ -81,18 +75,16 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
81
75
|
},
|
82
76
|
|
83
77
|
// Find a view by the model that was attached to
|
84
|
-
// it. Uses the model's `cid` to find it
|
85
|
-
// retrieves the view by it's `cid` from the result
|
78
|
+
// it. Uses the model's `cid` to find it.
|
86
79
|
findByModel: function(model){
|
87
|
-
|
88
|
-
return this.findByCid(viewCid);
|
80
|
+
return this.findByModelCid(model.cid);
|
89
81
|
},
|
90
82
|
|
91
|
-
// Find a view by the
|
92
|
-
// it. Uses the
|
93
|
-
//
|
94
|
-
|
95
|
-
var viewCid = this.
|
83
|
+
// Find a view by the `cid` of the model that was attached to
|
84
|
+
// it. Uses the model's `cid` to find the view `cid` and
|
85
|
+
// retrieve the view using it.
|
86
|
+
findByModelCid: function(modelCid){
|
87
|
+
var viewCid = this._indexByModel[modelCid];
|
96
88
|
return this.findByCid(viewCid);
|
97
89
|
},
|
98
90
|
|
@@ -122,26 +114,13 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
122
114
|
delete this._indexByModel[view.model.cid];
|
123
115
|
}
|
124
116
|
|
125
|
-
// delete collection index
|
126
|
-
if (view.collection){
|
127
|
-
delete this._indexByCollection[view.collection.cid];
|
128
|
-
}
|
129
|
-
|
130
117
|
// delete custom index
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
if (this._indexByCustom[key] === viewCid){
|
136
|
-
cust = key;
|
137
|
-
break;
|
138
|
-
}
|
118
|
+
_.any(this._indexByCustom, function(cid, key) {
|
119
|
+
if (cid === viewCid) {
|
120
|
+
delete this._indexByCustom[key];
|
121
|
+
return true;
|
139
122
|
}
|
140
|
-
}
|
141
|
-
|
142
|
-
if (cust){
|
143
|
-
delete this._indexByCustom[cust];
|
144
|
-
}
|
123
|
+
}, this);
|
145
124
|
|
146
125
|
// remove the view from the container
|
147
126
|
delete this._views[viewCid];
|
@@ -153,44 +132,24 @@ Backbone.ChildViewContainer = (function(Backbone, _){
|
|
153
132
|
// Call a method on every view in the container,
|
154
133
|
// passing parameters to the call method one at a
|
155
134
|
// time, like `function.call`.
|
156
|
-
call: function(method
|
157
|
-
|
158
|
-
this.apply(method, args);
|
135
|
+
call: function(method){
|
136
|
+
this.apply(method, _.tail(arguments));
|
159
137
|
},
|
160
138
|
|
161
139
|
// Apply a method on every view in the container,
|
162
140
|
// passing parameters to the call method one at a
|
163
141
|
// time, like `function.apply`.
|
164
142
|
apply: function(method, args){
|
165
|
-
|
166
|
-
|
167
|
-
// fix for IE < 9
|
168
|
-
args = args || [];
|
169
|
-
|
170
|
-
_.each(this._views, function(view, key){
|
143
|
+
_.each(this._views, function(view){
|
171
144
|
if (_.isFunction(view[method])){
|
172
|
-
view[method].apply(view, args);
|
145
|
+
view[method].apply(view, args || []);
|
173
146
|
}
|
174
147
|
});
|
175
|
-
|
176
148
|
},
|
177
149
|
|
178
150
|
// Update the `.length` attribute on this container
|
179
151
|
_updateLength: function(){
|
180
152
|
this.length = _.size(this._views);
|
181
|
-
},
|
182
|
-
|
183
|
-
// set up an initial list of views
|
184
|
-
_addInitialViews: function(views){
|
185
|
-
if (!views){ return; }
|
186
|
-
|
187
|
-
var view, i,
|
188
|
-
length = views.length;
|
189
|
-
|
190
|
-
for (i=0; i<length; i++){
|
191
|
-
view = views[i];
|
192
|
-
this.add(view);
|
193
|
-
}
|
194
153
|
}
|
195
154
|
});
|
196
155
|
|
@@ -518,7 +477,7 @@ Marionette.extend = Backbone.Model.extend;
|
|
518
477
|
// --------------------
|
519
478
|
|
520
479
|
// Retrieve an object, function or other value from a target
|
521
|
-
// object or
|
480
|
+
// object or its `options`, with `options` taking precedence.
|
522
481
|
Marionette.getOption = function(target, optionName){
|
523
482
|
if (!target || !optionName){ return; }
|
524
483
|
var value;
|
@@ -532,21 +491,21 @@ Marionette.getOption = function(target, optionName){
|
|
532
491
|
return value;
|
533
492
|
};
|
534
493
|
|
535
|
-
// Trigger an event and a corresponding method name. Examples:
|
494
|
+
// Trigger an event and/or a corresponding method name. Examples:
|
536
495
|
//
|
537
496
|
// `this.triggerMethod("foo")` will trigger the "foo" event and
|
538
|
-
// call the "onFoo" method.
|
497
|
+
// call the "onFoo" method.
|
539
498
|
//
|
540
499
|
// `this.triggerMethod("foo:bar") will trigger the "foo:bar" event and
|
541
500
|
// call the "onFooBar" method.
|
542
501
|
Marionette.triggerMethod = (function(){
|
543
|
-
|
502
|
+
|
544
503
|
// split the event name on the :
|
545
504
|
var splitter = /(^|:)(\w)/gi;
|
546
505
|
|
547
506
|
// take the event section ("section1:section2:section3")
|
548
507
|
// and turn it in to uppercase name
|
549
|
-
function getEventName(match, prefix, eventName) {
|
508
|
+
function getEventName(match, prefix, eventName) {
|
550
509
|
return eventName.toUpperCase();
|
551
510
|
}
|
552
511
|
|
@@ -556,8 +515,10 @@ Marionette.triggerMethod = (function(){
|
|
556
515
|
var methodName = 'on' + event.replace(splitter, getEventName);
|
557
516
|
var method = this[methodName];
|
558
517
|
|
559
|
-
// trigger the event
|
560
|
-
this.trigger
|
518
|
+
// trigger the event, if a trigger method exists
|
519
|
+
if(_.isFunction(this.trigger)) {
|
520
|
+
this.trigger.apply(this, arguments);
|
521
|
+
}
|
561
522
|
|
562
523
|
// call the onMethodName if it exists
|
563
524
|
if (_.isFunction(method)) {
|
@@ -577,14 +538,14 @@ Marionette.triggerMethod = (function(){
|
|
577
538
|
// re-rendered.
|
578
539
|
|
579
540
|
Marionette.MonitorDOMRefresh = (function(){
|
580
|
-
// track when the view has been
|
541
|
+
// track when the view has been shown in the DOM,
|
542
|
+
// using a Marionette.Region (or by other means of triggering "show")
|
581
543
|
function handleShow(view){
|
582
544
|
view._isShown = true;
|
583
545
|
triggerDOMRefresh(view);
|
584
546
|
}
|
585
547
|
|
586
|
-
// track when the view has been
|
587
|
-
// using a Marionette.Region (or by other means of triggering "show")
|
548
|
+
// track when the view has been rendered
|
588
549
|
function handleRender(view){
|
589
550
|
view._isRendered = true;
|
590
551
|
triggerDOMRefresh(view);
|
@@ -615,8 +576,8 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
615
576
|
// Marionette.bindEntityEvents & unbindEntityEvents
|
616
577
|
// ---------------------------
|
617
578
|
//
|
618
|
-
// These methods are used to bind/unbind a backbone "entity" (collection/model)
|
619
|
-
// to methods on a target object.
|
579
|
+
// These methods are used to bind/unbind a backbone "entity" (collection/model)
|
580
|
+
// to methods on a target object.
|
620
581
|
//
|
621
582
|
// The first parameter, `target`, must have a `listenTo` method from the
|
622
583
|
// EventBinder object.
|
@@ -626,7 +587,7 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
626
587
|
//
|
627
588
|
// The third parameter is a hash of { "event:name": "eventHandler" }
|
628
589
|
// configuration. Multiple handlers can be separated by a space. A
|
629
|
-
// function can be supplied instead of a string handler name.
|
590
|
+
// function can be supplied instead of a string handler name.
|
630
591
|
|
631
592
|
(function(Marionette){
|
632
593
|
"use strict";
|
@@ -658,7 +619,7 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
658
619
|
var methodNames = methods.split(/\s+/);
|
659
620
|
|
660
621
|
_.each(methodNames,function(methodName) {
|
661
|
-
var method = target[
|
622
|
+
var method = target[methodName];
|
662
623
|
target.stopListening(entity, evt, method, target);
|
663
624
|
});
|
664
625
|
}
|
@@ -668,7 +629,7 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
668
629
|
target.stopListening(entity, evt, method, target);
|
669
630
|
}
|
670
631
|
|
671
|
-
|
632
|
+
|
672
633
|
// generic looping function
|
673
634
|
function iterateEvents(target, entity, bindings, functionCallback, stringCallback){
|
674
635
|
if (!entity || !bindings) { return; }
|
@@ -681,7 +642,7 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
681
642
|
// iterate the bindings and bind them
|
682
643
|
_.each(bindings, function(methods, evt){
|
683
644
|
|
684
|
-
// allow for a function as the handler,
|
645
|
+
// allow for a function as the handler,
|
685
646
|
// or a list of event names as a string
|
686
647
|
if (_.isFunction(methods)){
|
687
648
|
functionCallback(target, entity, evt, methods);
|
@@ -691,7 +652,7 @@ Marionette.MonitorDOMRefresh = (function(){
|
|
691
652
|
|
692
653
|
});
|
693
654
|
}
|
694
|
-
|
655
|
+
|
695
656
|
// Export Public API
|
696
657
|
Marionette.bindEntityEvents = function(target, entity, bindings){
|
697
658
|
iterateEvents(target, entity, bindings, bindToFunction, bindFromStrings);
|
@@ -718,7 +679,7 @@ Marionette.Callbacks = function(){
|
|
718
679
|
_.extend(Marionette.Callbacks.prototype, {
|
719
680
|
|
720
681
|
// Add a callback to be executed. Callbacks added here are
|
721
|
-
// guaranteed to execute, even if they are added after the
|
682
|
+
// guaranteed to execute, even if they are added after the
|
722
683
|
// `run` method is called.
|
723
684
|
add: function(callback, contextOverride){
|
724
685
|
this._callbacks.push({cb: callback, ctx: contextOverride});
|
@@ -729,8 +690,8 @@ _.extend(Marionette.Callbacks.prototype, {
|
|
729
690
|
});
|
730
691
|
},
|
731
692
|
|
732
|
-
// Run all registered callbacks with the context specified.
|
733
|
-
// Additional callbacks can be added after this has been run
|
693
|
+
// Run all registered callbacks with the context specified.
|
694
|
+
// Additional callbacks can be added after this has been run
|
734
695
|
// and they will still be executed.
|
735
696
|
run: function(options, context){
|
736
697
|
this._deferred.resolve(context, options);
|
@@ -742,7 +703,7 @@ _.extend(Marionette.Callbacks.prototype, {
|
|
742
703
|
var callbacks = this._callbacks;
|
743
704
|
this._deferred = Marionette.$.Deferred();
|
744
705
|
this._callbacks = [];
|
745
|
-
|
706
|
+
|
746
707
|
_.each(callbacks, function(cb){
|
747
708
|
this.add(cb.cb, cb.ctx);
|
748
709
|
}, this);
|
@@ -779,7 +740,7 @@ _.extend(Marionette.Controller.prototype, Backbone.Events, {
|
|
779
740
|
}
|
780
741
|
});
|
781
742
|
|
782
|
-
// Region
|
743
|
+
// Region
|
783
744
|
// ------
|
784
745
|
//
|
785
746
|
// Manage the visual regions of your composite application. See
|
@@ -823,6 +784,7 @@ _.extend(Marionette.Region, {
|
|
823
784
|
// ```
|
824
785
|
//
|
825
786
|
buildRegion: function(regionConfig, defaultRegionType){
|
787
|
+
|
826
788
|
var regionIsString = (typeof regionConfig === "string");
|
827
789
|
var regionSelectorIsString = (typeof regionConfig.selector === "string");
|
828
790
|
var regionTypeIsUndefined = (typeof regionConfig.regionType === "undefined");
|
@@ -833,19 +795,19 @@ _.extend(Marionette.Region, {
|
|
833
795
|
}
|
834
796
|
|
835
797
|
var selector, RegionType;
|
836
|
-
|
798
|
+
|
837
799
|
// get the selector for the region
|
838
|
-
|
800
|
+
|
839
801
|
if (regionIsString) {
|
840
802
|
selector = regionConfig;
|
841
|
-
}
|
803
|
+
}
|
842
804
|
|
843
805
|
if (regionConfig.selector) {
|
844
806
|
selector = regionConfig.selector;
|
845
807
|
}
|
846
808
|
|
847
809
|
// get the type for the region
|
848
|
-
|
810
|
+
|
849
811
|
if (regionIsType){
|
850
812
|
RegionType = regionConfig;
|
851
813
|
}
|
@@ -857,7 +819,7 @@ _.extend(Marionette.Region, {
|
|
857
819
|
if (regionConfig.regionType) {
|
858
820
|
RegionType = regionConfig.regionType;
|
859
821
|
}
|
860
|
-
|
822
|
+
|
861
823
|
// build the region instance
|
862
824
|
var region = new RegionType({
|
863
825
|
el: selector
|
@@ -899,18 +861,24 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
|
|
899
861
|
|
900
862
|
this.ensureEl();
|
901
863
|
|
902
|
-
|
864
|
+
var isViewClosed = view.isClosed || _.isUndefined(view.$el);
|
865
|
+
|
866
|
+
var isDifferentView = view !== this.currentView;
|
867
|
+
|
868
|
+
if (isDifferentView) {
|
903
869
|
this.close();
|
904
|
-
view.render();
|
905
|
-
this.open(view);
|
906
|
-
} else {
|
907
|
-
view.render();
|
908
870
|
}
|
909
871
|
|
910
|
-
|
911
|
-
|
872
|
+
view.render();
|
873
|
+
|
874
|
+
if (isDifferentView || isViewClosed) {
|
875
|
+
this.open(view);
|
876
|
+
}
|
912
877
|
|
913
878
|
this.currentView = view;
|
879
|
+
|
880
|
+
Marionette.triggerMethod.call(this, "show", view);
|
881
|
+
Marionette.triggerMethod.call(view, "show");
|
914
882
|
},
|
915
883
|
|
916
884
|
ensureEl: function(){
|
@@ -946,8 +914,8 @@ _.extend(Marionette.Region.prototype, Backbone.Events, {
|
|
946
914
|
delete this.currentView;
|
947
915
|
},
|
948
916
|
|
949
|
-
// Attach an existing view to the region. This
|
950
|
-
// will not call `render` or `onShow` for the new view,
|
917
|
+
// Attach an existing view to the region. This
|
918
|
+
// will not call `render` or `onShow` for the new view,
|
951
919
|
// and will not replace the current HTML for the `el`
|
952
920
|
// of the region.
|
953
921
|
attachView: function(view){
|
@@ -1061,14 +1029,20 @@ Marionette.RegionManager = (function(Marionette){
|
|
1061
1029
|
// internal method to store regions
|
1062
1030
|
_store: function(name, region){
|
1063
1031
|
this._regions[name] = region;
|
1064
|
-
this.
|
1032
|
+
this._setLength();
|
1065
1033
|
},
|
1066
1034
|
|
1067
1035
|
// internal method to remove a region
|
1068
1036
|
_remove: function(name, region){
|
1069
1037
|
region.close();
|
1070
1038
|
delete this._regions[name];
|
1039
|
+
this._setLength();
|
1071
1040
|
this.triggerMethod("region:remove", name, region);
|
1041
|
+
},
|
1042
|
+
|
1043
|
+
// set the number of regions current held
|
1044
|
+
_setLength: function(){
|
1045
|
+
this.length = _.size(this._regions);
|
1072
1046
|
}
|
1073
1047
|
|
1074
1048
|
});
|
@@ -1147,7 +1121,7 @@ _.extend(Marionette.TemplateCache, {
|
|
1147
1121
|
});
|
1148
1122
|
|
1149
1123
|
// TemplateCache instance methods, allowing each
|
1150
|
-
// template cache object to manage
|
1124
|
+
// template cache object to manage its own state
|
1151
1125
|
// and know whether or not it has been loaded
|
1152
1126
|
_.extend(Marionette.TemplateCache.prototype, {
|
1153
1127
|
|
@@ -1202,7 +1176,20 @@ Marionette.Renderer = {
|
|
1202
1176
|
// template function. Override this method to provide your own
|
1203
1177
|
// custom rendering and template handling for all of Marionette.
|
1204
1178
|
render: function(template, data){
|
1205
|
-
|
1179
|
+
|
1180
|
+
if (!template) {
|
1181
|
+
var error = new Error("Cannot render the template since it's false, null or undefined.");
|
1182
|
+
error.name = "TemplateNotFoundError";
|
1183
|
+
throw error;
|
1184
|
+
}
|
1185
|
+
|
1186
|
+
var templateFunc;
|
1187
|
+
if (typeof template === "function"){
|
1188
|
+
templateFunc = template;
|
1189
|
+
} else {
|
1190
|
+
templateFunc = Marionette.TemplateCache.get(template);
|
1191
|
+
}
|
1192
|
+
|
1206
1193
|
return templateFunc(data);
|
1207
1194
|
}
|
1208
1195
|
};
|
@@ -1215,10 +1202,19 @@ Marionette.Renderer = {
|
|
1215
1202
|
// The core view type that other Marionette views extend from.
|
1216
1203
|
Marionette.View = Backbone.View.extend({
|
1217
1204
|
|
1218
|
-
constructor: function(){
|
1205
|
+
constructor: function(options){
|
1219
1206
|
_.bindAll(this, "render");
|
1220
1207
|
|
1221
1208
|
var args = Array.prototype.slice.apply(arguments);
|
1209
|
+
|
1210
|
+
// this exposes view options to the view initializer
|
1211
|
+
// this is a backfill since backbone removed the assignment
|
1212
|
+
// of this.options
|
1213
|
+
// at some point however this may be removed
|
1214
|
+
this.options = _.extend({}, this.options, options);
|
1215
|
+
|
1216
|
+
// parses out the @ui DSL for events
|
1217
|
+
this.events = this.normalizeUIKeys(_.result(this, 'events'));
|
1222
1218
|
Backbone.View.prototype.constructor.apply(this, args);
|
1223
1219
|
|
1224
1220
|
Marionette.MonitorDOMRefresh(this);
|
@@ -1226,7 +1222,7 @@ Marionette.View = Backbone.View.extend({
|
|
1226
1222
|
},
|
1227
1223
|
|
1228
1224
|
// import the "triggerMethod" to trigger events with corresponding
|
1229
|
-
// methods if the method exists
|
1225
|
+
// methods if the method exists
|
1230
1226
|
triggerMethod: Marionette.triggerMethod,
|
1231
1227
|
|
1232
1228
|
// Get the template for this view
|
@@ -1244,13 +1240,32 @@ Marionette.View = Backbone.View.extend({
|
|
1244
1240
|
// are copies to the object passed in.
|
1245
1241
|
mixinTemplateHelpers: function(target){
|
1246
1242
|
target = target || {};
|
1247
|
-
var templateHelpers = this
|
1243
|
+
var templateHelpers = Marionette.getOption(this, "templateHelpers");
|
1248
1244
|
if (_.isFunction(templateHelpers)){
|
1249
1245
|
templateHelpers = templateHelpers.call(this);
|
1250
1246
|
}
|
1251
1247
|
return _.extend(target, templateHelpers);
|
1252
1248
|
},
|
1253
1249
|
|
1250
|
+
// allows for the use of the @ui. syntax within
|
1251
|
+
// a given key for triggers and events
|
1252
|
+
// swaps the @ui with the associated selector
|
1253
|
+
normalizeUIKeys: function(hash) {
|
1254
|
+
if (typeof(hash) === "undefined") {
|
1255
|
+
return;
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
_.each(_.keys(hash), function(v) {
|
1259
|
+
var split = v.split("@ui.");
|
1260
|
+
if (split.length === 2) {
|
1261
|
+
hash[split[0]+this.ui[split[1]]] = hash[v];
|
1262
|
+
delete hash[v];
|
1263
|
+
}
|
1264
|
+
}, this);
|
1265
|
+
|
1266
|
+
return hash;
|
1267
|
+
},
|
1268
|
+
|
1254
1269
|
// Configure `triggers` to forward DOM events to view
|
1255
1270
|
// events. `triggers: {"click .foo": "do:foo"}`
|
1256
1271
|
configureTriggers: function(){
|
@@ -1259,18 +1274,29 @@ Marionette.View = Backbone.View.extend({
|
|
1259
1274
|
var triggerEvents = {};
|
1260
1275
|
|
1261
1276
|
// Allow `triggers` to be configured as a function
|
1262
|
-
var triggers = _.result(this, "triggers");
|
1277
|
+
var triggers = this.normalizeUIKeys(_.result(this, "triggers"));
|
1263
1278
|
|
1264
1279
|
// Configure the triggers, prevent default
|
1265
1280
|
// action and stop propagation of DOM events
|
1266
1281
|
_.each(triggers, function(value, key){
|
1267
1282
|
|
1283
|
+
var hasOptions = _.isObject(value);
|
1284
|
+
var eventName = hasOptions ? value.event : value;
|
1285
|
+
|
1268
1286
|
// build the event handler function for the DOM event
|
1269
1287
|
triggerEvents[key] = function(e){
|
1270
1288
|
|
1271
|
-
// stop the event in
|
1272
|
-
if (e
|
1273
|
-
|
1289
|
+
// stop the event in its tracks
|
1290
|
+
if (e) {
|
1291
|
+
var prevent = e.preventDefault;
|
1292
|
+
var stop = e.stopPropagation;
|
1293
|
+
|
1294
|
+
var shouldPrevent = hasOptions ? value.preventDefault : prevent;
|
1295
|
+
var shouldStop = hasOptions ? value.stopPropagation : stop;
|
1296
|
+
|
1297
|
+
if (shouldPrevent && prevent) { prevent.apply(e); }
|
1298
|
+
if (shouldStop && stop) { stop.apply(e); }
|
1299
|
+
}
|
1274
1300
|
|
1275
1301
|
// build the args for the event
|
1276
1302
|
var args = {
|
@@ -1280,7 +1306,7 @@ Marionette.View = Backbone.View.extend({
|
|
1280
1306
|
};
|
1281
1307
|
|
1282
1308
|
// trigger the event
|
1283
|
-
this.triggerMethod(
|
1309
|
+
this.triggerMethod(eventName, args);
|
1284
1310
|
};
|
1285
1311
|
|
1286
1312
|
}, this);
|
@@ -1288,7 +1314,7 @@ Marionette.View = Backbone.View.extend({
|
|
1288
1314
|
return triggerEvents;
|
1289
1315
|
},
|
1290
1316
|
|
1291
|
-
// Overriding Backbone.View's delegateEvents to handle
|
1317
|
+
// Overriding Backbone.View's delegateEvents to handle
|
1292
1318
|
// the `triggers`, `modelEvents`, and `collectionEvents` configuration
|
1293
1319
|
delegateEvents: function(events){
|
1294
1320
|
this._delegateDOMEvents(events);
|
@@ -1374,7 +1400,7 @@ Marionette.View = Backbone.View.extend({
|
|
1374
1400
|
|
1375
1401
|
// This method unbinds the elements specified in the "ui" hash
|
1376
1402
|
unbindUIElements: function(){
|
1377
|
-
if (!this.ui){ return; }
|
1403
|
+
if (!this.ui || !this._uiBindings){ return; }
|
1378
1404
|
|
1379
1405
|
// delete all of the existing ui bindings
|
1380
1406
|
_.each(this.ui, function($el, name){
|
@@ -1393,7 +1419,10 @@ Marionette.View = Backbone.View.extend({
|
|
1393
1419
|
// A single item view implementation that contains code for rendering
|
1394
1420
|
// with underscore.js templates, serializing the view's model or collection,
|
1395
1421
|
// and calling several methods on extended views, such as `onRender`.
|
1396
|
-
Marionette.ItemView =
|
1422
|
+
Marionette.ItemView = Marionette.View.extend({
|
1423
|
+
|
1424
|
+
// Setting up the inheritance chain which allows changes to
|
1425
|
+
// Marionette.View.prototype.constructor which allows overriding
|
1397
1426
|
constructor: function(){
|
1398
1427
|
Marionette.View.prototype.constructor.apply(this, slice(arguments));
|
1399
1428
|
},
|
@@ -1433,6 +1462,7 @@ Marionette.ItemView = Marionette.View.extend({
|
|
1433
1462
|
|
1434
1463
|
var template = this.getTemplate();
|
1435
1464
|
var html = Marionette.Renderer.render(template, data);
|
1465
|
+
|
1436
1466
|
this.$el.html(html);
|
1437
1467
|
this.bindUIElements();
|
1438
1468
|
|
@@ -1472,6 +1502,25 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1472
1502
|
Marionette.View.prototype.constructor.apply(this, slice(arguments));
|
1473
1503
|
|
1474
1504
|
this._initialEvents();
|
1505
|
+
this.initRenderBuffer();
|
1506
|
+
},
|
1507
|
+
|
1508
|
+
// Instead of inserting elements one by one into the page,
|
1509
|
+
// it's much more performant to insert elements into a document
|
1510
|
+
// fragment and then insert that document fragment into the page
|
1511
|
+
initRenderBuffer: function() {
|
1512
|
+
this.elBuffer = document.createDocumentFragment();
|
1513
|
+
},
|
1514
|
+
|
1515
|
+
startBuffering: function() {
|
1516
|
+
this.initRenderBuffer();
|
1517
|
+
this.isBuffering = true;
|
1518
|
+
},
|
1519
|
+
|
1520
|
+
endBuffering: function() {
|
1521
|
+
this.appendBuffer(this, this.elBuffer);
|
1522
|
+
this.initRenderBuffer();
|
1523
|
+
this.isBuffering = false;
|
1475
1524
|
},
|
1476
1525
|
|
1477
1526
|
// Configured the initial events that the collection view
|
@@ -1530,6 +1579,8 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1530
1579
|
// more control over events being triggered, around the rendering
|
1531
1580
|
// process
|
1532
1581
|
_renderChildren: function(){
|
1582
|
+
this.startBuffering();
|
1583
|
+
|
1533
1584
|
this.closeEmptyView();
|
1534
1585
|
this.closeChildren();
|
1535
1586
|
|
@@ -1538,6 +1589,8 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1538
1589
|
} else {
|
1539
1590
|
this.showEmptyView();
|
1540
1591
|
}
|
1592
|
+
|
1593
|
+
this.endBuffering();
|
1541
1594
|
},
|
1542
1595
|
|
1543
1596
|
// Internal method to loop through each item in the
|
@@ -1554,7 +1607,7 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1554
1607
|
// a collection of item views, when the collection is
|
1555
1608
|
// empty
|
1556
1609
|
showEmptyView: function(){
|
1557
|
-
var EmptyView =
|
1610
|
+
var EmptyView = this.getEmptyView();
|
1558
1611
|
|
1559
1612
|
if (EmptyView && !this._showingEmptyView){
|
1560
1613
|
this._showingEmptyView = true;
|
@@ -1573,6 +1626,11 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1573
1626
|
}
|
1574
1627
|
},
|
1575
1628
|
|
1629
|
+
// Retrieve the empty view type
|
1630
|
+
getEmptyView: function(){
|
1631
|
+
return Marionette.getOption(this, "emptyView");
|
1632
|
+
},
|
1633
|
+
|
1576
1634
|
// Retrieve the itemView type, either from `this.options.itemView`
|
1577
1635
|
// or from the `itemView` in the object definition. The "options"
|
1578
1636
|
// takes precedence.
|
@@ -1595,9 +1653,9 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1595
1653
|
itemViewOptions = itemViewOptions.call(this, item, index);
|
1596
1654
|
}
|
1597
1655
|
|
1598
|
-
// build the view
|
1656
|
+
// build the view
|
1599
1657
|
var view = this.buildItemView(item, ItemView, itemViewOptions);
|
1600
|
-
|
1658
|
+
|
1601
1659
|
// set up the child view event forwarding
|
1602
1660
|
this.addChildViewEventForwarding(view);
|
1603
1661
|
|
@@ -1683,11 +1741,26 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1683
1741
|
}
|
1684
1742
|
},
|
1685
1743
|
|
1744
|
+
// You might need to override this if you've overridden appendHtml
|
1745
|
+
appendBuffer: function(collectionView, buffer) {
|
1746
|
+
collectionView.$el.append(buffer);
|
1747
|
+
},
|
1748
|
+
|
1686
1749
|
// Append the HTML to the collection's `el`.
|
1687
1750
|
// Override this method to do something other
|
1688
1751
|
// then `.append`.
|
1689
1752
|
appendHtml: function(collectionView, itemView, index){
|
1690
|
-
collectionView
|
1753
|
+
if (collectionView.isBuffering) {
|
1754
|
+
// buffering happens on reset events and initial renders
|
1755
|
+
// in order to reduce the number of inserts into the
|
1756
|
+
// document, which are expensive.
|
1757
|
+
collectionView.elBuffer.appendChild(itemView.el);
|
1758
|
+
}
|
1759
|
+
else {
|
1760
|
+
// If we've already rendered the main collection, just
|
1761
|
+
// append the new items directly into the element.
|
1762
|
+
collectionView.$el.append(itemView.el);
|
1763
|
+
}
|
1691
1764
|
},
|
1692
1765
|
|
1693
1766
|
// Internal method to set up the `children` object for
|
@@ -1726,21 +1799,28 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
1726
1799
|
// Extends directly from CollectionView and also renders an
|
1727
1800
|
// an item view as `modelView`, for the top leaf
|
1728
1801
|
Marionette.CompositeView = Marionette.CollectionView.extend({
|
1729
|
-
constructor: function(options){
|
1730
|
-
Marionette.CollectionView.apply(this, slice(arguments));
|
1731
1802
|
|
1732
|
-
|
1803
|
+
// Setting up the inheritance chain which allows changes to
|
1804
|
+
// Marionette.CollectionView.prototype.constructor which allows overriding
|
1805
|
+
constructor: function(){
|
1806
|
+
Marionette.CollectionView.prototype.constructor.apply(this, slice(arguments));
|
1733
1807
|
},
|
1734
1808
|
|
1735
1809
|
// Configured the initial events that the composite view
|
1736
1810
|
// binds to. Override this method to prevent the initial
|
1737
1811
|
// events, or to add your own initial events.
|
1738
1812
|
_initialEvents: function(){
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1813
|
+
|
1814
|
+
// Bind only after composite view in rendered to avoid adding child views
|
1815
|
+
// to unexisting itemViewContainer
|
1816
|
+
this.once('render', function () {
|
1817
|
+
if (this.collection){
|
1818
|
+
this.listenTo(this.collection, "add", this.addChildView, this);
|
1819
|
+
this.listenTo(this.collection, "remove", this.removeItemView, this);
|
1820
|
+
this.listenTo(this.collection, "reset", this._renderChildren, this);
|
1821
|
+
}
|
1822
|
+
});
|
1823
|
+
|
1744
1824
|
},
|
1745
1825
|
|
1746
1826
|
// Retrieve the `itemView` to be used when rendering each of
|
@@ -1757,7 +1837,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
1757
1837
|
return itemView;
|
1758
1838
|
},
|
1759
1839
|
|
1760
|
-
// Serialize the collection for the view.
|
1840
|
+
// Serialize the collection for the view.
|
1761
1841
|
// You can override the `serializeData` method in your own view
|
1762
1842
|
// definition, to provide custom serialization for your view's data.
|
1763
1843
|
serializeData: function(){
|
@@ -1781,7 +1861,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
1781
1861
|
this.triggerBeforeRender();
|
1782
1862
|
var html = this.renderModel();
|
1783
1863
|
this.$el.html(html);
|
1784
|
-
// the ui bindings is done here and not at the end of render since they
|
1864
|
+
// the ui bindings is done here and not at the end of render since they
|
1785
1865
|
// will not be available until after the model is rendered, but should be
|
1786
1866
|
// available before the collection is rendered.
|
1787
1867
|
this.bindUIElements();
|
@@ -1813,15 +1893,30 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
1813
1893
|
return Marionette.Renderer.render(template, data);
|
1814
1894
|
},
|
1815
1895
|
|
1896
|
+
|
1897
|
+
// You might need to override this if you've overridden appendHtml
|
1898
|
+
appendBuffer: function(compositeView, buffer) {
|
1899
|
+
var $container = this.getItemViewContainer(compositeView);
|
1900
|
+
$container.append(buffer);
|
1901
|
+
},
|
1902
|
+
|
1816
1903
|
// Appends the `el` of itemView instances to the specified
|
1817
1904
|
// `itemViewContainer` (a jQuery selector). Override this method to
|
1818
1905
|
// provide custom logic of how the child item view instances have their
|
1819
1906
|
// HTML appended to the composite view instance.
|
1820
|
-
appendHtml: function(
|
1821
|
-
|
1822
|
-
|
1907
|
+
appendHtml: function(compositeView, itemView, index){
|
1908
|
+
if (compositeView.isBuffering) {
|
1909
|
+
compositeView.elBuffer.appendChild(itemView.el);
|
1910
|
+
}
|
1911
|
+
else {
|
1912
|
+
// If we've already rendered the main collection, just
|
1913
|
+
// append the new items directly into the element.
|
1914
|
+
var $container = this.getItemViewContainer(compositeView);
|
1915
|
+
$container.append(itemView.el);
|
1916
|
+
}
|
1823
1917
|
},
|
1824
1918
|
|
1919
|
+
|
1825
1920
|
// Internal method to ensure an `$itemViewContainer` exists, for the
|
1826
1921
|
// `appendHtml` method to use.
|
1827
1922
|
getItemViewContainer: function(containerView){
|
@@ -1830,9 +1925,10 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
1830
1925
|
}
|
1831
1926
|
|
1832
1927
|
var container;
|
1833
|
-
|
1928
|
+
var itemViewContainer = Marionette.getOption(containerView, "itemViewContainer");
|
1929
|
+
if (itemViewContainer){
|
1834
1930
|
|
1835
|
-
var selector = _.
|
1931
|
+
var selector = _.isFunction(itemViewContainer) ? itemViewContainer() : itemViewContainer;
|
1836
1932
|
container = containerView.$(selector);
|
1837
1933
|
if (container.length <= 0) {
|
1838
1934
|
throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError");
|
@@ -1866,7 +1962,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
1866
1962
|
// Used for composite view management and sub-application areas.
|
1867
1963
|
Marionette.Layout = Marionette.ItemView.extend({
|
1868
1964
|
regionType: Marionette.Region,
|
1869
|
-
|
1965
|
+
|
1870
1966
|
// Ensure the regions are available when the `initialize` method
|
1871
1967
|
// is called.
|
1872
1968
|
constructor: function (options) {
|
@@ -1874,8 +1970,8 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1874
1970
|
|
1875
1971
|
this._firstRender = true;
|
1876
1972
|
this._initializeRegions(options);
|
1877
|
-
|
1878
|
-
Marionette.ItemView.call(this, options);
|
1973
|
+
|
1974
|
+
Marionette.ItemView.prototype.constructor.call(this, options);
|
1879
1975
|
},
|
1880
1976
|
|
1881
1977
|
// Layout's render will use the existing region objects the
|
@@ -1884,16 +1980,17 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1884
1980
|
// for the regions to the newly rendered DOM elements.
|
1885
1981
|
render: function(){
|
1886
1982
|
|
1887
|
-
if (this.
|
1983
|
+
if (this.isClosed){
|
1984
|
+
// a previously closed layout means we need to
|
1985
|
+
// completely re-initialize the regions
|
1986
|
+
this._initializeRegions();
|
1987
|
+
}
|
1988
|
+
if (this._firstRender) {
|
1888
1989
|
// if this is the first render, don't do anything to
|
1889
1990
|
// reset the regions
|
1890
1991
|
this._firstRender = false;
|
1891
|
-
} else if (this.isClosed){
|
1892
|
-
//
|
1893
|
-
// completely re-initialize the regions
|
1894
|
-
this._initializeRegions();
|
1895
|
-
} else {
|
1896
|
-
// If this is not the first render call, then we need to
|
1992
|
+
} else if (!this.isClosed){
|
1993
|
+
// If this is not the first render call, then we need to
|
1897
1994
|
// re-initializing the `el` for each region
|
1898
1995
|
this._reInitializeRegions();
|
1899
1996
|
}
|
@@ -1916,17 +2013,18 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1916
2013
|
addRegion: function(name, definition){
|
1917
2014
|
var regions = {};
|
1918
2015
|
regions[name] = definition;
|
1919
|
-
return this.
|
2016
|
+
return this._buildRegions(regions)[name];
|
1920
2017
|
},
|
1921
2018
|
|
1922
2019
|
// Add multiple regions as a {name: definition, name2: def2} object literal
|
1923
2020
|
addRegions: function(regions){
|
1924
|
-
this.regions = _.extend(this.regions
|
2021
|
+
this.regions = _.extend({}, this.regions, regions);
|
1925
2022
|
return this._buildRegions(regions);
|
1926
2023
|
},
|
1927
2024
|
|
1928
2025
|
// Remove a single region from the Layout, by name
|
1929
2026
|
removeRegion: function(name){
|
2027
|
+
delete this.regions[name];
|
1930
2028
|
return this.regionManager.removeRegion(name);
|
1931
2029
|
},
|
1932
2030
|
|
@@ -1935,6 +2033,7 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1935
2033
|
var that = this;
|
1936
2034
|
|
1937
2035
|
var defaults = {
|
2036
|
+
regionType: Marionette.getOption(this, "regionType"),
|
1938
2037
|
parentEl: function(){ return that.$el; }
|
1939
2038
|
};
|
1940
2039
|
|
@@ -1942,7 +2041,7 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1942
2041
|
},
|
1943
2042
|
|
1944
2043
|
// Internal method to initialize the regions that have been defined in a
|
1945
|
-
// `regions` attribute on this layout.
|
2044
|
+
// `regions` attribute on this layout.
|
1946
2045
|
_initializeRegions: function (options) {
|
1947
2046
|
var regions;
|
1948
2047
|
this._initRegionManager();
|
@@ -2005,31 +2104,46 @@ Marionette.AppRouter = Backbone.Router.extend({
|
|
2005
2104
|
|
2006
2105
|
constructor: function(options){
|
2007
2106
|
Backbone.Router.prototype.constructor.apply(this, slice(arguments));
|
2107
|
+
|
2108
|
+
this.options = options || {};
|
2008
2109
|
|
2009
|
-
|
2110
|
+
var appRoutes = Marionette.getOption(this, "appRoutes");
|
2111
|
+
var controller = this._getController();
|
2112
|
+
this.processAppRoutes(controller, appRoutes);
|
2113
|
+
},
|
2010
2114
|
|
2011
|
-
|
2012
|
-
|
2013
|
-
|
2014
|
-
|
2115
|
+
// Similar to route method on a Backbone Router but
|
2116
|
+
// method is called on the controller
|
2117
|
+
appRoute: function(route, methodName) {
|
2118
|
+
var controller = this._getController();
|
2119
|
+
this._addAppRoute(controller, route, methodName);
|
2015
2120
|
},
|
2016
2121
|
|
2017
2122
|
// Internal method to process the `appRoutes` for the
|
2018
2123
|
// router, and turn them in to routes that trigger the
|
2019
2124
|
// specified method on the specified `controller`.
|
2020
2125
|
processAppRoutes: function(controller, appRoutes) {
|
2126
|
+
if (!appRoutes){ return; }
|
2127
|
+
|
2021
2128
|
var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes
|
2022
2129
|
|
2023
2130
|
_.each(routeNames, function(route) {
|
2024
|
-
|
2025
|
-
|
2131
|
+
this._addAppRoute(controller, route, appRoutes[route]);
|
2132
|
+
}, this);
|
2133
|
+
},
|
2026
2134
|
|
2027
|
-
|
2028
|
-
|
2029
|
-
|
2135
|
+
_getController: function(){
|
2136
|
+
return Marionette.getOption(this, "controller");
|
2137
|
+
},
|
2030
2138
|
|
2031
|
-
|
2032
|
-
|
2139
|
+
_addAppRoute: function(controller, route, methodName){
|
2140
|
+
var method = controller[methodName];
|
2141
|
+
|
2142
|
+
if (!method) {
|
2143
|
+
throw new Error("Method '" + methodName + "' was not found on the controller");
|
2144
|
+
}
|
2145
|
+
|
2146
|
+
this.route(route, methodName, _.bind(method, controller));
|
2033
2147
|
}
|
2034
2148
|
});
|
2035
2149
|
|
@@ -2084,7 +2198,7 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
|
|
2084
2198
|
this.triggerMethod("start", options);
|
2085
2199
|
},
|
2086
2200
|
|
2087
|
-
// Add regions to your app.
|
2201
|
+
// Add regions to your app.
|
2088
2202
|
// Accepts a hash of named strings or Region objects
|
2089
2203
|
// addRegions({something: "#someRegion"})
|
2090
2204
|
// addRegions({something: Region.extend({el: "#someRegion"}) });
|
@@ -2092,13 +2206,25 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
|
|
2092
2206
|
return this._regionManager.addRegions(regions);
|
2093
2207
|
},
|
2094
2208
|
|
2095
|
-
//
|
2209
|
+
// Close all regions in the app, without removing them
|
2210
|
+
closeRegions: function(){
|
2211
|
+
this._regionManager.closeRegions();
|
2212
|
+
},
|
2213
|
+
|
2214
|
+
// Removes a region from your app, by name
|
2096
2215
|
// Accepts the regions name
|
2097
2216
|
// removeRegion('myRegion')
|
2098
2217
|
removeRegion: function(region) {
|
2099
2218
|
this._regionManager.removeRegion(region);
|
2100
2219
|
},
|
2101
2220
|
|
2221
|
+
// Provides alternative access to regions
|
2222
|
+
// Accepts the region name
|
2223
|
+
// getRegion('main')
|
2224
|
+
getRegion: function(region) {
|
2225
|
+
return this._regionManager.get(region);
|
2226
|
+
},
|
2227
|
+
|
2102
2228
|
// Create a module, attached to the application
|
2103
2229
|
module: function(moduleNames, moduleDefinition){
|
2104
2230
|
// slice the args, and add this application object as the
|
@@ -2289,7 +2415,7 @@ _.extend(Marionette.Module, {
|
|
2289
2415
|
},
|
2290
2416
|
|
2291
2417
|
_addModuleDefinition: function(parentModule, module, def, args){
|
2292
|
-
var fn;
|
2418
|
+
var fn;
|
2293
2419
|
var startWithParent;
|
2294
2420
|
|
2295
2421
|
if (_.isFunction(def)){
|
@@ -2301,7 +2427,7 @@ _.extend(Marionette.Module, {
|
|
2301
2427
|
// if an object is supplied
|
2302
2428
|
fn = def.define;
|
2303
2429
|
startWithParent = def.startWithParent;
|
2304
|
-
|
2430
|
+
|
2305
2431
|
} else {
|
2306
2432
|
// if nothing is supplied
|
2307
2433
|
startWithParent = true;
|
@@ -2337,4 +2463,4 @@ _.extend(Marionette.Module, {
|
|
2337
2463
|
|
2338
2464
|
|
2339
2465
|
return Marionette;
|
2340
|
-
})(this, Backbone, _);
|
2466
|
+
})(this, Backbone, _);
|