marionette-amd-rails 0.8.4.1 → 0.10.2.1
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.
- data/README.md +6 -6
- data/vendor/assets/javascripts/backbone.marionette.js +864 -455
- metadata +4 -4
data/README.md
CHANGED
@@ -3,7 +3,7 @@ marionette-amd-rails
|
|
3
3
|
|
4
4
|
[](https://gemnasium.com/eploko/marionette-amd-rails)
|
5
5
|
|
6
|
-
This gem is a wrapper for the AMD version of Derick Bailey's [Backbone.Marionette](https://github.com/
|
6
|
+
This gem is a wrapper for the AMD version of Derick Bailey's [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) library. It vendors the javascript library code for use with Rails' asset pipeline (Rails 3.1+).
|
7
7
|
|
8
8
|
## Dependencies
|
9
9
|
|
@@ -15,7 +15,7 @@ Add it to your Gemfile:
|
|
15
15
|
|
16
16
|
group :assets do
|
17
17
|
# Your other asset gems (sass-rails, coffee-rails, etc)
|
18
|
-
|
18
|
+
|
19
19
|
gem 'marionette-amd-rails'
|
20
20
|
end
|
21
21
|
|
@@ -23,20 +23,20 @@ Load `backbone.marionette` module as a dependency when appropriate.
|
|
23
23
|
|
24
24
|
## Versioning
|
25
25
|
|
26
|
-
The gem will mirror the [Backbone.Marionette](https://github.com/
|
26
|
+
The gem will mirror the [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) versioning scheme. That is, version 0.8.2.* of `marionette-amd-rails` would vendor [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) v0.8.2.
|
27
27
|
|
28
28
|
## Contributing
|
29
29
|
|
30
|
-
For bugs in [Backbone.Marionette](https://github.com/
|
30
|
+
For bugs in [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) itself, head over to their [issue tracker](https://github.com/derickbailey/backbone.marionette/issues). If you have a question, post it at [StackOverflow under the `backbone.marionette` tag](http://stackoverflow.com/questions/tagged/backbone.marionette).
|
31
31
|
|
32
32
|
For bugs in this gem distribution, use the [GitHub issue tracker](https://github.com/eploko/marionette-amd-rails/issues). If you could submit a pull request - that's even better!
|
33
33
|
|
34
34
|
## Donations
|
35
35
|
|
36
|
-
If you're using Marionette and you're finding that it is saving you time and effort, then please consider donating to the upstream [Backbone.Marionette](https://github.com/
|
36
|
+
If you're using Marionette and you're finding that it is saving you time and effort, then please consider donating to the upstream [Backbone.Marionette](https://github.com/marionettejs/backbone.marionette) project.
|
37
37
|
|
38
38
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7SJHYWJ487SF4)
|
39
39
|
|
40
40
|
## License
|
41
41
|
|
42
|
-
This library is distributed under the MIT license. Please see the LICENSE file.
|
42
|
+
This library is distributed under the MIT license. Please see the LICENSE file.
|
@@ -1,9 +1,6 @@
|
|
1
|
-
// Backbone.Marionette v0.
|
2
|
-
//
|
3
|
-
//
|
4
|
-
// Distributed Under MIT License
|
5
|
-
//
|
6
|
-
// Documentation and Full License Available at:
|
1
|
+
// Backbone.Marionette, v0.10.2
|
2
|
+
// Copyright (c)2012 Derick Bailey, Muted Solutions, LLC.
|
3
|
+
// Distributed under MIT license
|
7
4
|
// http://github.com/derickbailey/backbone.marionette
|
8
5
|
(function (root, factory) {
|
9
6
|
if (typeof exports === 'object') {
|
@@ -18,25 +15,91 @@
|
|
18
15
|
|
19
16
|
define(['jquery', 'underscore', 'backbone'], factory);
|
20
17
|
|
21
|
-
}
|
18
|
+
}
|
22
19
|
}(this, function ($, _, Backbone) {
|
23
20
|
|
24
21
|
Backbone.Marionette = (function(Backbone, _, $){
|
25
|
-
|
22
|
+
var Marionette = {};
|
23
|
+
|
24
|
+
// EventBinder
|
25
|
+
// -----------
|
26
|
+
|
27
|
+
// The event binder facilitates the binding and unbinding of events
|
28
|
+
// from objects that extend `Backbone.Events`. It makes
|
29
|
+
// unbinding events, even with anonymous callback functions,
|
30
|
+
// easy.
|
31
|
+
//
|
32
|
+
// Inspired by [Johnny Oshika](http://stackoverflow.com/questions/7567404/backbone-js-repopulate-or-recreate-the-view/7607853#7607853)
|
33
|
+
|
34
|
+
Marionette.EventBinder = function(){
|
35
|
+
this._eventBindings = [];
|
36
|
+
};
|
37
|
+
|
38
|
+
_.extend(Marionette.EventBinder.prototype, {
|
39
|
+
// Store the event binding in array so it can be unbound
|
40
|
+
// easily, at a later point in time.
|
41
|
+
bindTo: function (obj, eventName, callback, context) {
|
42
|
+
context = context || this;
|
43
|
+
obj.on(eventName, callback, context);
|
44
|
+
|
45
|
+
var binding = {
|
46
|
+
obj: obj,
|
47
|
+
eventName: eventName,
|
48
|
+
callback: callback,
|
49
|
+
context: context
|
50
|
+
};
|
51
|
+
|
52
|
+
this._eventBindings.push(binding);
|
53
|
+
|
54
|
+
return binding;
|
55
|
+
},
|
56
|
+
|
57
|
+
// Unbind from a single binding object. Binding objects are
|
58
|
+
// returned from the `bindTo` method call.
|
59
|
+
unbindFrom: function(binding){
|
60
|
+
binding.obj.off(binding.eventName, binding.callback, binding.context);
|
61
|
+
this._eventBindings = _.reject(this._eventBindings, function(bind){return bind === binding;});
|
62
|
+
},
|
63
|
+
|
64
|
+
// Unbind all of the events that we have stored.
|
65
|
+
unbindAll: function () {
|
66
|
+
var that = this;
|
67
|
+
|
68
|
+
// The `unbindFrom` call removes elements from the array
|
69
|
+
// while it is being iterated, so clone it first.
|
70
|
+
var bindings = _.map(this._eventBindings, _.identity);
|
71
|
+
_.each(bindings, function (binding, index) {
|
72
|
+
that.unbindFrom(binding);
|
73
|
+
});
|
74
|
+
}
|
75
|
+
});
|
26
76
|
|
27
|
-
|
77
|
+
// Copy the `extend` function used by Backbone's classes
|
78
|
+
Marionette.EventBinder.extend = Backbone.View.extend;
|
28
79
|
|
29
80
|
// Marionette.View
|
30
81
|
// ---------------
|
31
82
|
|
32
83
|
// The core view type that other Marionette views extend from.
|
33
84
|
Marionette.View = Backbone.View.extend({
|
34
|
-
|
85
|
+
|
86
|
+
constructor: function(){
|
87
|
+
var eventBinder = new Marionette.EventBinder();
|
88
|
+
_.extend(this, eventBinder);
|
89
|
+
|
90
|
+
Backbone.View.prototype.constructor.apply(this, arguments);
|
91
|
+
|
92
|
+
this.bindBackboneEntityTo(this.model, this.modelEvents);
|
93
|
+
this.bindBackboneEntityTo(this.collection, this.collectionEvents);
|
94
|
+
|
95
|
+
this.bindTo(this, "show", this.onShowCalled, this);
|
96
|
+
},
|
97
|
+
|
98
|
+
// Get the template for this view
|
35
99
|
// instance. You can set a `template` attribute in the view
|
36
100
|
// definition or pass a `template: "whatever"` parameter in
|
37
|
-
// to the constructor options.
|
38
|
-
|
39
|
-
getTemplateSelector: function(){
|
101
|
+
// to the constructor options.
|
102
|
+
getTemplate: function(){
|
40
103
|
var template;
|
41
104
|
|
42
105
|
// Get the template from `this.options.template` or
|
@@ -47,25 +110,20 @@
|
|
47
110
|
template = this.template;
|
48
111
|
}
|
49
112
|
|
50
|
-
// check if it's a function and execute it, if it is
|
51
|
-
if (_.isFunction(template)){
|
52
|
-
template = template.call(this);
|
53
|
-
}
|
54
|
-
|
55
113
|
return template;
|
56
114
|
},
|
57
115
|
|
58
116
|
// Serialize the model or collection for the view. If a model is
|
59
117
|
// found, `.toJSON()` is called. If a collection is found, `.toJSON()`
|
60
118
|
// is also called, but is used to populate an `items` array in the
|
61
|
-
// resulting data. If both are found, defaults to the model.
|
62
|
-
// You can override the `serializeData` method in your own view
|
119
|
+
// resulting data. If both are found, defaults to the model.
|
120
|
+
// You can override the `serializeData` method in your own view
|
63
121
|
// definition, to provide custom serialization for your view's data.
|
64
122
|
serializeData: function(){
|
65
123
|
var data;
|
66
124
|
|
67
|
-
if (this.model) {
|
68
|
-
data = this.model.toJSON();
|
125
|
+
if (this.model) {
|
126
|
+
data = this.model.toJSON();
|
69
127
|
}
|
70
128
|
else if (this.collection) {
|
71
129
|
data = { items: this.collection.toJSON() };
|
@@ -110,16 +168,18 @@
|
|
110
168
|
if (e && e.preventDefault){ e.preventDefault(); }
|
111
169
|
if (e && e.stopPropagation){ e.stopPropagation(); }
|
112
170
|
that.trigger(value);
|
113
|
-
}
|
171
|
+
};
|
114
172
|
|
115
173
|
});
|
116
174
|
|
117
175
|
return triggerEvents;
|
118
176
|
},
|
119
177
|
|
178
|
+
// Overriding Backbone.View's delegateEvents specifically
|
179
|
+
// to handle the `triggers` configuration
|
120
180
|
delegateEvents: function(events){
|
121
181
|
events = events || this.events;
|
122
|
-
if (_.isFunction(events)){ events = events.call(this)}
|
182
|
+
if (_.isFunction(events)){ events = events.call(this); }
|
123
183
|
|
124
184
|
var combinedEvents = {};
|
125
185
|
var triggers = this.configureTriggers();
|
@@ -128,6 +188,9 @@
|
|
128
188
|
Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
|
129
189
|
},
|
130
190
|
|
191
|
+
// Internal method, handles the `show` event.
|
192
|
+
onShowCalled: function(){},
|
193
|
+
|
131
194
|
// Default `close` implementation, for removing a view from the
|
132
195
|
// DOM and unbinding it. Regions will call this method
|
133
196
|
// for you. You can specify an `onClose` method in your view to
|
@@ -135,83 +198,87 @@
|
|
135
198
|
close: function(){
|
136
199
|
if (this.beforeClose) { this.beforeClose(); }
|
137
200
|
|
138
|
-
this.unbindAll();
|
139
201
|
this.remove();
|
140
202
|
|
141
203
|
if (this.onClose) { this.onClose(); }
|
142
204
|
this.trigger('close');
|
205
|
+
this.unbindAll();
|
143
206
|
this.unbind();
|
144
|
-
}
|
145
|
-
});
|
146
|
-
|
147
|
-
// Item View
|
148
|
-
// ---------
|
149
|
-
|
150
|
-
// A single item view implementation that contains code for rendering
|
151
|
-
// with underscore.js templates, serializing the view's model or collection,
|
152
|
-
// and calling several methods on extended views, such as `onRender`.
|
153
|
-
Marionette.ItemView = Marionette.View.extend({
|
154
|
-
constructor: function(){
|
155
|
-
var args = slice.call(arguments);
|
156
|
-
Marionette.View.prototype.constructor.apply(this, args);
|
157
|
-
|
158
|
-
_.bindAll(this, "render");
|
159
|
-
|
160
|
-
this.initialEvents();
|
161
207
|
},
|
162
208
|
|
163
|
-
//
|
164
|
-
//
|
165
|
-
|
166
|
-
|
167
|
-
if (this.collection){
|
168
|
-
this.bindTo(this.collection, "reset", this.render, this);
|
169
|
-
}
|
170
|
-
},
|
209
|
+
// This method binds the elements specified in the "ui" hash inside the view's code with
|
210
|
+
// the associated jQuery selectors.
|
211
|
+
bindUIElements: function(){
|
212
|
+
if (!this.ui) { return; }
|
171
213
|
|
172
|
-
// Render the view, defaulting to underscore.js templates.
|
173
|
-
// You can override this in your view definition.
|
174
|
-
render: function(){
|
175
214
|
var that = this;
|
176
215
|
|
177
|
-
|
216
|
+
if (!this.uiBindings) {
|
217
|
+
// We want to store the ui hash in uiBindings, since afterwards the values in the ui hash
|
218
|
+
// will be overridden with jQuery selectors.
|
219
|
+
this.uiBindings = this.ui;
|
220
|
+
}
|
178
221
|
|
179
|
-
|
180
|
-
|
181
|
-
|
222
|
+
// refreshing the associated selectors since they should point to the newly rendered elements.
|
223
|
+
this.ui = {};
|
224
|
+
_.each(_.keys(this.uiBindings), function(key) {
|
225
|
+
var selector = that.uiBindings[key];
|
226
|
+
that.ui[key] = that.$(selector);
|
227
|
+
});
|
228
|
+
},
|
182
229
|
|
183
|
-
|
184
|
-
|
185
|
-
}
|
230
|
+
// This method is used to bind a backbone "entity" (collection/model) to methods on the view.
|
231
|
+
bindBackboneEntityTo: function(entity, bindings){
|
232
|
+
if (!entity || !bindings) { return; }
|
186
233
|
|
187
|
-
var
|
188
|
-
|
189
|
-
$.when(asyncRender).then(templateRendered);
|
190
|
-
}
|
234
|
+
var view = this;
|
235
|
+
_.each(bindings, function(methodName, evt){
|
191
236
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
237
|
+
var method = view[methodName];
|
238
|
+
if(!method) {
|
239
|
+
throw new Error("View method '"+ methodName +"' was configured as an event handler, but does not exist.");
|
240
|
+
}
|
196
241
|
|
197
|
-
|
198
|
-
|
199
|
-
|
242
|
+
view.bindTo(entity, evt, method, view);
|
243
|
+
});
|
244
|
+
}
|
245
|
+
});
|
200
246
|
|
201
|
-
|
202
|
-
|
247
|
+
// Item View
|
248
|
+
// ---------
|
203
249
|
|
204
|
-
|
250
|
+
// A single item view implementation that contains code for rendering
|
251
|
+
// with underscore.js templates, serializing the view's model or collection,
|
252
|
+
// and calling several methods on extended views, such as `onRender`.
|
253
|
+
Marionette.ItemView = Marionette.View.extend({
|
254
|
+
constructor: function(){
|
255
|
+
Marionette.View.prototype.constructor.apply(this, arguments);
|
205
256
|
|
206
|
-
|
257
|
+
if (this.initialEvents){
|
258
|
+
this.initialEvents();
|
259
|
+
}
|
207
260
|
},
|
208
261
|
|
209
|
-
// Render the
|
210
|
-
//
|
211
|
-
//
|
212
|
-
|
213
|
-
|
214
|
-
|
262
|
+
// Render the view, defaulting to underscore.js templates.
|
263
|
+
// You can override this in your view definition to provide
|
264
|
+
// a very specific rendering for your view. In general, though,
|
265
|
+
// you should override the `Marionette.Renderer` object to
|
266
|
+
// change how Marionette renders views.
|
267
|
+
render: function(){
|
268
|
+
if (this.beforeRender){ this.beforeRender(); }
|
269
|
+
this.trigger("before:render", this);
|
270
|
+
this.trigger("item:before:render", this);
|
271
|
+
|
272
|
+
var data = this.serializeData();
|
273
|
+
var template = this.getTemplate();
|
274
|
+
var html = Marionette.Renderer.render(template, data);
|
275
|
+
this.$el.html(html);
|
276
|
+
this.bindUIElements();
|
277
|
+
|
278
|
+
if (this.onRender){ this.onRender(); }
|
279
|
+
this.trigger("render", this);
|
280
|
+
this.trigger("item:rendered", this);
|
281
|
+
return this;
|
215
282
|
},
|
216
283
|
|
217
284
|
// Override the default close event to add a few
|
@@ -231,12 +298,12 @@
|
|
231
298
|
Marionette.CollectionView = Marionette.View.extend({
|
232
299
|
constructor: function(){
|
233
300
|
Marionette.View.prototype.constructor.apply(this, arguments);
|
234
|
-
|
235
|
-
_.bindAll(this, "addItemView", "render");
|
301
|
+
this.initChildViewStorage();
|
236
302
|
this.initialEvents();
|
303
|
+
this.onShowCallbacks = new Marionette.Callbacks();
|
237
304
|
},
|
238
305
|
|
239
|
-
// Configured the initial events that the collection view
|
306
|
+
// Configured the initial events that the collection view
|
240
307
|
// binds to. Override this method to prevent the initial
|
241
308
|
// events, or to add your own initial events.
|
242
309
|
initialEvents: function(){
|
@@ -248,47 +315,89 @@
|
|
248
315
|
},
|
249
316
|
|
250
317
|
// Handle a child item added to the collection
|
251
|
-
addChildView: function(item){
|
252
|
-
|
253
|
-
|
318
|
+
addChildView: function(item, collection, options){
|
319
|
+
this.closeEmptyView();
|
320
|
+
var ItemView = this.getItemView(item);
|
321
|
+
return this.addItemView(item, ItemView, options.index);
|
254
322
|
},
|
255
323
|
|
256
|
-
//
|
257
|
-
//
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
var promises = [];
|
262
|
-
var ItemView = this.getItemView();
|
324
|
+
// Override from `Marionette.View` to guarantee the `onShow` method
|
325
|
+
// of child views is called.
|
326
|
+
onShowCalled: function(){
|
327
|
+
this.onShowCallbacks.run();
|
328
|
+
},
|
263
329
|
|
330
|
+
// Internal method to trigger the before render callbacks
|
331
|
+
// and events
|
332
|
+
triggerBeforeRender: function(){
|
264
333
|
if (this.beforeRender) { this.beforeRender(); }
|
334
|
+
this.trigger("before:render", this);
|
265
335
|
this.trigger("collection:before:render", this);
|
336
|
+
},
|
337
|
+
|
338
|
+
// Internal method to trigger the rendered callbacks and
|
339
|
+
// events
|
340
|
+
triggerRendered: function(){
|
341
|
+
if (this.onRender) { this.onRender(); }
|
342
|
+
this.trigger("render", this);
|
343
|
+
this.trigger("collection:rendered", this);
|
344
|
+
},
|
266
345
|
|
346
|
+
// Render the collection of items. Override this method to
|
347
|
+
// provide your own implementation of a render function for
|
348
|
+
// the collection view.
|
349
|
+
render: function(){
|
350
|
+
this.triggerBeforeRender();
|
351
|
+
this.closeEmptyView();
|
267
352
|
this.closeChildren();
|
268
353
|
|
269
|
-
if (this.collection) {
|
270
|
-
this.
|
271
|
-
|
272
|
-
|
273
|
-
});
|
354
|
+
if (this.collection && this.collection.length > 0) {
|
355
|
+
this.showCollection();
|
356
|
+
} else {
|
357
|
+
this.showEmptyView();
|
274
358
|
}
|
275
359
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
});
|
360
|
+
this.triggerRendered();
|
361
|
+
return this;
|
362
|
+
},
|
280
363
|
|
281
|
-
|
282
|
-
|
364
|
+
// Internal method to loop through each item in the
|
365
|
+
// collection view and show it
|
366
|
+
showCollection: function(){
|
367
|
+
var that = this;
|
368
|
+
var ItemView;
|
369
|
+
this.collection.each(function(item, index){
|
370
|
+
ItemView = that.getItemView(item);
|
371
|
+
that.addItemView(item, ItemView, index);
|
283
372
|
});
|
373
|
+
},
|
374
|
+
|
375
|
+
// Internal method to show an empty view in place of
|
376
|
+
// a collection of item views, when the collection is
|
377
|
+
// empty
|
378
|
+
showEmptyView: function(){
|
379
|
+
var EmptyView = this.options.emptyView || this.emptyView;
|
380
|
+
if (EmptyView && !this._showingEmptyView){
|
381
|
+
this._showingEmptyView = true;
|
382
|
+
var model = new Backbone.Model();
|
383
|
+
this.addItemView(model, EmptyView, 0);
|
384
|
+
}
|
385
|
+
},
|
284
386
|
|
285
|
-
|
387
|
+
// Internal method to close an existing emptyView instance
|
388
|
+
// if one exists. Called when a collection view has been
|
389
|
+
// rendered empty, and then an item is added to the collection.
|
390
|
+
closeEmptyView: function(){
|
391
|
+
if (this._showingEmptyView){
|
392
|
+
this.closeChildren();
|
393
|
+
delete this._showingEmptyView;
|
394
|
+
}
|
286
395
|
},
|
287
396
|
|
288
397
|
// Retrieve the itemView type, either from `this.options.itemView`
|
289
398
|
// or from the `itemView` in the object definition. The "options"
|
290
399
|
// takes precedence.
|
291
|
-
getItemView: function(){
|
400
|
+
getItemView: function(item){
|
292
401
|
var itemView = this.options.itemView || this.itemView;
|
293
402
|
|
294
403
|
if (!itemView){
|
@@ -302,15 +411,28 @@
|
|
302
411
|
|
303
412
|
// Render the child item's view and add it to the
|
304
413
|
// HTML for the collection view.
|
305
|
-
addItemView: function(item, ItemView){
|
414
|
+
addItemView: function(item, ItemView, index){
|
306
415
|
var that = this;
|
307
416
|
|
308
417
|
var view = this.buildItemView(item, ItemView);
|
309
|
-
this.bindTo(view, "all", function(){
|
310
418
|
|
311
|
-
|
312
|
-
|
313
|
-
|
419
|
+
// Store the child view itself so we can properly
|
420
|
+
// remove and/or close it later
|
421
|
+
this.storeChild(view);
|
422
|
+
if (this.onItemAdded){ this.onItemAdded(view); }
|
423
|
+
this.trigger("item:added", view);
|
424
|
+
|
425
|
+
// Render it and show it
|
426
|
+
var renderResult = this.renderItemView(view, index);
|
427
|
+
|
428
|
+
// call onShow for child item views
|
429
|
+
if (view.onShow){
|
430
|
+
this.onShowCallbacks.add(view.onShow, view);
|
431
|
+
}
|
432
|
+
|
433
|
+
// Forward all child item view events through the parent,
|
434
|
+
// prepending "itemview:" to the event name
|
435
|
+
var childBinding = this.bindTo(view, "all", function(){
|
314
436
|
var args = slice.call(arguments);
|
315
437
|
args[0] = "itemview:" + args[0];
|
316
438
|
args.splice(1, 0, view);
|
@@ -318,22 +440,32 @@
|
|
318
440
|
that.trigger.apply(that, args);
|
319
441
|
});
|
320
442
|
|
321
|
-
|
322
|
-
|
443
|
+
// Store all child event bindings so we can unbind
|
444
|
+
// them when removing / closing the child view
|
445
|
+
this.childBindings = this.childBindings || {};
|
446
|
+
this.childBindings[view.cid] = childBinding;
|
323
447
|
|
324
|
-
|
325
|
-
$.when(viewRendered).then(function(){
|
326
|
-
that.appendHtml(that, view);
|
327
|
-
});
|
328
|
-
|
329
|
-
return viewRendered;
|
448
|
+
return renderResult;
|
330
449
|
},
|
331
450
|
|
332
|
-
//
|
451
|
+
// render the item view
|
452
|
+
renderItemView: function(view, index) {
|
453
|
+
view.render();
|
454
|
+
this.appendHtml(this, view, index);
|
455
|
+
},
|
456
|
+
|
457
|
+
// Build an `itemView` for every model in the collection.
|
333
458
|
buildItemView: function(item, ItemView){
|
334
|
-
var
|
335
|
-
|
336
|
-
|
459
|
+
var itemViewOptions;
|
460
|
+
|
461
|
+
if (_.isFunction(this.itemViewOptions)){
|
462
|
+
itemViewOptions = this.itemViewOptions(item);
|
463
|
+
} else {
|
464
|
+
itemViewOptions = this.itemViewOptions;
|
465
|
+
}
|
466
|
+
|
467
|
+
var options = _.extend({model: item}, itemViewOptions);
|
468
|
+
var view = new ItemView(options);
|
337
469
|
return view;
|
338
470
|
},
|
339
471
|
|
@@ -341,46 +473,63 @@
|
|
341
473
|
removeItemView: function(item){
|
342
474
|
var view = this.children[item.cid];
|
343
475
|
if (view){
|
476
|
+
var childBinding = this.childBindings[view.cid];
|
477
|
+
if (childBinding) {
|
478
|
+
this.unbindFrom(childBinding);
|
479
|
+
delete this.childBindings[view.cid];
|
480
|
+
}
|
344
481
|
view.close();
|
345
482
|
delete this.children[item.cid];
|
346
483
|
}
|
484
|
+
|
485
|
+
if (!this.collection || this.collection.length === 0){
|
486
|
+
this.showEmptyView();
|
487
|
+
}
|
488
|
+
|
347
489
|
this.trigger("item:removed", view);
|
348
490
|
},
|
349
491
|
|
350
492
|
// Append the HTML to the collection's `el`.
|
351
493
|
// Override this method to do something other
|
352
494
|
// then `.append`.
|
353
|
-
appendHtml: function(collectionView, itemView){
|
495
|
+
appendHtml: function(collectionView, itemView, index){
|
354
496
|
collectionView.$el.append(itemView.el);
|
355
497
|
},
|
356
498
|
|
357
499
|
// Store references to all of the child `itemView`
|
358
500
|
// instances so they can be managed and cleaned up, later.
|
359
501
|
storeChild: function(view){
|
360
|
-
if (!this.children){
|
361
|
-
this.children = {};
|
362
|
-
}
|
363
502
|
this.children[view.model.cid] = view;
|
364
503
|
},
|
365
|
-
|
504
|
+
|
505
|
+
// Internal method to set up the `children` object for
|
506
|
+
// storing all of the child views
|
507
|
+
initChildViewStorage: function(){
|
508
|
+
this.children = {};
|
509
|
+
},
|
510
|
+
|
366
511
|
// Handle cleanup and other closing needs for
|
367
512
|
// the collection of views.
|
368
513
|
close: function(){
|
369
514
|
this.trigger("collection:before:close");
|
370
515
|
this.closeChildren();
|
371
|
-
Marionette.View.prototype.close.apply(this, arguments);
|
372
516
|
this.trigger("collection:closed");
|
517
|
+
Marionette.View.prototype.close.apply(this, arguments);
|
373
518
|
},
|
374
519
|
|
520
|
+
// Close the child views that this collection view
|
521
|
+
// is holding on to, if any
|
375
522
|
closeChildren: function(){
|
523
|
+
var that = this;
|
376
524
|
if (this.children){
|
377
|
-
_.each(this.children, function(childView){
|
378
|
-
childView.
|
525
|
+
_.each(_.clone(this.children), function(childView){
|
526
|
+
that.removeItemView(childView.model);
|
379
527
|
});
|
380
528
|
}
|
381
529
|
}
|
382
530
|
});
|
383
531
|
|
532
|
+
|
384
533
|
// Composite View
|
385
534
|
// --------------
|
386
535
|
|
@@ -393,12 +542,31 @@
|
|
393
542
|
this.itemView = this.getItemView();
|
394
543
|
},
|
395
544
|
|
545
|
+
// Configured the initial events that the composite view
|
546
|
+
// binds to. Override this method to prevent the initial
|
547
|
+
// events, or to add your own initial events.
|
548
|
+
initialEvents: function(){
|
549
|
+
if (this.collection){
|
550
|
+
this.bindTo(this.collection, "add", this.addChildView, this);
|
551
|
+
this.bindTo(this.collection, "remove", this.removeItemView, this);
|
552
|
+
this.bindTo(this.collection, "reset", this.renderCollection, this);
|
553
|
+
}
|
554
|
+
},
|
555
|
+
|
396
556
|
// Retrieve the `itemView` to be used when rendering each of
|
397
557
|
// the items in the collection. The default is to return
|
398
558
|
// `this.itemView` or Marionette.CompositeView if no `itemView`
|
399
559
|
// has been defined
|
400
|
-
getItemView: function(){
|
401
|
-
|
560
|
+
getItemView: function(item){
|
561
|
+
var itemView = this.options.itemView || this.itemView || this.constructor;
|
562
|
+
|
563
|
+
if (!itemView){
|
564
|
+
var err = new Error("An `itemView` must be specified");
|
565
|
+
err.name = "NoItemViewError";
|
566
|
+
throw err;
|
567
|
+
}
|
568
|
+
|
569
|
+
return itemView;
|
402
570
|
},
|
403
571
|
|
404
572
|
// Renders the model once, and the collection once. Calling
|
@@ -406,34 +574,26 @@
|
|
406
574
|
// but the collection will not re-render.
|
407
575
|
render: function(){
|
408
576
|
var that = this;
|
409
|
-
var compositeRendered = $.Deferred();
|
410
577
|
|
411
|
-
|
412
|
-
$.when(modelIsRendered).then(function(html){
|
413
|
-
that.$el.html(html);
|
414
|
-
that.trigger("composite:model:rendered");
|
415
|
-
that.trigger("render");
|
578
|
+
this.resetItemViewContainer();
|
416
579
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
that.trigger("composite:rendered");
|
425
|
-
});
|
580
|
+
var html = this.renderModel();
|
581
|
+
this.$el.html(html);
|
582
|
+
// the ui bindings is done here and not at the end of render since they should be
|
583
|
+
// available before the collection is rendered.
|
584
|
+
this.bindUIElements();
|
585
|
+
this.trigger("composite:model:rendered");
|
586
|
+
this.trigger("render");
|
426
587
|
|
427
|
-
|
588
|
+
this.renderCollection();
|
589
|
+
this.trigger("composite:rendered");
|
590
|
+
return this;
|
428
591
|
},
|
429
592
|
|
430
593
|
// Render the collection for the composite view
|
431
594
|
renderCollection: function(){
|
432
|
-
|
433
|
-
|
434
|
-
this.trigger("composite:collection:rendered");
|
435
|
-
});
|
436
|
-
return collectionDeferred.promise();
|
595
|
+
Marionette.CollectionView.prototype.render.apply(this, arguments);
|
596
|
+
this.trigger("composite:collection:rendered");
|
437
597
|
},
|
438
598
|
|
439
599
|
// Render an individual model, if we have one, as
|
@@ -443,12 +603,52 @@
|
|
443
603
|
var data = {};
|
444
604
|
data = this.serializeData();
|
445
605
|
|
446
|
-
var template = this.
|
606
|
+
var template = this.getTemplate();
|
447
607
|
return Marionette.Renderer.render(template, data);
|
608
|
+
},
|
609
|
+
|
610
|
+
// Appends the `el` of itemView instances to the specified
|
611
|
+
// `itemViewContainer` (a jQuery selector). Override this method to
|
612
|
+
// provide custom logic of how the child item view instances have their
|
613
|
+
// HTML appended to the composite view instance.
|
614
|
+
appendHtml: function(cv, iv){
|
615
|
+
var $container = this.getItemViewContainer(cv);
|
616
|
+
$container.append(iv.el);
|
617
|
+
},
|
618
|
+
|
619
|
+
// Internal method to ensure an `$itemViewContainer` exists, for the
|
620
|
+
// `appendHtml` method to use.
|
621
|
+
getItemViewContainer: function(containerView){
|
622
|
+
var container;
|
623
|
+
if ("$itemViewContainer" in containerView){
|
624
|
+
container = containerView.$itemViewContainer;
|
625
|
+
} else {
|
626
|
+
if (containerView.itemViewContainer){
|
627
|
+
container = containerView.$(_.result(containerView, "itemViewContainer"));
|
628
|
+
|
629
|
+
if (container.length <= 0) {
|
630
|
+
var err = new Error("Missing `itemViewContainer`");
|
631
|
+
err.name = "ItemViewContainerMissingError";
|
632
|
+
throw err;
|
633
|
+
}
|
634
|
+
} else {
|
635
|
+
container = containerView.$el;
|
636
|
+
}
|
637
|
+
containerView.$itemViewContainer = container;
|
638
|
+
}
|
639
|
+
return container;
|
640
|
+
},
|
641
|
+
|
642
|
+
// Internal method to reset the `$itemViewContainer` on render
|
643
|
+
resetItemViewContainer: function(){
|
644
|
+
if (this.$itemViewContainer){
|
645
|
+
delete this.$itemViewContainer;
|
646
|
+
}
|
448
647
|
}
|
449
648
|
});
|
450
649
|
|
451
|
-
|
650
|
+
|
651
|
+
// Region
|
452
652
|
// ------
|
453
653
|
|
454
654
|
// Manage the visual regions of your composite application. See
|
@@ -456,7 +656,8 @@
|
|
456
656
|
Marionette.Region = function(options){
|
457
657
|
this.options = options || {};
|
458
658
|
|
459
|
-
|
659
|
+
var eventBinder = new Marionette.EventBinder();
|
660
|
+
_.extend(this, eventBinder, options);
|
460
661
|
|
461
662
|
if (!this.el){
|
462
663
|
var err = new Error("An 'el' must be specified");
|
@@ -476,11 +677,19 @@
|
|
476
677
|
// directly from the `el` attribute. Also calls an optional
|
477
678
|
// `onShow` and `close` method on your view, just after showing
|
478
679
|
// or just before closing the view, respectively.
|
479
|
-
show: function(view
|
480
|
-
this.ensureEl();
|
680
|
+
show: function(view){
|
481
681
|
|
682
|
+
this.ensureEl();
|
482
683
|
this.close();
|
483
|
-
|
684
|
+
|
685
|
+
view.render();
|
686
|
+
this.open(view);
|
687
|
+
|
688
|
+
if (view.onShow) { view.onShow(); }
|
689
|
+
view.trigger("show");
|
690
|
+
|
691
|
+
if (this.onShow) { this.onShow(view); }
|
692
|
+
this.trigger("view:show", view);
|
484
693
|
|
485
694
|
this.currentView = view;
|
486
695
|
},
|
@@ -494,22 +703,13 @@
|
|
494
703
|
// Override this method to change how the region finds the
|
495
704
|
// DOM element that it manages. Return a jQuery selector object.
|
496
705
|
getEl: function(selector){
|
497
|
-
|
706
|
+
return $(selector);
|
498
707
|
},
|
499
708
|
|
500
|
-
//
|
501
|
-
// to
|
502
|
-
open: function(view
|
503
|
-
|
504
|
-
appendMethod = appendMethod || "html";
|
505
|
-
|
506
|
-
$.when(view.render()).then(function () {
|
507
|
-
that.$el[appendMethod](view.el);
|
508
|
-
if (view.onShow) { view.onShow(); }
|
509
|
-
if (that.onShow) { that.onShow(view); }
|
510
|
-
view.trigger("show");
|
511
|
-
that.trigger("view:show", view);
|
512
|
-
});
|
709
|
+
// Override this method to change how the new view is
|
710
|
+
// appended to the `$el` that the region is managing
|
711
|
+
open: function(view){
|
712
|
+
this.$el.html(view.el);
|
513
713
|
},
|
514
714
|
|
515
715
|
// Close the current view, if there is one. If there is no
|
@@ -524,20 +724,30 @@
|
|
524
724
|
delete this.currentView;
|
525
725
|
},
|
526
726
|
|
527
|
-
// Attach an existing view to the region. This
|
528
|
-
// will not call `render` or `onShow` for the new view,
|
727
|
+
// Attach an existing view to the region. This
|
728
|
+
// will not call `render` or `onShow` for the new view,
|
529
729
|
// and will not replace the current HTML for the `el`
|
530
730
|
// of the region.
|
531
731
|
attachView: function(view){
|
532
732
|
this.currentView = view;
|
733
|
+
},
|
734
|
+
|
735
|
+
// Reset the region by closing any existing view and
|
736
|
+
// clearing out the cached `$el`. The next time a view
|
737
|
+
// is shown via this region, the region will re-query the
|
738
|
+
// DOM for the region's `el`.
|
739
|
+
reset: function(){
|
740
|
+
this.close();
|
741
|
+
delete this.$el;
|
533
742
|
}
|
534
743
|
});
|
535
744
|
|
745
|
+
// Copy the `extend` function used by Backbone's classes
|
746
|
+
Marionette.Region.extend = Backbone.View.extend;
|
747
|
+
|
536
748
|
// Layout
|
537
749
|
// ------
|
538
750
|
|
539
|
-
// Formerly known as Composite Region.
|
540
|
-
//
|
541
751
|
// Used for managing application layouts, nested layouts and
|
542
752
|
// multiple regions within an application or sub-application.
|
543
753
|
//
|
@@ -545,19 +755,37 @@
|
|
545
755
|
// attaches `Region` instances to the specified `regions`.
|
546
756
|
// Used for composite view management and sub-application areas.
|
547
757
|
Marionette.Layout = Marionette.ItemView.extend({
|
758
|
+
regionType: Marionette.Region,
|
759
|
+
|
760
|
+
// Ensure the regions are avialable when the `initialize` method
|
761
|
+
// is called.
|
548
762
|
constructor: function () {
|
549
|
-
this.
|
763
|
+
this.initializeRegions();
|
550
764
|
Backbone.Marionette.ItemView.apply(this, arguments);
|
551
|
-
this.regionManagers = {};
|
552
765
|
},
|
553
766
|
|
554
|
-
render
|
555
|
-
|
556
|
-
|
767
|
+
// Layout's render will use the existing region objects the
|
768
|
+
// first time it is called. Subsequent calls will close the
|
769
|
+
// views that the regions are showing and then reset the `el`
|
770
|
+
// for the regions to the newly rendered DOM elements.
|
771
|
+
render: function(){
|
772
|
+
// If this is not the first render call, then we need to
|
773
|
+
// re-initializing the `el` for each region
|
774
|
+
if (!this._firstRender){
|
775
|
+
this.closeRegions();
|
776
|
+
this.reInitializeRegions();
|
777
|
+
} else {
|
778
|
+
this._firstRender = false;
|
779
|
+
}
|
780
|
+
|
781
|
+
var result = Marionette.ItemView.prototype.render.apply(this, arguments);
|
782
|
+
return result;
|
557
783
|
},
|
558
784
|
|
785
|
+
// Handle closing regions, and then close the view itself.
|
559
786
|
close: function () {
|
560
787
|
this.closeRegions();
|
788
|
+
this.destroyRegions();
|
561
789
|
Backbone.Marionette.ItemView.prototype.close.call(this, arguments);
|
562
790
|
},
|
563
791
|
|
@@ -568,18 +796,57 @@
|
|
568
796
|
// will product a `layout.menu` object which is a region
|
569
797
|
// that controls the `.menu-container` DOM element.
|
570
798
|
initializeRegions: function () {
|
799
|
+
if (!this.regionManagers){
|
800
|
+
this.regionManagers = {};
|
801
|
+
}
|
802
|
+
|
571
803
|
var that = this;
|
572
|
-
_.each(this.regions, function (
|
573
|
-
var
|
574
|
-
|
804
|
+
_.each(this.regions, function (region, name) {
|
805
|
+
var regionIsString = (typeof region === "string");
|
806
|
+
var regionSelectorIsString = (typeof region.selector === "string");
|
807
|
+
var regionTypeIsUndefined = (typeof region.regionType === "undefined");
|
808
|
+
|
809
|
+
if (!regionIsString && !regionSelectorIsString) {
|
810
|
+
throw new Error("Region must be specified as a selector string or an object with selector property");
|
811
|
+
}
|
812
|
+
|
813
|
+
var selector, RegionType;
|
814
|
+
|
815
|
+
if (regionIsString) {
|
816
|
+
selector = region;
|
817
|
+
} else {
|
818
|
+
selector = region.selector;
|
819
|
+
}
|
820
|
+
|
821
|
+
if (regionTypeIsUndefined){
|
822
|
+
RegionType = that.regionType;
|
823
|
+
} else {
|
824
|
+
RegionType = region.regionType;
|
825
|
+
}
|
575
826
|
|
827
|
+
var regionManager = new RegionType({
|
828
|
+
el: selector,
|
576
829
|
getEl: function(selector){
|
577
830
|
return that.$(selector);
|
578
831
|
}
|
579
832
|
});
|
833
|
+
|
580
834
|
that.regionManagers[name] = regionManager;
|
581
835
|
that[name] = regionManager;
|
582
836
|
});
|
837
|
+
|
838
|
+
},
|
839
|
+
|
840
|
+
// Re-initialize all of the regions by updating the `el` that
|
841
|
+
// they point to
|
842
|
+
reInitializeRegions: function(){
|
843
|
+
if (this.regionManagers && _.size(this.regionManagers)===0){
|
844
|
+
this.initializeRegions();
|
845
|
+
} else {
|
846
|
+
_.each(this.regionManagers, function(region){
|
847
|
+
region.reset();
|
848
|
+
});
|
849
|
+
}
|
583
850
|
},
|
584
851
|
|
585
852
|
// Close all of the regions that have been opened by
|
@@ -589,12 +856,102 @@
|
|
589
856
|
var that = this;
|
590
857
|
_.each(this.regionManagers, function (manager, name) {
|
591
858
|
manager.close();
|
859
|
+
});
|
860
|
+
},
|
861
|
+
|
862
|
+
// Destroys all of the regions by removing references
|
863
|
+
// from the Layout
|
864
|
+
destroyRegions: function(){
|
865
|
+
var that = this;
|
866
|
+
_.each(this.regionManagers, function (manager, name) {
|
592
867
|
delete that[name];
|
593
868
|
});
|
594
869
|
this.regionManagers = {};
|
595
870
|
}
|
596
871
|
});
|
597
872
|
|
873
|
+
|
874
|
+
// Application
|
875
|
+
// -----------
|
876
|
+
|
877
|
+
// Contain and manage the composite application as a whole.
|
878
|
+
// Stores and starts up `Region` objects, includes an
|
879
|
+
// event aggregator as `app.vent`
|
880
|
+
Marionette.Application = function(options){
|
881
|
+
this.initCallbacks = new Marionette.Callbacks();
|
882
|
+
this.vent = new Marionette.EventAggregator();
|
883
|
+
this.submodules = {};
|
884
|
+
|
885
|
+
var eventBinder = new Marionette.EventBinder();
|
886
|
+
_.extend(this, eventBinder, options);
|
887
|
+
};
|
888
|
+
|
889
|
+
_.extend(Marionette.Application.prototype, Backbone.Events, {
|
890
|
+
// Add an initializer that is either run at when the `start`
|
891
|
+
// method is called, or run immediately if added after `start`
|
892
|
+
// has already been called.
|
893
|
+
addInitializer: function(initializer){
|
894
|
+
this.initCallbacks.add(initializer);
|
895
|
+
},
|
896
|
+
|
897
|
+
// kick off all of the application's processes.
|
898
|
+
// initializes all of the regions that have been added
|
899
|
+
// to the app, and runs all of the initializer functions
|
900
|
+
start: function(options){
|
901
|
+
this.trigger("initialize:before", options);
|
902
|
+
this.initCallbacks.run(options, this);
|
903
|
+
this.trigger("initialize:after", options);
|
904
|
+
|
905
|
+
this.trigger("start", options);
|
906
|
+
},
|
907
|
+
|
908
|
+
// Add regions to your app.
|
909
|
+
// Accepts a hash of named strings or Region objects
|
910
|
+
// addRegions({something: "#someRegion"})
|
911
|
+
// addRegions{{something: Region.extend({el: "#someRegion"}) });
|
912
|
+
addRegions: function(regions){
|
913
|
+
var RegionValue, regionObj, region;
|
914
|
+
|
915
|
+
for(region in regions){
|
916
|
+
if (regions.hasOwnProperty(region)){
|
917
|
+
RegionValue = regions[region];
|
918
|
+
|
919
|
+
if (typeof RegionValue === "string"){
|
920
|
+
regionObj = new Marionette.Region({
|
921
|
+
el: RegionValue
|
922
|
+
});
|
923
|
+
} else {
|
924
|
+
regionObj = new RegionValue();
|
925
|
+
}
|
926
|
+
|
927
|
+
this[region] = regionObj;
|
928
|
+
}
|
929
|
+
}
|
930
|
+
},
|
931
|
+
|
932
|
+
// Removes a region from your app.
|
933
|
+
// Accepts the regions name
|
934
|
+
// removeRegion('myRegion')
|
935
|
+
removeRegion: function(region) {
|
936
|
+
this[region].close();
|
937
|
+
delete this[region];
|
938
|
+
},
|
939
|
+
|
940
|
+
// Create a module, attached to the application
|
941
|
+
module: function(moduleNames, moduleDefinition){
|
942
|
+
// slice the args, and add this application object as the
|
943
|
+
// first argument of the array
|
944
|
+
var args = slice.call(arguments);
|
945
|
+
args.unshift(this);
|
946
|
+
|
947
|
+
// see the Marionette.Module object for more information
|
948
|
+
return Marionette.Module.create.apply(Marionette.Module, args);
|
949
|
+
}
|
950
|
+
});
|
951
|
+
|
952
|
+
// Copy the `extend` function used by Backbone's classes
|
953
|
+
Marionette.Application.extend = Backbone.View.extend;
|
954
|
+
|
598
955
|
// AppRouter
|
599
956
|
// ---------
|
600
957
|
|
@@ -605,14 +962,14 @@
|
|
605
962
|
//
|
606
963
|
// Configure an AppRouter with `appRoutes`.
|
607
964
|
//
|
608
|
-
// App routers can only take one `controller` object.
|
965
|
+
// App routers can only take one `controller` object.
|
609
966
|
// It is recommended that you divide your controller
|
610
967
|
// objects in to smaller peices of related functionality
|
611
968
|
// and have multiple routers / controllers, instead of
|
612
969
|
// just one giant router and controller.
|
613
970
|
//
|
614
971
|
// You can also add standard routes to an AppRouter.
|
615
|
-
|
972
|
+
|
616
973
|
Marionette.AppRouter = Backbone.Router.extend({
|
617
974
|
|
618
975
|
constructor: function(options){
|
@@ -627,6 +984,9 @@
|
|
627
984
|
}
|
628
985
|
},
|
629
986
|
|
987
|
+
// Internal method to process the `appRoutes` for the
|
988
|
+
// router, and turn them in to routes that trigger the
|
989
|
+
// specified method on the specified `controller`.
|
630
990
|
processAppRoutes: function(controller, appRoutes){
|
631
991
|
var method, methodName;
|
632
992
|
var route, routesLength, i;
|
@@ -657,206 +1017,304 @@
|
|
657
1017
|
}
|
658
1018
|
}
|
659
1019
|
});
|
660
|
-
|
661
|
-
// Composite Application
|
662
|
-
// ---------------------
|
663
1020
|
|
664
|
-
|
665
|
-
//
|
666
|
-
//
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
1021
|
+
|
1022
|
+
// Module
|
1023
|
+
// ------
|
1024
|
+
|
1025
|
+
// A simple module system, used to create privacy and encapsulation in
|
1026
|
+
// Marionette applications
|
1027
|
+
Marionette.Module = function(moduleName, app, customArgs){
|
1028
|
+
this.moduleName = moduleName;
|
1029
|
+
|
1030
|
+
// store sub-modules
|
1031
|
+
this.submodules = {};
|
1032
|
+
|
1033
|
+
this._setupInitializersAndFinalizers();
|
1034
|
+
|
1035
|
+
// store the configuration for this module
|
1036
|
+
this.config = {};
|
1037
|
+
this.config.app = app;
|
1038
|
+
this.config.customArgs = customArgs;
|
1039
|
+
this.config.definitions = [];
|
1040
|
+
|
1041
|
+
// extend this module with an event binder
|
1042
|
+
var eventBinder = new Marionette.EventBinder();
|
1043
|
+
_.extend(this, eventBinder);
|
671
1044
|
};
|
672
1045
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
1046
|
+
// Extend the Module prototype with events / bindTo, so that the module
|
1047
|
+
// can be used as an event aggregator or pub/sub.
|
1048
|
+
_.extend(Marionette.Module.prototype, Backbone.Events, {
|
1049
|
+
|
1050
|
+
// Initializer for a specific module. Initializers are run when the
|
1051
|
+
// module's `start` method is called.
|
1052
|
+
addInitializer: function(callback){
|
1053
|
+
this._initializerCallbacks.add(callback);
|
679
1054
|
},
|
680
1055
|
|
681
|
-
//
|
682
|
-
//
|
683
|
-
//
|
1056
|
+
// Finalizers are run when a module is stopped. They are used to teardown
|
1057
|
+
// and finalize any variables, references, events and other code that the
|
1058
|
+
// module had set up.
|
1059
|
+
addFinalizer: function(callback){
|
1060
|
+
this._finalizerCallbacks.add(callback);
|
1061
|
+
},
|
1062
|
+
|
1063
|
+
// Start the module, and run all of it's initializers
|
684
1064
|
start: function(options){
|
685
|
-
|
686
|
-
this.
|
687
|
-
this.trigger("initialize:after", options);
|
1065
|
+
// Prevent re-start the module
|
1066
|
+
if (this._isInitialized){ return; }
|
688
1067
|
|
689
|
-
|
1068
|
+
// start the sub-modules (depth-first hierarchy)
|
1069
|
+
_.each(this.submodules, function(mod){
|
1070
|
+
if (mod.config.options.startWithParent){
|
1071
|
+
mod.start(options);
|
1072
|
+
}
|
1073
|
+
});
|
1074
|
+
|
1075
|
+
// run the callbacks to "start" the current module
|
1076
|
+
this._initializerCallbacks.run(options, this);
|
1077
|
+
this._isInitialized = true;
|
690
1078
|
},
|
691
1079
|
|
692
|
-
//
|
693
|
-
//
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
1080
|
+
// Stop this module by running its finalizers and then stop all of
|
1081
|
+
// the sub-modules for this module
|
1082
|
+
stop: function(){
|
1083
|
+
// if we are not initialized, don't bother finalizing
|
1084
|
+
if (!this._isInitialized){ return; }
|
1085
|
+
this._isInitialized = false;
|
698
1086
|
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
if (typeof regionValue === "string"){
|
704
|
-
regionObj = new Marionette.Region({
|
705
|
-
el: regionValue
|
706
|
-
});
|
707
|
-
} else {
|
708
|
-
regionObj = new regionValue();
|
709
|
-
}
|
1087
|
+
// stop the sub-modules; depth-first, to make sure the
|
1088
|
+
// sub-modules are stopped / finalized before parents
|
1089
|
+
_.each(this.submodules, function(mod){ mod.stop(); });
|
710
1090
|
|
711
|
-
|
712
|
-
|
713
|
-
|
1091
|
+
// run the finalizers
|
1092
|
+
this._finalizerCallbacks.run();
|
1093
|
+
|
1094
|
+
// reset the initializers and finalizers
|
1095
|
+
this._initializerCallbacks.reset();
|
1096
|
+
this._finalizerCallbacks.reset();
|
1097
|
+
},
|
1098
|
+
|
1099
|
+
// Configure the module with a definition function and any custom args
|
1100
|
+
// that are to be passed in to the definition function
|
1101
|
+
addDefinition: function(moduleDefinition){
|
1102
|
+
this._runModuleDefinition(moduleDefinition);
|
1103
|
+
},
|
1104
|
+
|
1105
|
+
// Internal method: run the module definition function with the correct
|
1106
|
+
// arguments
|
1107
|
+
_runModuleDefinition: function(definition){
|
1108
|
+
if (!definition){ return; }
|
1109
|
+
|
1110
|
+
// build the correct list of arguments for the module definition
|
1111
|
+
var args = _.flatten([
|
1112
|
+
this,
|
1113
|
+
this.config.app,
|
1114
|
+
Backbone,
|
1115
|
+
Marionette,
|
1116
|
+
$, _,
|
1117
|
+
this.config.customArgs
|
1118
|
+
]);
|
1119
|
+
|
1120
|
+
definition.apply(this, args);
|
1121
|
+
},
|
1122
|
+
|
1123
|
+
// Internal method: set up new copies of initializers and finalizers.
|
1124
|
+
// Calling this method will wipe out all existing initializers and
|
1125
|
+
// finalizers.
|
1126
|
+
_setupInitializersAndFinalizers: function(){
|
1127
|
+
this._initializerCallbacks = new Marionette.Callbacks();
|
1128
|
+
this._finalizerCallbacks = new Marionette.Callbacks();
|
714
1129
|
}
|
715
1130
|
});
|
716
1131
|
|
717
|
-
//
|
718
|
-
|
719
|
-
|
720
|
-
// BindTo facilitates the binding and unbinding of events
|
721
|
-
// from objects that extend `Backbone.Events`. It makes
|
722
|
-
// unbinding events, even with anonymous callback functions,
|
723
|
-
// easy.
|
724
|
-
//
|
725
|
-
// Thanks to Johnny Oshika for this code.
|
726
|
-
// http://stackoverflow.com/questions/7567404/backbone-js-repopulate-or-recreate-the-view/7607853#7607853
|
727
|
-
Marionette.BindTo = {
|
728
|
-
// Store the event binding in array so it can be unbound
|
729
|
-
// easily, at a later point in time.
|
730
|
-
bindTo: function (obj, eventName, callback, context) {
|
731
|
-
context = context || this;
|
732
|
-
obj.on(eventName, callback, context);
|
1132
|
+
// Function level methods to create modules
|
1133
|
+
_.extend(Marionette.Module, {
|
733
1134
|
|
734
|
-
|
1135
|
+
// Create a module, hanging off the app parameter as the parent object.
|
1136
|
+
create: function(app, moduleNames, moduleDefinition){
|
1137
|
+
var that = this;
|
1138
|
+
var parentModule = app;
|
1139
|
+
moduleNames = moduleNames.split(".");
|
735
1140
|
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
context: context
|
741
|
-
}
|
1141
|
+
// get the custom args passed in after the module definition and
|
1142
|
+
// get rid of the module name and definition function
|
1143
|
+
var customArgs = slice.apply(arguments);
|
1144
|
+
customArgs.splice(0, 3);
|
742
1145
|
|
743
|
-
|
1146
|
+
// Loop through all the parts of the module definition
|
1147
|
+
var length = moduleNames.length;
|
1148
|
+
_.each(moduleNames, function(moduleName, i){
|
1149
|
+
var isLastModuleInChain = (i === length-1);
|
744
1150
|
|
745
|
-
|
746
|
-
|
1151
|
+
var module = that._getModuleDefinition(parentModule, moduleName, app, customArgs);
|
1152
|
+
module.config.options = that._getModuleOptions(parentModule, moduleDefinition);
|
747
1153
|
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
},
|
1154
|
+
// if it's the first module in the chain, configure it
|
1155
|
+
// for auto-start, as specified by the options
|
1156
|
+
if (isLastModuleInChain){
|
1157
|
+
that._configureAutoStart(app, module);
|
1158
|
+
}
|
754
1159
|
|
755
|
-
|
756
|
-
|
757
|
-
|
1160
|
+
// Only add a module definition and initializer when this is
|
1161
|
+
// the last module in a "parent.child.grandchild" hierarchy of
|
1162
|
+
// module names
|
1163
|
+
if (isLastModuleInChain && module.config.options.hasDefinition){
|
1164
|
+
module.addDefinition(module.config.options.definition);
|
1165
|
+
}
|
758
1166
|
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
_.each(bindings, function (binding, index) {
|
763
|
-
that.unbindFrom(binding);
|
1167
|
+
// Reset the parent module so that the next child
|
1168
|
+
// in the list will be added to the correct parent
|
1169
|
+
parentModule = module;
|
764
1170
|
});
|
765
|
-
}
|
766
|
-
};
|
767
1171
|
|
768
|
-
|
769
|
-
|
1172
|
+
// Return the last module in the definition chain
|
1173
|
+
return parentModule;
|
1174
|
+
},
|
770
1175
|
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
1176
|
+
_configureAutoStart: function(app, module){
|
1177
|
+
// Only add the initializer if it's the first module, and
|
1178
|
+
// if it is set to auto-start, and if it has not yet been added
|
1179
|
+
if (module.config.options.startWithParent && !module.config.autoStartConfigured){
|
1180
|
+
// start the module when the app starts
|
1181
|
+
app.addInitializer(function(options){
|
1182
|
+
module.start(options);
|
1183
|
+
});
|
1184
|
+
}
|
778
1185
|
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
// `run` method is called.
|
784
|
-
add: function(callback){
|
785
|
-
this.promise.done(function(context, options){
|
786
|
-
callback.call(context, options);
|
787
|
-
});
|
1186
|
+
// prevent this module from being configured for
|
1187
|
+
// auto start again. the first time the module
|
1188
|
+
// is defined, determines it's auto-start
|
1189
|
+
module.config.autoStartConfigured = true;
|
788
1190
|
},
|
789
1191
|
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
run: function(context, options){
|
794
|
-
this.deferred.resolve(context, options);
|
795
|
-
}
|
796
|
-
});
|
1192
|
+
_getModuleDefinition: function(parentModule, moduleName, app, customArgs){
|
1193
|
+
// Get an existing module of this name if we have one
|
1194
|
+
var module = parentModule[moduleName];
|
797
1195
|
|
798
|
-
|
799
|
-
|
1196
|
+
if (!module){
|
1197
|
+
// Create a new module if we don't have one
|
1198
|
+
module = new Marionette.Module(moduleName, app, customArgs);
|
1199
|
+
parentModule[moduleName] = module;
|
1200
|
+
// store the module on the parent
|
1201
|
+
parentModule.submodules[moduleName] = module;
|
1202
|
+
}
|
800
1203
|
|
801
|
-
|
802
|
-
|
803
|
-
Marionette.EventAggregator = function(options){
|
804
|
-
_.extend(this, options);
|
805
|
-
};
|
1204
|
+
return module;
|
1205
|
+
},
|
806
1206
|
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
1207
|
+
_getModuleOptions: function(parentModule, moduleDefinition){
|
1208
|
+
// default to starting the module with the app
|
1209
|
+
var options = {
|
1210
|
+
startWithParent: true,
|
1211
|
+
hasDefinition: !!moduleDefinition
|
1212
|
+
};
|
1213
|
+
|
1214
|
+
// short circuit if we don't have a module definition
|
1215
|
+
if (!options.hasDefinition){ return options; }
|
1216
|
+
|
1217
|
+
if (_.isFunction(moduleDefinition)){
|
1218
|
+
// if the definition is a function, assign it directly
|
1219
|
+
// and use the defaults
|
1220
|
+
options.definition = moduleDefinition;
|
1221
|
+
|
1222
|
+
} else {
|
1223
|
+
|
1224
|
+
// the definition is an object.
|
1225
|
+
|
1226
|
+
// grab the "define" attribute
|
1227
|
+
options.hasDefinition = !!moduleDefinition.define;
|
1228
|
+
options.definition = moduleDefinition.define;
|
1229
|
+
|
1230
|
+
// grab the "startWithParent" attribute if one exists
|
1231
|
+
if (moduleDefinition.hasOwnProperty("startWithParent")){
|
1232
|
+
options.startWithParent = moduleDefinition.startWithParent;
|
1233
|
+
}
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
return options;
|
812
1237
|
}
|
813
1238
|
});
|
814
1239
|
|
815
1240
|
// Template Cache
|
816
1241
|
// --------------
|
817
|
-
|
1242
|
+
|
818
1243
|
// Manage templates stored in `<script>` blocks,
|
819
1244
|
// caching them for faster access.
|
820
|
-
Marionette.TemplateCache = {
|
821
|
-
|
822
|
-
|
1245
|
+
Marionette.TemplateCache = function(templateId){
|
1246
|
+
this.templateId = templateId;
|
1247
|
+
};
|
1248
|
+
|
1249
|
+
// TemplateCache object-level methods. Manage the template
|
1250
|
+
// caches from these method calls instead of creating
|
1251
|
+
// your own TemplateCache instances
|
1252
|
+
_.extend(Marionette.TemplateCache, {
|
1253
|
+
templateCaches: {},
|
823
1254
|
|
824
1255
|
// Get the specified template by id. Either
|
825
1256
|
// retrieves the cached version, or loads it
|
826
1257
|
// from the DOM.
|
827
1258
|
get: function(templateId){
|
828
1259
|
var that = this;
|
829
|
-
var
|
830
|
-
var cachedTemplate = this.templates[templateId];
|
1260
|
+
var cachedTemplate = this.templateCaches[templateId];
|
831
1261
|
|
832
|
-
if (cachedTemplate){
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
this.loaders[templateId] = templateRetrieval;
|
1262
|
+
if (!cachedTemplate){
|
1263
|
+
cachedTemplate = new Marionette.TemplateCache(templateId);
|
1264
|
+
this.templateCaches[templateId] = cachedTemplate;
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
return cachedTemplate.load();
|
1268
|
+
},
|
840
1269
|
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
1270
|
+
// Clear templates from the cache. If no arguments
|
1271
|
+
// are specified, clears all templates:
|
1272
|
+
// `clear()`
|
1273
|
+
//
|
1274
|
+
// If arguments are specified, clears each of the
|
1275
|
+
// specified templates from the cache:
|
1276
|
+
// `clear("#t1", "#t2", "...")`
|
1277
|
+
clear: function(){
|
1278
|
+
var i;
|
1279
|
+
var length = arguments.length;
|
1280
|
+
|
1281
|
+
if (length > 0){
|
1282
|
+
for(i=0; i<length; i++){
|
1283
|
+
delete this.templateCaches[arguments[i]];
|
846
1284
|
}
|
1285
|
+
} else {
|
1286
|
+
this.templateCaches = {};
|
1287
|
+
}
|
1288
|
+
}
|
1289
|
+
});
|
1290
|
+
|
1291
|
+
// TemplateCache instance methods, allowing each
|
1292
|
+
// template cache object to manage it's own state
|
1293
|
+
// and know whether or not it has been loaded
|
1294
|
+
_.extend(Marionette.TemplateCache.prototype, {
|
1295
|
+
|
1296
|
+
// Internal method to load the template asynchronously.
|
1297
|
+
load: function(){
|
1298
|
+
var that = this;
|
847
1299
|
|
1300
|
+
// Guard clause to prevent loading this template more than once
|
1301
|
+
if (this.compiledTemplate){
|
1302
|
+
return this.compiledTemplate;
|
848
1303
|
}
|
849
1304
|
|
850
|
-
|
1305
|
+
// Load the template and compile it
|
1306
|
+
var template = this.loadTemplate(this.templateId);
|
1307
|
+
this.compiledTemplate = this.compileTemplate(template);
|
1308
|
+
|
1309
|
+
return this.compiledTemplate;
|
851
1310
|
},
|
852
1311
|
|
853
1312
|
// Load a template from the DOM, by default. Override
|
854
1313
|
// this method to provide your own template retrieval,
|
855
1314
|
// such as asynchronous loading from a server.
|
856
|
-
loadTemplate: function(templateId
|
1315
|
+
loadTemplate: function(templateId){
|
857
1316
|
var template = $(templateId).html();
|
858
1317
|
|
859
|
-
// Make sure we have a template before trying to compile it
|
860
1318
|
if (!template || template.length === 0){
|
861
1319
|
var msg = "Could not find template: '" + templateId + "'";
|
862
1320
|
var err = new Error(msg);
|
@@ -864,9 +1322,7 @@
|
|
864
1322
|
throw err;
|
865
1323
|
}
|
866
1324
|
|
867
|
-
|
868
|
-
|
869
|
-
callback.call(this, template);
|
1325
|
+
return template;
|
870
1326
|
},
|
871
1327
|
|
872
1328
|
// Pre-compile the template before caching it. Override
|
@@ -875,159 +1331,112 @@
|
|
875
1331
|
// the template engine used (Handebars, etc).
|
876
1332
|
compileTemplate: function(rawTemplate){
|
877
1333
|
return _.template(rawTemplate);
|
878
|
-
},
|
879
|
-
|
880
|
-
// Clear templates from the cache. If no arguments
|
881
|
-
// are specified, clears all templates:
|
882
|
-
// `clear()`
|
883
|
-
//
|
884
|
-
// If arguments are specified, clears each of the
|
885
|
-
// specified templates from the cache:
|
886
|
-
// `clear("#t1", "#t2", "...")`
|
887
|
-
clear: function(){
|
888
|
-
var i;
|
889
|
-
var length = arguments.length;
|
890
|
-
|
891
|
-
if (length > 0){
|
892
|
-
for(i=0; i<length; i++){
|
893
|
-
delete this.templates[arguments[i]];
|
894
|
-
}
|
895
|
-
} else {
|
896
|
-
this.templates = {};
|
897
|
-
}
|
898
1334
|
}
|
899
|
-
};
|
1335
|
+
});
|
1336
|
+
|
900
1337
|
|
901
1338
|
// Renderer
|
902
1339
|
// --------
|
903
|
-
|
1340
|
+
|
904
1341
|
// Render a template with data by passing in the template
|
905
1342
|
// selector and the data to render.
|
906
1343
|
Marionette.Renderer = {
|
907
1344
|
|
908
1345
|
// Render a template with data. The `template` parameter is
|
909
1346
|
// passed to the `TemplateCache` object to retrieve the
|
910
|
-
//
|
1347
|
+
// template function. Override this method to provide your own
|
911
1348
|
// custom rendering and template handling for all of Marionette.
|
912
1349
|
render: function(template, data){
|
913
|
-
var
|
914
|
-
var
|
915
|
-
|
916
|
-
var templateRetrieval = Marionette.TemplateCache.get(template);
|
917
|
-
|
918
|
-
$.when(templateRetrieval).then(function(template){
|
919
|
-
var html = that.renderTemplate(template, data);
|
920
|
-
asyncRender.resolve(html);
|
921
|
-
});
|
922
|
-
|
923
|
-
return asyncRender.promise();
|
924
|
-
},
|
925
|
-
|
926
|
-
// Default implementation uses underscore.js templates. Override
|
927
|
-
// this method to use your own templating engine.
|
928
|
-
renderTemplate: function(template, data){
|
929
|
-
var html = template(data);
|
1350
|
+
var templateFunc = typeof template === 'function' ? template : Marionette.TemplateCache.get(template);
|
1351
|
+
var html = templateFunc(data);
|
930
1352
|
return html;
|
931
1353
|
}
|
1354
|
+
};
|
1355
|
+
|
1356
|
+
|
1357
|
+
// Callbacks
|
1358
|
+
// ---------
|
932
1359
|
|
1360
|
+
// A simple way of managing a collection of callbacks
|
1361
|
+
// and executing them at a later point in time, using jQuery's
|
1362
|
+
// `Deferred` object.
|
1363
|
+
Marionette.Callbacks = function(){
|
1364
|
+
this._deferred = $.Deferred();
|
1365
|
+
this._callbacks = [];
|
933
1366
|
};
|
934
1367
|
|
935
|
-
|
936
|
-
// -------
|
1368
|
+
_.extend(Marionette.Callbacks.prototype, {
|
937
1369
|
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
1370
|
+
// Add a callback to be executed. Callbacks added here are
|
1371
|
+
// guaranteed to execute, even if they are added after the
|
1372
|
+
// `run` method is called.
|
1373
|
+
add: function(callback, contextOverride){
|
1374
|
+
this._callbacks.push({cb: callback, ctx: contextOverride});
|
943
1375
|
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
var moduleName, module, moduleOverride;
|
950
|
-
var parentModule = this;
|
951
|
-
var parentApp = this;
|
952
|
-
var moduleNames = moduleNames.split(".");
|
1376
|
+
this._deferred.done(function(context, options){
|
1377
|
+
if (contextOverride){ context = contextOverride; }
|
1378
|
+
callback.call(context, options);
|
1379
|
+
});
|
1380
|
+
},
|
953
1381
|
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
1382
|
+
// Run all registered callbacks with the context specified.
|
1383
|
+
// Additional callbacks can be added after this has been run
|
1384
|
+
// and they will still be executed.
|
1385
|
+
run: function(options, context){
|
1386
|
+
this._deferred.resolve(context, options);
|
1387
|
+
},
|
958
1388
|
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
1389
|
+
// Resets the list of callbacks to be run, allowing the same list
|
1390
|
+
// to be run multiple times - whenever the `run` method is called.
|
1391
|
+
reset: function(){
|
1392
|
+
var that = this;
|
1393
|
+
var callbacks = this._callbacks;
|
1394
|
+
this._deferred = $.Deferred();
|
1395
|
+
this._callbacks = [];
|
1396
|
+
_.each(callbacks, function(cb){
|
1397
|
+
that.add(cb.cb, cb.ctx);
|
1398
|
+
});
|
1399
|
+
}
|
1400
|
+
});
|
963
1401
|
|
964
|
-
// Create a new module if we don't have one already
|
965
|
-
if (!module){
|
966
|
-
module = new Marionette.Application();
|
967
|
-
}
|
968
1402
|
|
969
|
-
|
970
|
-
|
971
|
-
// is supplied, and if we're at the last segment
|
972
|
-
// of the "Module.Name" chain.
|
973
|
-
if (isLastModuleInChain && moduleDefinition){
|
974
|
-
moduleOverride = moduleDefinition(module, parentApp, Backbone, Marionette, jQuery, _);
|
975
|
-
// If we have a module override, use it instead.
|
976
|
-
if (moduleOverride){
|
977
|
-
module = moduleOverride;
|
978
|
-
}
|
979
|
-
}
|
1403
|
+
// Event Aggregator
|
1404
|
+
// ----------------
|
980
1405
|
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
parentModule[moduleName] = module;
|
985
|
-
}
|
1406
|
+
// A pub-sub object that can be used to decouple various parts
|
1407
|
+
// of an application through event-driven architecture.
|
1408
|
+
Marionette.EventAggregator = Marionette.EventBinder.extend({
|
986
1409
|
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
1410
|
+
// Extend any provided options directly on to the event binder
|
1411
|
+
constructor: function(options){
|
1412
|
+
Marionette.EventBinder.apply(this, arguments);
|
1413
|
+
_.extend(this, options);
|
1414
|
+
},
|
991
1415
|
|
992
|
-
|
993
|
-
|
1416
|
+
// Override the `bindTo` method to ensure that the event aggregator
|
1417
|
+
// is used as the event binding storage
|
1418
|
+
bindTo: function(eventName, callback, context){
|
1419
|
+
return Marionette.EventBinder.prototype.bindTo.call(this, this, eventName, callback, context);
|
994
1420
|
}
|
995
|
-
};
|
1421
|
+
});
|
1422
|
+
|
1423
|
+
// Copy the basic Backbone.Events on to the event aggregator
|
1424
|
+
_.extend(Marionette.EventAggregator.prototype, Backbone.Events);
|
1425
|
+
|
1426
|
+
// Copy the `extend` function used by Backbone's classes
|
1427
|
+
Marionette.EventAggregator.extend = Backbone.View.extend;
|
1428
|
+
|
996
1429
|
|
997
1430
|
// Helpers
|
998
1431
|
// -------
|
999
1432
|
|
1000
1433
|
// For slicing `arguments` in functions
|
1001
1434
|
var slice = Array.prototype.slice;
|
1002
|
-
|
1003
|
-
// Copy the `extend` function used by Backbone's classes
|
1004
|
-
var extend = Marionette.View.extend;
|
1005
|
-
Marionette.Region.extend = extend;
|
1006
|
-
Marionette.Application.extend = extend;
|
1007
|
-
|
1008
|
-
// Copy the `modules` feature on to the `Application` object
|
1009
|
-
Marionette.Application.prototype.module = Marionette.Modules.module;
|
1010
|
-
|
1011
|
-
// Copy the features of `BindTo` on to these objects
|
1012
|
-
_.extend(Marionette.View.prototype, Marionette.BindTo);
|
1013
|
-
_.extend(Marionette.Application.prototype, Marionette.BindTo);
|
1014
|
-
_.extend(Marionette.Region.prototype, Marionette.BindTo);
|
1015
|
-
|
1016
|
-
// A simple wrapper method for deferring a callback until
|
1017
|
-
// after another method has been called, passing the
|
1018
|
-
// results of the first method to the second. Uses jQuery's
|
1019
|
-
// deferred / promise objects, and $.when/then to make it
|
1020
|
-
// work.
|
1021
|
-
var callDeferredMethod = function(fn, callback, context){
|
1022
|
-
var promise;
|
1023
|
-
if (fn) { promise = fn.call(context); }
|
1024
|
-
$.when(promise).then(callback);
|
1025
|
-
}
|
1026
1435
|
|
1027
1436
|
|
1028
|
-
|
1029
|
-
})(Backbone, _, window.jQuery || window.Zepto || window.ender);
|
1437
|
+
return Marionette;
|
1438
|
+
})(Backbone, _, window.jQuery || window.Zepto || window.ender);
|
1030
1439
|
|
1031
|
-
return Backbone.Marionette;
|
1440
|
+
return Backbone.Marionette;
|
1032
1441
|
|
1033
|
-
}));
|
1442
|
+
}));
|