kraken-js 0.0.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/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/kraken/application.js.coffee +14 -0
- data/app/assets/javascripts/kraken/bootstrap.js.coffee +10 -0
- data/app/assets/javascripts/kraken/config.js.coffee +9 -0
- data/app/assets/javascripts/kraken/overrides.js.coffee +15 -0
- data/app/assets/javascripts/kraken/router.js.coffee +5 -0
- data/app/assets/javascripts/kraken/vendor/backbone.js +1431 -0
- data/app/assets/javascripts/kraken/vendor/backbone.marionette.js +797 -0
- data/app/assets/javascripts/kraken/vendor/underscore.js +1059 -0
- data/app/assets/javascripts/kraken/widget.js.coffee +1 -0
- data/lib/kraken-js.rb +7 -0
- data/lib/kraken-js/engine.rb +5 -0
- data/lib/kraken-js/version.rb +3 -0
- data/lib/tasks/kraken-js_tasks.rake +4 -0
- metadata +105 -0
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
// Backbone.Marionette v0.7.0
|
|
2
|
+
//
|
|
3
|
+
// Copyright (C)2011 Derick Bailey, Muted Solutions, LLC
|
|
4
|
+
// Distributed Under MIT License
|
|
5
|
+
//
|
|
6
|
+
// Documentation and Full License Available at:
|
|
7
|
+
// http://github.com/derickbailey/backbone.marionette
|
|
8
|
+
Backbone.Marionette = (function(Backbone, _, $){
|
|
9
|
+
var Marionette = {};
|
|
10
|
+
|
|
11
|
+
Marionette.version = "0.7.0";
|
|
12
|
+
|
|
13
|
+
// Marionette.View
|
|
14
|
+
// ---------------
|
|
15
|
+
|
|
16
|
+
// The core view type that other Marionette views extend from.
|
|
17
|
+
Marionette.View = Backbone.View.extend({
|
|
18
|
+
// Get the template or template id/selector for this view
|
|
19
|
+
// instance. You can set a `template` attribute in the view
|
|
20
|
+
// definition or pass a `template: "whatever"` parameter in
|
|
21
|
+
// to the constructor options. The `template` can also be
|
|
22
|
+
// a function that returns a selector string.
|
|
23
|
+
getTemplateSelector: function(){
|
|
24
|
+
var template;
|
|
25
|
+
|
|
26
|
+
// Get the template from `this.options.template` or
|
|
27
|
+
// `this.template`. The `options` takes precedence.
|
|
28
|
+
if (this.options && this.options.template){
|
|
29
|
+
template = this.options.template;
|
|
30
|
+
} else {
|
|
31
|
+
template = this.template;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// check if it's a function and execute it, if it is
|
|
35
|
+
if (_.isFunction(template)){
|
|
36
|
+
template = template.call(this);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return template;
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// Default `close` implementation, for removing a view from the
|
|
43
|
+
// DOM and unbinding it. Regions will call this method
|
|
44
|
+
// for you. You can specify an `onClose` method in your view to
|
|
45
|
+
// add custom code that is called after the view is closed.
|
|
46
|
+
close: function(){
|
|
47
|
+
this.beforeClose && this.beforeClose();
|
|
48
|
+
|
|
49
|
+
this.unbindAll();
|
|
50
|
+
this.remove();
|
|
51
|
+
|
|
52
|
+
this.onClose && this.onClose();
|
|
53
|
+
this.trigger('close');
|
|
54
|
+
this.unbind();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Item View
|
|
59
|
+
// ---------
|
|
60
|
+
|
|
61
|
+
// A single item view implementation that contains code for rendering
|
|
62
|
+
// with underscore.js templates, serializing the view's model or collection,
|
|
63
|
+
// and calling several methods on extended views, such as `onRender`.
|
|
64
|
+
Marionette.ItemView = Marionette.View.extend({
|
|
65
|
+
constructor: function(){
|
|
66
|
+
var args = slice.call(arguments);
|
|
67
|
+
Marionette.View.prototype.constructor.apply(this, args);
|
|
68
|
+
|
|
69
|
+
_.bindAll(this, "render");
|
|
70
|
+
|
|
71
|
+
this.initialEvents();
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Configured the initial events that the item view
|
|
75
|
+
// binds to. Override this method to prevent the initial
|
|
76
|
+
// events, or to add your own initial events.
|
|
77
|
+
initialEvents: function(){
|
|
78
|
+
if (this.collection){
|
|
79
|
+
this.bindTo(this.collection, "reset", this.render, this);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Serialize the model or collection for the view. If a model is
|
|
84
|
+
// found, `.toJSON()` is called. If a collection is found, `.toJSON()`
|
|
85
|
+
// is also called, but is used to populate an `items` array in the
|
|
86
|
+
// resulting data. If both are found, defaults to the model.
|
|
87
|
+
// You can override the `serializeData` method in your own view
|
|
88
|
+
// definition, to provide custom serialization for your view's data.
|
|
89
|
+
serializeData: function(){
|
|
90
|
+
var data;
|
|
91
|
+
|
|
92
|
+
if (this.model) { data = this.model.toJSON(); }
|
|
93
|
+
else if (this.collection) {
|
|
94
|
+
data = { items: this.collection.toJSON() };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return data;
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Render the view, defaulting to underscore.js templates.
|
|
101
|
+
// You can override this in your view definition.
|
|
102
|
+
render: function(){
|
|
103
|
+
var that = this;
|
|
104
|
+
|
|
105
|
+
var deferredRender = $.Deferred();
|
|
106
|
+
var template = this.getTemplateSelector();
|
|
107
|
+
var deferredData = this.serializeData();
|
|
108
|
+
|
|
109
|
+
this.beforeRender && this.beforeRender();
|
|
110
|
+
this.trigger("item:before:render", that);
|
|
111
|
+
|
|
112
|
+
$.when(deferredData).then(function(data) {
|
|
113
|
+
var asyncRender = Marionette.Renderer.render(template, data);
|
|
114
|
+
$.when(asyncRender).then(function(html){
|
|
115
|
+
that.$el.html(html);
|
|
116
|
+
that.onRender && that.onRender();
|
|
117
|
+
that.trigger("item:rendered", that);
|
|
118
|
+
that.trigger("render", that);
|
|
119
|
+
deferredRender.resolve();
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return deferredRender.promise();
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// Override the default close event to add a few
|
|
127
|
+
// more events that are triggered.
|
|
128
|
+
close: function(){
|
|
129
|
+
this.trigger('item:before:close');
|
|
130
|
+
Marionette.View.prototype.close.apply(this, arguments);
|
|
131
|
+
this.trigger('item:closed');
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Collection View
|
|
136
|
+
// ---------------
|
|
137
|
+
|
|
138
|
+
// A view that iterates over a Backbone.Collection
|
|
139
|
+
// and renders an individual ItemView for each model.
|
|
140
|
+
Marionette.CollectionView = Marionette.View.extend({
|
|
141
|
+
constructor: function(){
|
|
142
|
+
Marionette.View.prototype.constructor.apply(this, arguments);
|
|
143
|
+
|
|
144
|
+
_.bindAll(this, "addItemView", "render");
|
|
145
|
+
this.initialEvents();
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
// Configured the initial events that the collection view
|
|
149
|
+
// binds to. Override this method to prevent the initial
|
|
150
|
+
// events, or to add your own initial events.
|
|
151
|
+
initialEvents: function(){
|
|
152
|
+
if (this.collection){
|
|
153
|
+
this.bindTo(this.collection, "add", this.addChildView, this);
|
|
154
|
+
this.bindTo(this.collection, "remove", this.removeItemView, this);
|
|
155
|
+
this.bindTo(this.collection, "reset", this.render, this);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Handle a child item added to the collection
|
|
160
|
+
addChildView: function(item){
|
|
161
|
+
var ItemView = this.getItemView();
|
|
162
|
+
return this.addItemView(item, ItemView);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Loop through all of the items and render
|
|
166
|
+
// each of them with the specified `itemView`.
|
|
167
|
+
render: function(){
|
|
168
|
+
var that = this;
|
|
169
|
+
var deferredRender = $.Deferred();
|
|
170
|
+
var promises = [];
|
|
171
|
+
var ItemView = this.getItemView();
|
|
172
|
+
|
|
173
|
+
this.beforeRender && this.beforeRender();
|
|
174
|
+
this.trigger("collection:before:render", this);
|
|
175
|
+
|
|
176
|
+
this.closeChildren();
|
|
177
|
+
this.collection && this.collection.each(function(item){
|
|
178
|
+
var promise = that.addItemView(item, ItemView);
|
|
179
|
+
promises.push(promise);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
deferredRender.done(function(){
|
|
183
|
+
this.onRender && this.onRender();
|
|
184
|
+
this.trigger("collection:rendered", this);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
$.when(promises).then(function(){
|
|
188
|
+
deferredRender.resolveWith(that);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return deferredRender.promise();
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
// Retrieve the itemView type, either from `this.options.itemView`
|
|
195
|
+
// or from the `itemView` in the object definition. The "options"
|
|
196
|
+
// takes precedence.
|
|
197
|
+
getItemView: function(){
|
|
198
|
+
var itemView = this.options.itemView || this.itemView;
|
|
199
|
+
|
|
200
|
+
if (!itemView){
|
|
201
|
+
var err = new Error("An `itemView` must be specified");
|
|
202
|
+
err.name = "NoItemViewError";
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return itemView;
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// Render the child item's view and add it to the
|
|
210
|
+
// HTML for the collection view.
|
|
211
|
+
addItemView: function(item, ItemView){
|
|
212
|
+
var that = this;
|
|
213
|
+
|
|
214
|
+
var view = this.buildItemView(item, ItemView);
|
|
215
|
+
this.storeChild(view);
|
|
216
|
+
this.trigger("item:added", view);
|
|
217
|
+
|
|
218
|
+
var viewRendered = view.render();
|
|
219
|
+
$.when(viewRendered).then(function(){
|
|
220
|
+
that.appendHtml(that, view);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return viewRendered;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// Build an `itemView` for every model in the collection.
|
|
227
|
+
buildItemView: function(item, ItemView){
|
|
228
|
+
var view = new ItemView({
|
|
229
|
+
model: item
|
|
230
|
+
});
|
|
231
|
+
return view;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// Remove the child view and close it
|
|
235
|
+
removeItemView: function(item){
|
|
236
|
+
var view = this.children[item.cid];
|
|
237
|
+
if (view){
|
|
238
|
+
view.close();
|
|
239
|
+
delete this.children[item.cid];
|
|
240
|
+
}
|
|
241
|
+
this.trigger("item:removed", view);
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// Append the HTML to the collection's `el`.
|
|
245
|
+
// Override this method to do something other
|
|
246
|
+
// then `.append`.
|
|
247
|
+
appendHtml: function(collectionView, itemView){
|
|
248
|
+
collectionView.$el.append(itemView.el);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
// Store references to all of the child `itemView`
|
|
252
|
+
// instances so they can be managed and cleaned up, later.
|
|
253
|
+
storeChild: function(view){
|
|
254
|
+
if (!this.children){
|
|
255
|
+
this.children = {};
|
|
256
|
+
}
|
|
257
|
+
this.children[view.model.cid] = view;
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
// Handle cleanup and other closing needs for
|
|
261
|
+
// the collection of views.
|
|
262
|
+
close: function(){
|
|
263
|
+
this.trigger("collection:before:close");
|
|
264
|
+
this.closeChildren();
|
|
265
|
+
Marionette.View.prototype.close.apply(this, arguments);
|
|
266
|
+
this.trigger("collection:closed");
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
closeChildren: function(){
|
|
270
|
+
if (this.children){
|
|
271
|
+
_.each(this.children, function(childView){
|
|
272
|
+
childView.close();
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Composite View
|
|
279
|
+
// --------------
|
|
280
|
+
|
|
281
|
+
// Used for rendering a branch-leaf, hierarchical structure.
|
|
282
|
+
// Extends directly from CollectionView and also renders an
|
|
283
|
+
// an item view as `modelView`, for the top leaf
|
|
284
|
+
Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
285
|
+
constructor: function(options){
|
|
286
|
+
Marionette.CollectionView.apply(this, arguments);
|
|
287
|
+
this.itemView = this.getItemView();
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
// Retrieve the `itemView` to be used when rendering each of
|
|
291
|
+
// the items in the collection. The default is to return
|
|
292
|
+
// `this.itemView` or Marionette.CompositeView if no `itemView`
|
|
293
|
+
// has been defined
|
|
294
|
+
getItemView: function(){
|
|
295
|
+
return this.itemView || this.constructor;
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// Renders the model once, and the collection once. Calling
|
|
299
|
+
// this again will tell the model's view to re-render itself
|
|
300
|
+
// but the collection will not re-render.
|
|
301
|
+
render: function(){
|
|
302
|
+
var that = this;
|
|
303
|
+
var compositeRendered = $.Deferred();
|
|
304
|
+
|
|
305
|
+
var modelIsRendered = this.renderModel();
|
|
306
|
+
$.when(modelIsRendered).then(function(html){
|
|
307
|
+
that.$el.html(html);
|
|
308
|
+
that.trigger("composite:model:rendered");
|
|
309
|
+
that.trigger("render");
|
|
310
|
+
|
|
311
|
+
var collectionIsRendered = that.renderCollection();
|
|
312
|
+
$.when(collectionIsRendered).then(function(){
|
|
313
|
+
compositeRendered.resolve();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
compositeRendered.done(function(){
|
|
318
|
+
that.trigger("composite:rendered");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return compositeRendered.promise();
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
// Render the collection for the composite view
|
|
325
|
+
renderCollection: function(){
|
|
326
|
+
var collectionDeferred = Marionette.CollectionView.prototype.render.apply(this, arguments);
|
|
327
|
+
collectionDeferred.done(function(){
|
|
328
|
+
this.trigger("composite:collection:rendered");
|
|
329
|
+
});
|
|
330
|
+
return collectionDeferred.promise();
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
// Render an individual model, if we have one, as
|
|
334
|
+
// part of a composite view (branch / leaf). For example:
|
|
335
|
+
// a treeview.
|
|
336
|
+
renderModel: function(){
|
|
337
|
+
var data = {};
|
|
338
|
+
if (this.model){
|
|
339
|
+
data = this.model.toJSON();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
var template = this.getTemplateSelector();
|
|
343
|
+
return Marionette.Renderer.render(template, data);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Region
|
|
348
|
+
// ------
|
|
349
|
+
|
|
350
|
+
// Manage the visual regions of your composite application. See
|
|
351
|
+
// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
|
|
352
|
+
Marionette.Region = function(options){
|
|
353
|
+
this.options = options || {};
|
|
354
|
+
|
|
355
|
+
_.extend(this, options);
|
|
356
|
+
|
|
357
|
+
if (!this.el){
|
|
358
|
+
var err = new Error("An 'el' must be specified");
|
|
359
|
+
err.name = "NoElError";
|
|
360
|
+
throw err;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
_.extend(Marionette.Region.prototype, Backbone.Events, {
|
|
365
|
+
|
|
366
|
+
// Displays a backbone view instance inside of the region.
|
|
367
|
+
// Handles calling the `render` method for you. Reads content
|
|
368
|
+
// directly from the `el` attribute. Also calls an optional
|
|
369
|
+
// `onShow` and `close` method on your view, just after showing
|
|
370
|
+
// or just before closing the view, respectively.
|
|
371
|
+
show: function(view, appendMethod){
|
|
372
|
+
this.ensureEl();
|
|
373
|
+
|
|
374
|
+
this.close();
|
|
375
|
+
this.open(view, appendMethod);
|
|
376
|
+
|
|
377
|
+
this.currentView = view;
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
ensureEl: function(){
|
|
381
|
+
if (!this.$el || this.$el.length == 0){
|
|
382
|
+
this.$el = this.getEl(this.el);
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
// Override this method to change how the region finds the
|
|
387
|
+
// DOM element that it manages. Return a jQuery selector object.
|
|
388
|
+
getEl: function(selector){
|
|
389
|
+
return $(selector);
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
// Internal method to render and display a view. Not meant
|
|
393
|
+
// to be called from any external code.
|
|
394
|
+
open: function(view, appendMethod){
|
|
395
|
+
var that = this;
|
|
396
|
+
appendMethod = appendMethod || "html";
|
|
397
|
+
|
|
398
|
+
$.when(view.render()).then(function () {
|
|
399
|
+
that.$el[appendMethod](view.el);
|
|
400
|
+
view.onShow && view.onShow();
|
|
401
|
+
view.trigger("show");
|
|
402
|
+
that.trigger("view:show", view);
|
|
403
|
+
});
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
// Close the current view, if there is one. If there is no
|
|
407
|
+
// current view, it does nothing and returns immediately.
|
|
408
|
+
close: function(){
|
|
409
|
+
var view = this.currentView;
|
|
410
|
+
if (!view){ return; }
|
|
411
|
+
|
|
412
|
+
view.close && view.close();
|
|
413
|
+
this.trigger("view:closed", view);
|
|
414
|
+
|
|
415
|
+
delete this.currentView;
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
// Attach an existing view to the region. This
|
|
419
|
+
// will not call `render` or `onShow` for the new view,
|
|
420
|
+
// and will not replace the current HTML for the `el`
|
|
421
|
+
// of the region.
|
|
422
|
+
attachView: function(view){
|
|
423
|
+
this.currentView = view;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Layout
|
|
428
|
+
// ------
|
|
429
|
+
|
|
430
|
+
// Formerly known as Composite Region.
|
|
431
|
+
//
|
|
432
|
+
// Used for managing application layouts, nested layouts and
|
|
433
|
+
// multiple regions within an application or sub-application.
|
|
434
|
+
//
|
|
435
|
+
// A specialized view type that renders an area of HTML and then
|
|
436
|
+
// attaches `Region` instances to the specified `regions`.
|
|
437
|
+
// Used for composite view management and sub-application areas.
|
|
438
|
+
Marionette.Layout = Marionette.ItemView.extend({
|
|
439
|
+
constructor: function () {
|
|
440
|
+
this.vent = new Backbone.Marionette.EventAggregator();
|
|
441
|
+
Backbone.Marionette.ItemView.apply(this, arguments);
|
|
442
|
+
this.regionManagers = {};
|
|
443
|
+
},
|
|
444
|
+
|
|
445
|
+
render: function () {
|
|
446
|
+
this.initializeRegions();
|
|
447
|
+
return Backbone.Marionette.ItemView.prototype.render.call(this, arguments);
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
close: function () {
|
|
451
|
+
this.closeRegions();
|
|
452
|
+
Backbone.Marionette.ItemView.prototype.close.call(this, arguments);
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
initializeRegions: function () {
|
|
456
|
+
var that = this;
|
|
457
|
+
_.each(this.regions, function (selector, name) {
|
|
458
|
+
var regionManager = new Backbone.Marionette.Region({
|
|
459
|
+
el: selector,
|
|
460
|
+
|
|
461
|
+
getEl: function(selector){
|
|
462
|
+
return that.$(selector);
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
that.regionManagers[name] = regionManager;
|
|
466
|
+
that[name] = regionManager;
|
|
467
|
+
});
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
closeRegions: function () {
|
|
471
|
+
var that = this;
|
|
472
|
+
_.each(this.regionManagers, function (manager, name) {
|
|
473
|
+
manager.close();
|
|
474
|
+
delete that[name];
|
|
475
|
+
});
|
|
476
|
+
this.regionManagers = {};
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// AppRouter
|
|
481
|
+
// ---------
|
|
482
|
+
|
|
483
|
+
// Reduce the boilerplate code of handling route events
|
|
484
|
+
// and then calling a single method on another object.
|
|
485
|
+
// Have your routers configured to call the method on
|
|
486
|
+
// your object, directly.
|
|
487
|
+
//
|
|
488
|
+
// Configure an AppRouter with `appRoutes`.
|
|
489
|
+
//
|
|
490
|
+
// App routers can only take one `controller` object.
|
|
491
|
+
// It is reocmmended that you divide your controller
|
|
492
|
+
// objects in to smaller peices of related functionality
|
|
493
|
+
// and have multiple routers / controllers, instead of
|
|
494
|
+
// just one giant router and controller.
|
|
495
|
+
//
|
|
496
|
+
// You can also add standard routes to an AppRouter.
|
|
497
|
+
|
|
498
|
+
Marionette.AppRouter = Backbone.Router.extend({
|
|
499
|
+
|
|
500
|
+
constructor: function(options){
|
|
501
|
+
Backbone.Router.prototype.constructor.call(this, options);
|
|
502
|
+
|
|
503
|
+
if (this.appRoutes){
|
|
504
|
+
var controller = this.controller;
|
|
505
|
+
if (options && options.controller) {
|
|
506
|
+
controller = options.controller;
|
|
507
|
+
}
|
|
508
|
+
this.processAppRoutes(controller, this.appRoutes);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
processAppRoutes: function(controller, appRoutes){
|
|
513
|
+
var method, methodName;
|
|
514
|
+
var route, routesLength;
|
|
515
|
+
var routes = [];
|
|
516
|
+
var router = this;
|
|
517
|
+
|
|
518
|
+
for(route in appRoutes){
|
|
519
|
+
routes.unshift([route, appRoutes[route]]);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
routesLength = routes.length;
|
|
523
|
+
for (var i = 0; i < routesLength; i++){
|
|
524
|
+
route = routes[i][0];
|
|
525
|
+
methodName = routes[i][1];
|
|
526
|
+
method = _.bind(controller[methodName], controller);
|
|
527
|
+
router.route(route, methodName, method);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Composite Application
|
|
533
|
+
// ---------------------
|
|
534
|
+
|
|
535
|
+
// Contain and manage the composite application as a whole.
|
|
536
|
+
// Stores and starts up `Region` objects, includes an
|
|
537
|
+
// event aggregator as `app.vent`
|
|
538
|
+
Marionette.Application = function(options){
|
|
539
|
+
this.initCallbacks = new Marionette.Callbacks();
|
|
540
|
+
this.vent = new Marionette.EventAggregator();
|
|
541
|
+
_.extend(this, options);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
_.extend(Marionette.Application.prototype, Backbone.Events, {
|
|
545
|
+
// Add an initializer that is either run at when the `start`
|
|
546
|
+
// method is called, or run immediately if added after `start`
|
|
547
|
+
// has already been called.
|
|
548
|
+
addInitializer: function(initializer){
|
|
549
|
+
this.initCallbacks.add(initializer);
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
// kick off all of the application's processes.
|
|
553
|
+
// initializes all of the regions that have been added
|
|
554
|
+
// to the app, and runs all of the initializer functions
|
|
555
|
+
start: function(options){
|
|
556
|
+
this.trigger("initialize:before", options);
|
|
557
|
+
this.initCallbacks.run(this, options);
|
|
558
|
+
this.trigger("initialize:after", options);
|
|
559
|
+
|
|
560
|
+
this.trigger("start", options);
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
// Add regions to your app.
|
|
564
|
+
// Accepts a hash of named strings or Region objects
|
|
565
|
+
// addRegions({something: "#someRegion"})
|
|
566
|
+
// addRegions{{something: Region.extend({el: "#someRegion"}) });
|
|
567
|
+
addRegions: function(regions){
|
|
568
|
+
var regionValue, regionObj;
|
|
569
|
+
|
|
570
|
+
for(var region in regions){
|
|
571
|
+
if (regions.hasOwnProperty(region)){
|
|
572
|
+
regionValue = regions[region];
|
|
573
|
+
|
|
574
|
+
if (typeof regionValue === "string"){
|
|
575
|
+
regionObj = new Marionette.Region({
|
|
576
|
+
el: regionValue
|
|
577
|
+
});
|
|
578
|
+
} else {
|
|
579
|
+
regionObj = new regionValue;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
this[region] = regionObj;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// BindTo: Event Binding
|
|
589
|
+
// ---------------------
|
|
590
|
+
|
|
591
|
+
// BindTo facilitates the binding and unbinding of events
|
|
592
|
+
// from objects that extend `Backbone.Events`. It makes
|
|
593
|
+
// unbinding events, even with anonymous callback functions,
|
|
594
|
+
// easy.
|
|
595
|
+
//
|
|
596
|
+
// Thanks to Johnny Oshika for this code.
|
|
597
|
+
// http://stackoverflow.com/questions/7567404/backbone-js-repopulate-or-recreate-the-view/7607853#7607853
|
|
598
|
+
Marionette.BindTo = {
|
|
599
|
+
// Store the event binding in array so it can be unbound
|
|
600
|
+
// easily, at a later point in time.
|
|
601
|
+
bindTo: function (obj, eventName, callback, context) {
|
|
602
|
+
context = context || this;
|
|
603
|
+
obj.on(eventName, callback, context);
|
|
604
|
+
|
|
605
|
+
if (!this.bindings) this.bindings = [];
|
|
606
|
+
|
|
607
|
+
this.bindings.push({
|
|
608
|
+
obj: obj,
|
|
609
|
+
eventName: eventName,
|
|
610
|
+
callback: callback,
|
|
611
|
+
context: context
|
|
612
|
+
});
|
|
613
|
+
},
|
|
614
|
+
|
|
615
|
+
// Unbind all of the events that we have stored.
|
|
616
|
+
unbindAll: function () {
|
|
617
|
+
_.each(this.bindings, function (binding) {
|
|
618
|
+
binding.obj.off(binding.eventName, binding.callback);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
this.bindings = [];
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// Callbacks
|
|
626
|
+
// ---------
|
|
627
|
+
|
|
628
|
+
// A simple way of managing a collection of callbacks
|
|
629
|
+
// and executing them at a later point in time, using jQuery's
|
|
630
|
+
// `Deferred` object.
|
|
631
|
+
Marionette.Callbacks = function(){
|
|
632
|
+
this.deferred = $.Deferred();
|
|
633
|
+
this.promise = this.deferred.promise();
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
_.extend(Marionette.Callbacks.prototype, {
|
|
637
|
+
|
|
638
|
+
// Add a callback to be executed. Callbacks added here are
|
|
639
|
+
// guaranteed to execute, even if they are added after the
|
|
640
|
+
// `run` method is called.
|
|
641
|
+
add: function(callback){
|
|
642
|
+
this.promise.done(function(context, options){
|
|
643
|
+
callback.call(context, options);
|
|
644
|
+
});
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
// Run all registered callbacks with the context specified.
|
|
648
|
+
// Additional callbacks can be added after this has been run
|
|
649
|
+
// and they will still be executed.
|
|
650
|
+
run: function(context, options){
|
|
651
|
+
this.deferred.resolve(context, options);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// Event Aggregator
|
|
656
|
+
// ----------------
|
|
657
|
+
|
|
658
|
+
// A pub-sub object that can be used to decouple various parts
|
|
659
|
+
// of an application through event-driven architecture.
|
|
660
|
+
Marionette.EventAggregator = function(options){
|
|
661
|
+
_.extend(this, options);
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
_.extend(Marionette.EventAggregator.prototype, Backbone.Events, Marionette.BindTo, {
|
|
665
|
+
// Assumes the event aggregator itself is the
|
|
666
|
+
// object being bound to.
|
|
667
|
+
bindTo: function(eventName, callback, context){
|
|
668
|
+
Marionette.BindTo.bindTo.call(this, this, eventName, callback, context);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// Template Cache
|
|
673
|
+
// --------------
|
|
674
|
+
|
|
675
|
+
// Manage templates stored in `<script>` blocks,
|
|
676
|
+
// caching them for faster access.
|
|
677
|
+
Marionette.TemplateCache = {
|
|
678
|
+
templates: {},
|
|
679
|
+
loaders: {},
|
|
680
|
+
|
|
681
|
+
// Get the specified template by id. Either
|
|
682
|
+
// retrieves the cached version, or loads it
|
|
683
|
+
// from the DOM.
|
|
684
|
+
get: function(templateId){
|
|
685
|
+
var that = this;
|
|
686
|
+
var templateRetrieval = $.Deferred();
|
|
687
|
+
var cachedTemplate = this.templates[templateId];
|
|
688
|
+
|
|
689
|
+
if (cachedTemplate){
|
|
690
|
+
templateRetrieval.resolve(cachedTemplate);
|
|
691
|
+
} else {
|
|
692
|
+
var loader = this.loaders[templateId];
|
|
693
|
+
if(loader) {
|
|
694
|
+
templateRetrieval = loader;
|
|
695
|
+
} else {
|
|
696
|
+
this.loaders[templateId] = templateRetrieval;
|
|
697
|
+
|
|
698
|
+
this.loadTemplate(templateId, function(template){
|
|
699
|
+
delete that.loaders[templateId];
|
|
700
|
+
that.templates[templateId] = template;
|
|
701
|
+
templateRetrieval.resolve(template);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return templateRetrieval.promise();
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
// Load a template from the DOM, by default. Override
|
|
711
|
+
// this method to provide your own template retrieval,
|
|
712
|
+
// such as asynchronous loading from a server.
|
|
713
|
+
loadTemplate: function(templateId, callback){
|
|
714
|
+
var template = $(templateId).html();
|
|
715
|
+
callback.call(this, template);
|
|
716
|
+
},
|
|
717
|
+
|
|
718
|
+
// Clear templates from the cache. If no arguments
|
|
719
|
+
// are specified, clears all templates:
|
|
720
|
+
// `clear()`
|
|
721
|
+
//
|
|
722
|
+
// If arguments are specified, clears each of the
|
|
723
|
+
// specified templates from the cache:
|
|
724
|
+
// `clear("#t1", "#t2", "...")`
|
|
725
|
+
clear: function(){
|
|
726
|
+
var length = arguments.length;
|
|
727
|
+
if (length > 0){
|
|
728
|
+
for(var i=0; i<length; i++){
|
|
729
|
+
delete this.templates[arguments[i]];
|
|
730
|
+
}
|
|
731
|
+
} else {
|
|
732
|
+
this.templates = {};
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// Renderer
|
|
738
|
+
// --------
|
|
739
|
+
|
|
740
|
+
// Render a template with data by passing in the template
|
|
741
|
+
// selector and the data to render.
|
|
742
|
+
Marionette.Renderer = {
|
|
743
|
+
|
|
744
|
+
// Render a template with data. The `template` parameter is
|
|
745
|
+
// passed to the `TemplateCache` object to retrieve the
|
|
746
|
+
// actual template. Override this method to provide your own
|
|
747
|
+
// custom rendering and template handling for all of Marionette.
|
|
748
|
+
render: function(template, data){
|
|
749
|
+
var that = this;
|
|
750
|
+
var asyncRender = $.Deferred();
|
|
751
|
+
|
|
752
|
+
var templateRetrieval = Marionette.TemplateCache.get(template);
|
|
753
|
+
|
|
754
|
+
$.when(templateRetrieval).then(function(template){
|
|
755
|
+
var html = that.renderTemplate(template, data);
|
|
756
|
+
asyncRender.resolve(html);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
return asyncRender.promise();
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
// Default implementation uses underscore.js templates. Override
|
|
763
|
+
// this method to use your own templating engine.
|
|
764
|
+
renderTemplate: function(template, data){
|
|
765
|
+
if (!template || template.length === 0){
|
|
766
|
+
var msg = "A template must be specified";
|
|
767
|
+
var err = new Error(msg);
|
|
768
|
+
err.name = "NoTemplateError";
|
|
769
|
+
throw err;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
var html = _.template(template, data);
|
|
773
|
+
return html;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Helpers
|
|
779
|
+
// -------
|
|
780
|
+
|
|
781
|
+
// For slicing `arguments` in functions
|
|
782
|
+
var slice = Array.prototype.slice;
|
|
783
|
+
|
|
784
|
+
// Copy the `extend` function used by Backbone's classes
|
|
785
|
+
var extend = Marionette.View.extend;
|
|
786
|
+
Marionette.Region.extend = extend;
|
|
787
|
+
Marionette.Application.extend = extend;
|
|
788
|
+
|
|
789
|
+
// Copy the features of `BindTo` on to these objects
|
|
790
|
+
_.extend(Marionette.View.prototype, Marionette.BindTo);
|
|
791
|
+
_.extend(Marionette.Application.prototype, Marionette.BindTo);
|
|
792
|
+
_.extend(Marionette.Region.prototype, Marionette.BindTo);
|
|
793
|
+
|
|
794
|
+
return Marionette;
|
|
795
|
+
})(Backbone, _, window.jQuery || window.Zepto || window.ender);
|
|
796
|
+
|
|
797
|
+
;
|