marionette-rails 1.0.0.beta3 → 1.0.0.beta4
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/vendor/assets/javascripts/backbone.marionette.js +544 -450
- metadata +1 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* Backbone.Marionette, v1.0.0-
|
2
|
+
* Backbone.Marionette, v1.0.0-beta4
|
3
3
|
* Copyright (c)2012 Derick Bailey, Muted Solutions, LLC.
|
4
4
|
* Distributed under MIT license
|
5
5
|
* http://github.com/marionettejs/backbone.marionette
|
@@ -244,9 +244,79 @@ Backbone.Marionette = Marionette = (function(Backbone, _, $){
|
|
244
244
|
// For slicing `arguments` in functions
|
245
245
|
var slice = Array.prototype.slice;
|
246
246
|
|
247
|
+
// Marionette.extend
|
248
|
+
// -----------------
|
249
|
+
|
247
250
|
// Borrow the Backbone `extend` method so we can use it as needed
|
248
251
|
Marionette.extend = Backbone.Model.extend;
|
249
252
|
|
253
|
+
// Marionette.getOption
|
254
|
+
// --------------------
|
255
|
+
|
256
|
+
// Retrieve an object, function or other value from a target
|
257
|
+
// object or it's `options`, with `options` taking precedence.
|
258
|
+
Marionette.getOption = function(target, optionName){
|
259
|
+
if (!target || !optionName){ return; }
|
260
|
+
var value;
|
261
|
+
|
262
|
+
if (target.options && target.options[optionName]){
|
263
|
+
value = target.options[optionName];
|
264
|
+
} else {
|
265
|
+
value = target[optionName];
|
266
|
+
}
|
267
|
+
|
268
|
+
return value;
|
269
|
+
};
|
270
|
+
|
271
|
+
// Mairionette.createObject
|
272
|
+
// ------------------------
|
273
|
+
|
274
|
+
// A wrapper / shim for `Object.create`. Uses native `Object.create`
|
275
|
+
// if available, otherwise shims it in place for Marionette to use.
|
276
|
+
Marionette.createObject = (function(){
|
277
|
+
var createObject;
|
278
|
+
|
279
|
+
// Define this once, and just replace the .prototype on it as needed,
|
280
|
+
// to improve performance in older / less optimized JS engines
|
281
|
+
function F() {}
|
282
|
+
|
283
|
+
|
284
|
+
// Check for existing native / shimmed Object.create
|
285
|
+
if (typeof Object.create === "function"){
|
286
|
+
|
287
|
+
// found native/shim, so use it
|
288
|
+
createObject = Object.create;
|
289
|
+
|
290
|
+
} else {
|
291
|
+
|
292
|
+
// An implementation of the Boodman/Crockford delegation
|
293
|
+
// w/ Cornford optimization, as suggested by @unscriptable
|
294
|
+
// https://gist.github.com/3959151
|
295
|
+
|
296
|
+
// native/shim not found, so shim it ourself
|
297
|
+
createObject = function (o) {
|
298
|
+
|
299
|
+
// set the prototype of the function
|
300
|
+
// so we will get `o` as the prototype
|
301
|
+
// of the new object instance
|
302
|
+
F.prototype = o;
|
303
|
+
|
304
|
+
// create a new object that inherits from
|
305
|
+
// the `o` parameter
|
306
|
+
var child = new F();
|
307
|
+
|
308
|
+
// clean up just in case o is really large
|
309
|
+
F.prototype = null;
|
310
|
+
|
311
|
+
// send it back
|
312
|
+
return child;
|
313
|
+
};
|
314
|
+
|
315
|
+
}
|
316
|
+
|
317
|
+
return createObject;
|
318
|
+
})();
|
319
|
+
|
250
320
|
// Trigger an event and a corresponding method name. Examples:
|
251
321
|
//
|
252
322
|
// `this.triggerMethod("foo")` will trigger the "foo" event and
|
@@ -274,11 +344,22 @@ Marionette.triggerMethod = function(){
|
|
274
344
|
}
|
275
345
|
};
|
276
346
|
|
347
|
+
|
277
348
|
// EventBinder
|
278
349
|
// -----------
|
279
350
|
// Import the event binder from it's new home
|
280
351
|
// https://github.com/marionettejs/backbone.eventbinder
|
281
|
-
Marionette.EventBinder = Backbone.EventBinder
|
352
|
+
Marionette.EventBinder = Backbone.EventBinder.extend({
|
353
|
+
|
354
|
+
augment: function(target){
|
355
|
+
var eventBinder = new Marionette.EventBinder();
|
356
|
+
target.eventBinder = eventBinder;
|
357
|
+
target.bindTo = _.bind(eventBinder.bindTo, eventBinder);
|
358
|
+
target.unbindFrom = _.bind(eventBinder.unbindFrom, eventBinder);
|
359
|
+
target.unbindAll = _.bind(eventBinder.unbindAll, eventBinder);
|
360
|
+
}
|
361
|
+
|
362
|
+
});
|
282
363
|
|
283
364
|
// Add the EventBinder methods to the view directly,
|
284
365
|
// but keep them bound to the EventBinder instance so they work properly.
|
@@ -310,6 +391,361 @@ Marionette.EventAggregator = Backbone.Wreqr.EventAggregator.extend({
|
|
310
391
|
|
311
392
|
});
|
312
393
|
|
394
|
+
// Callbacks
|
395
|
+
// ---------
|
396
|
+
|
397
|
+
// A simple way of managing a collection of callbacks
|
398
|
+
// and executing them at a later point in time, using jQuery's
|
399
|
+
// `Deferred` object.
|
400
|
+
Marionette.Callbacks = function(){
|
401
|
+
this._deferred = $.Deferred();
|
402
|
+
this._callbacks = [];
|
403
|
+
};
|
404
|
+
|
405
|
+
_.extend(Marionette.Callbacks.prototype, {
|
406
|
+
|
407
|
+
// Add a callback to be executed. Callbacks added here are
|
408
|
+
// guaranteed to execute, even if they are added after the
|
409
|
+
// `run` method is called.
|
410
|
+
add: function(callback, contextOverride){
|
411
|
+
this._callbacks.push({cb: callback, ctx: contextOverride});
|
412
|
+
|
413
|
+
this._deferred.done(function(context, options){
|
414
|
+
if (contextOverride){ context = contextOverride; }
|
415
|
+
callback.call(context, options);
|
416
|
+
});
|
417
|
+
},
|
418
|
+
|
419
|
+
// Run all registered callbacks with the context specified.
|
420
|
+
// Additional callbacks can be added after this has been run
|
421
|
+
// and they will still be executed.
|
422
|
+
run: function(options, context){
|
423
|
+
this._deferred.resolve(context, options);
|
424
|
+
},
|
425
|
+
|
426
|
+
// Resets the list of callbacks to be run, allowing the same list
|
427
|
+
// to be run multiple times - whenever the `run` method is called.
|
428
|
+
reset: function(){
|
429
|
+
var that = this;
|
430
|
+
var callbacks = this._callbacks;
|
431
|
+
this._deferred = $.Deferred();
|
432
|
+
this._callbacks = [];
|
433
|
+
_.each(callbacks, function(cb){
|
434
|
+
that.add(cb.cb, cb.ctx);
|
435
|
+
});
|
436
|
+
}
|
437
|
+
});
|
438
|
+
|
439
|
+
|
440
|
+
// Template Cache
|
441
|
+
// --------------
|
442
|
+
|
443
|
+
// Manage templates stored in `<script>` blocks,
|
444
|
+
// caching them for faster access.
|
445
|
+
Marionette.TemplateCache = function(templateId){
|
446
|
+
this.templateId = templateId;
|
447
|
+
};
|
448
|
+
|
449
|
+
// TemplateCache object-level methods. Manage the template
|
450
|
+
// caches from these method calls instead of creating
|
451
|
+
// your own TemplateCache instances
|
452
|
+
_.extend(Marionette.TemplateCache, {
|
453
|
+
templateCaches: {},
|
454
|
+
|
455
|
+
// Get the specified template by id. Either
|
456
|
+
// retrieves the cached version, or loads it
|
457
|
+
// from the DOM.
|
458
|
+
get: function(templateId){
|
459
|
+
var that = this;
|
460
|
+
var cachedTemplate = this.templateCaches[templateId];
|
461
|
+
|
462
|
+
if (!cachedTemplate){
|
463
|
+
cachedTemplate = new Marionette.TemplateCache(templateId);
|
464
|
+
this.templateCaches[templateId] = cachedTemplate;
|
465
|
+
}
|
466
|
+
|
467
|
+
return cachedTemplate.load();
|
468
|
+
},
|
469
|
+
|
470
|
+
// Clear templates from the cache. If no arguments
|
471
|
+
// are specified, clears all templates:
|
472
|
+
// `clear()`
|
473
|
+
//
|
474
|
+
// If arguments are specified, clears each of the
|
475
|
+
// specified templates from the cache:
|
476
|
+
// `clear("#t1", "#t2", "...")`
|
477
|
+
clear: function(){
|
478
|
+
var i;
|
479
|
+
var length = arguments.length;
|
480
|
+
|
481
|
+
if (length > 0){
|
482
|
+
for(i=0; i<length; i++){
|
483
|
+
delete this.templateCaches[arguments[i]];
|
484
|
+
}
|
485
|
+
} else {
|
486
|
+
this.templateCaches = {};
|
487
|
+
}
|
488
|
+
}
|
489
|
+
});
|
490
|
+
|
491
|
+
// TemplateCache instance methods, allowing each
|
492
|
+
// template cache object to manage it's own state
|
493
|
+
// and know whether or not it has been loaded
|
494
|
+
_.extend(Marionette.TemplateCache.prototype, {
|
495
|
+
|
496
|
+
// Internal method to load the template asynchronously.
|
497
|
+
load: function(){
|
498
|
+
var that = this;
|
499
|
+
|
500
|
+
// Guard clause to prevent loading this template more than once
|
501
|
+
if (this.compiledTemplate){
|
502
|
+
return this.compiledTemplate;
|
503
|
+
}
|
504
|
+
|
505
|
+
// Load the template and compile it
|
506
|
+
var template = this.loadTemplate(this.templateId);
|
507
|
+
this.compiledTemplate = this.compileTemplate(template);
|
508
|
+
|
509
|
+
return this.compiledTemplate;
|
510
|
+
},
|
511
|
+
|
512
|
+
// Load a template from the DOM, by default. Override
|
513
|
+
// this method to provide your own template retrieval,
|
514
|
+
// such as asynchronous loading from a server.
|
515
|
+
loadTemplate: function(templateId){
|
516
|
+
var template = $(templateId).html();
|
517
|
+
|
518
|
+
if (!template || template.length === 0){
|
519
|
+
var msg = "Could not find template: '" + templateId + "'";
|
520
|
+
var err = new Error(msg);
|
521
|
+
err.name = "NoTemplateError";
|
522
|
+
throw err;
|
523
|
+
}
|
524
|
+
|
525
|
+
return template;
|
526
|
+
},
|
527
|
+
|
528
|
+
// Pre-compile the template before caching it. Override
|
529
|
+
// this method if you do not need to pre-compile a template
|
530
|
+
// (JST / RequireJS for example) or if you want to change
|
531
|
+
// the template engine used (Handebars, etc).
|
532
|
+
compileTemplate: function(rawTemplate){
|
533
|
+
return _.template(rawTemplate);
|
534
|
+
}
|
535
|
+
});
|
536
|
+
|
537
|
+
|
538
|
+
// Renderer
|
539
|
+
// --------
|
540
|
+
|
541
|
+
// Render a template with data by passing in the template
|
542
|
+
// selector and the data to render.
|
543
|
+
Marionette.Renderer = {
|
544
|
+
|
545
|
+
// Render a template with data. The `template` parameter is
|
546
|
+
// passed to the `TemplateCache` object to retrieve the
|
547
|
+
// template function. Override this method to provide your own
|
548
|
+
// custom rendering and template handling for all of Marionette.
|
549
|
+
render: function(template, data){
|
550
|
+
var templateFunc = typeof template === 'function' ? template : Marionette.TemplateCache.get(template);
|
551
|
+
var html = templateFunc(data);
|
552
|
+
return html;
|
553
|
+
}
|
554
|
+
};
|
555
|
+
|
556
|
+
|
557
|
+
|
558
|
+
// Marionette Controller
|
559
|
+
// ---------------------
|
560
|
+
//
|
561
|
+
// A multi-purpose object to use as a controller for
|
562
|
+
// modules and routers, and as a mediator for workflow
|
563
|
+
// and coordination of other objects, views, and more.
|
564
|
+
Marionette.Controller = function(options){
|
565
|
+
this.options = options || {};
|
566
|
+
|
567
|
+
Marionette.addEventBinder(this);
|
568
|
+
|
569
|
+
if (_.isFunction(this.initialize)){
|
570
|
+
this.initialize(options);
|
571
|
+
}
|
572
|
+
};
|
573
|
+
|
574
|
+
Marionette.Controller.extend = Marionette.extend;
|
575
|
+
|
576
|
+
// Controller Methods
|
577
|
+
// --------------
|
578
|
+
|
579
|
+
// Ensure it can trigger events with Backbone.Events
|
580
|
+
_.extend(Marionette.Controller.prototype, Backbone.Events);
|
581
|
+
|
582
|
+
// Region
|
583
|
+
// ------
|
584
|
+
//
|
585
|
+
// Manage the visual regions of your composite application. See
|
586
|
+
// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
|
587
|
+
|
588
|
+
Marionette.Region = function(options){
|
589
|
+
this.options = options || {};
|
590
|
+
|
591
|
+
Marionette.addEventBinder(this);
|
592
|
+
|
593
|
+
this.el = Marionette.getOption(this, "el");
|
594
|
+
|
595
|
+
if (!this.el){
|
596
|
+
var err = new Error("An 'el' must be specified for a region.");
|
597
|
+
err.name = "NoElError";
|
598
|
+
throw err;
|
599
|
+
}
|
600
|
+
|
601
|
+
if (this.initialize){
|
602
|
+
this.initialize.apply(this, arguments);
|
603
|
+
}
|
604
|
+
};
|
605
|
+
|
606
|
+
|
607
|
+
// Region Type methods
|
608
|
+
// -------------------
|
609
|
+
|
610
|
+
_.extend(Marionette.Region, {
|
611
|
+
|
612
|
+
// Build an instance of a region by passing in a configuration object
|
613
|
+
// and a default region type to use if none is specified in the config.
|
614
|
+
//
|
615
|
+
// The config object should either be a string as a jQuery DOM selector,
|
616
|
+
// a Region type directly, or an object literal that specifies both
|
617
|
+
// a selector and regionType:
|
618
|
+
//
|
619
|
+
// ```js
|
620
|
+
// {
|
621
|
+
// selector: "#foo",
|
622
|
+
// regionType: MyCustomRegion
|
623
|
+
// }
|
624
|
+
// ```
|
625
|
+
//
|
626
|
+
buildRegion: function(regionConfig, defaultRegionType){
|
627
|
+
var regionIsString = (typeof regionConfig === "string");
|
628
|
+
var regionSelectorIsString = (typeof regionConfig.selector === "string");
|
629
|
+
var regionTypeIsUndefined = (typeof regionConfig.regionType === "undefined");
|
630
|
+
var regionIsType = (typeof regionConfig === "function");
|
631
|
+
|
632
|
+
if (!regionIsType && !regionIsString && !regionSelectorIsString) {
|
633
|
+
throw new Error("Region must be specified as a Region type, a selector string or an object with selector property");
|
634
|
+
}
|
635
|
+
|
636
|
+
var selector, RegionType;
|
637
|
+
|
638
|
+
// get the selector for the region
|
639
|
+
|
640
|
+
if (regionIsString) {
|
641
|
+
selector = regionConfig;
|
642
|
+
}
|
643
|
+
|
644
|
+
if (regionConfig.selector) {
|
645
|
+
selector = regionConfig.selector;
|
646
|
+
}
|
647
|
+
|
648
|
+
// get the type for the region
|
649
|
+
|
650
|
+
if (regionIsType){
|
651
|
+
RegionType = regionConfig;
|
652
|
+
}
|
653
|
+
|
654
|
+
if (!regionIsType && regionTypeIsUndefined) {
|
655
|
+
RegionType = defaultRegionType;
|
656
|
+
}
|
657
|
+
|
658
|
+
if (regionConfig.regionType) {
|
659
|
+
RegionType = regionConfig.regionType;
|
660
|
+
}
|
661
|
+
|
662
|
+
// build the region instance
|
663
|
+
|
664
|
+
var regionManager = new RegionType({
|
665
|
+
el: selector
|
666
|
+
});
|
667
|
+
|
668
|
+
return regionManager;
|
669
|
+
}
|
670
|
+
|
671
|
+
});
|
672
|
+
|
673
|
+
// Region Instance Methods
|
674
|
+
// -----------------------
|
675
|
+
|
676
|
+
_.extend(Marionette.Region.prototype, Backbone.Events, {
|
677
|
+
|
678
|
+
// Displays a backbone view instance inside of the region.
|
679
|
+
// Handles calling the `render` method for you. Reads content
|
680
|
+
// directly from the `el` attribute. Also calls an optional
|
681
|
+
// `onShow` and `close` method on your view, just after showing
|
682
|
+
// or just before closing the view, respectively.
|
683
|
+
show: function(view){
|
684
|
+
|
685
|
+
this.ensureEl();
|
686
|
+
this.close();
|
687
|
+
|
688
|
+
view.render();
|
689
|
+
this.open(view);
|
690
|
+
|
691
|
+
Marionette.triggerMethod.call(view, "show");
|
692
|
+
Marionette.triggerMethod.call(this, "show", view);
|
693
|
+
|
694
|
+
this.currentView = view;
|
695
|
+
},
|
696
|
+
|
697
|
+
ensureEl: function(){
|
698
|
+
if (!this.$el || this.$el.length === 0){
|
699
|
+
this.$el = this.getEl(this.el);
|
700
|
+
}
|
701
|
+
},
|
702
|
+
|
703
|
+
// Override this method to change how the region finds the
|
704
|
+
// DOM element that it manages. Return a jQuery selector object.
|
705
|
+
getEl: function(selector){
|
706
|
+
return $(selector);
|
707
|
+
},
|
708
|
+
|
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.empty().append(view.el);
|
713
|
+
},
|
714
|
+
|
715
|
+
// Close the current view, if there is one. If there is no
|
716
|
+
// current view, it does nothing and returns immediately.
|
717
|
+
close: function(){
|
718
|
+
var view = this.currentView;
|
719
|
+
if (!view || view.isClosed){ return; }
|
720
|
+
|
721
|
+
if (view.close) { view.close(); }
|
722
|
+
Marionette.triggerMethod.call(this, "close");
|
723
|
+
|
724
|
+
delete this.currentView;
|
725
|
+
},
|
726
|
+
|
727
|
+
// Attach an existing view to the region. This
|
728
|
+
// will not call `render` or `onShow` for the new view,
|
729
|
+
// and will not replace the current HTML for the `el`
|
730
|
+
// of the region.
|
731
|
+
attachView: function(view){
|
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;
|
742
|
+
}
|
743
|
+
});
|
744
|
+
|
745
|
+
// Copy the `extend` function used by Backbone's classes
|
746
|
+
Marionette.Region.extend = Marionette.extend;
|
747
|
+
|
748
|
+
|
313
749
|
// Marionette.View
|
314
750
|
// ---------------
|
315
751
|
|
@@ -337,17 +773,7 @@ Marionette.View = Backbone.View.extend({
|
|
337
773
|
// definition or pass a `template: "whatever"` parameter in
|
338
774
|
// to the constructor options.
|
339
775
|
getTemplate: function(){
|
340
|
-
|
341
|
-
|
342
|
-
// Get the template from `this.options.template` or
|
343
|
-
// `this.template`. The `options` takes precedence.
|
344
|
-
if (this.options && this.options.template){
|
345
|
-
template = this.options.template;
|
346
|
-
} else {
|
347
|
-
template = this.template;
|
348
|
-
}
|
349
|
-
|
350
|
-
return template;
|
776
|
+
return Marionette.getOption(this, "template");
|
351
777
|
},
|
352
778
|
|
353
779
|
// Mix in template helper methods. Looks for a
|
@@ -628,7 +1054,8 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
628
1054
|
// a collection of item views, when the collection is
|
629
1055
|
// empty
|
630
1056
|
showEmptyView: function(){
|
631
|
-
var EmptyView = this
|
1057
|
+
var EmptyView = Marionette.getOption(this, "emptyView");
|
1058
|
+
|
632
1059
|
if (EmptyView && !this._showingEmptyView){
|
633
1060
|
this._showingEmptyView = true;
|
634
1061
|
var model = new Backbone.Model();
|
@@ -650,7 +1077,7 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
650
1077
|
// or from the `itemView` in the object definition. The "options"
|
651
1078
|
// takes precedence.
|
652
1079
|
getItemView: function(item){
|
653
|
-
var itemView = this
|
1080
|
+
var itemView = Marionette.getOption(this, "itemView");
|
654
1081
|
|
655
1082
|
if (!itemView){
|
656
1083
|
var err = new Error("An `itemView` must be specified");
|
@@ -666,11 +1093,20 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
666
1093
|
addItemView: function(item, ItemView, index){
|
667
1094
|
var that = this;
|
668
1095
|
|
669
|
-
|
1096
|
+
// get the itemViewOptions if any were specified
|
1097
|
+
var itemViewOptions;
|
1098
|
+
if (_.isFunction(this.itemViewOptions)){
|
1099
|
+
itemViewOptions = this.itemViewOptions(item);
|
1100
|
+
} else {
|
1101
|
+
itemViewOptions = this.itemViewOptions;
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
// build the view
|
1105
|
+
var view = this.buildItemView(item, ItemView, itemViewOptions);
|
670
1106
|
|
671
1107
|
// Store the child view itself so we can properly
|
672
1108
|
// remove and/or close it later
|
673
|
-
this.storeChild(view);
|
1109
|
+
this.storeChild(item, view);
|
674
1110
|
this.triggerMethod("item:added", view);
|
675
1111
|
|
676
1112
|
// Forward all child item view events through the parent,
|
@@ -706,17 +1142,9 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
706
1142
|
},
|
707
1143
|
|
708
1144
|
// Build an `itemView` for every model in the collection.
|
709
|
-
buildItemView: function(item,
|
710
|
-
var itemViewOptions;
|
711
|
-
|
712
|
-
if (_.isFunction(this.itemViewOptions)){
|
713
|
-
itemViewOptions = this.itemViewOptions(item);
|
714
|
-
} else {
|
715
|
-
itemViewOptions = this.itemViewOptions;
|
716
|
-
}
|
717
|
-
|
1145
|
+
buildItemView: function(item, ItemViewType, itemViewOptions){
|
718
1146
|
var options = _.extend({model: item}, itemViewOptions);
|
719
|
-
var view = new
|
1147
|
+
var view = new ItemViewType(options);
|
720
1148
|
return view;
|
721
1149
|
},
|
722
1150
|
|
@@ -749,8 +1177,8 @@ Marionette.CollectionView = Marionette.View.extend({
|
|
749
1177
|
|
750
1178
|
// Store references to all of the child `itemView`
|
751
1179
|
// instances so they can be managed and cleaned up, later.
|
752
|
-
storeChild: function(view){
|
753
|
-
this.children[
|
1180
|
+
storeChild: function(item, view){
|
1181
|
+
this.children[item.cid] = view;
|
754
1182
|
},
|
755
1183
|
|
756
1184
|
// Internal method to set up the `children` object for
|
@@ -811,7 +1239,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
811
1239
|
// `this.itemView` or Marionette.CompositeView if no `itemView`
|
812
1240
|
// has been defined
|
813
1241
|
getItemView: function(item){
|
814
|
-
var itemView = this
|
1242
|
+
var itemView = Marionette.getOption(this, "itemView") || this.constructor;
|
815
1243
|
|
816
1244
|
if (!itemView){
|
817
1245
|
var err = new Error("An `itemView` must be specified");
|
@@ -832,8 +1260,6 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
832
1260
|
data = this.model.toJSON();
|
833
1261
|
}
|
834
1262
|
|
835
|
-
data = this.mixinTemplateHelpers(data);
|
836
|
-
|
837
1263
|
return data;
|
838
1264
|
},
|
839
1265
|
|
@@ -872,6 +1298,7 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
872
1298
|
renderModel: function(){
|
873
1299
|
var data = {};
|
874
1300
|
data = this.serializeData();
|
1301
|
+
data = this.mixinTemplateHelpers(data);
|
875
1302
|
|
876
1303
|
var template = this.getTemplate();
|
877
1304
|
return Marionette.Renderer.render(template, data);
|
@@ -921,177 +1348,6 @@ Marionette.CompositeView = Marionette.CollectionView.extend({
|
|
921
1348
|
});
|
922
1349
|
|
923
1350
|
|
924
|
-
// Region
|
925
|
-
// ------
|
926
|
-
//
|
927
|
-
// Manage the visual regions of your composite application. See
|
928
|
-
// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
|
929
|
-
|
930
|
-
Marionette.Region = function(options){
|
931
|
-
this.options = options || {};
|
932
|
-
|
933
|
-
var el = this.options.el;
|
934
|
-
delete this.options.el;
|
935
|
-
|
936
|
-
Marionette.addEventBinder(this);
|
937
|
-
|
938
|
-
if (el){
|
939
|
-
this.el = el;
|
940
|
-
}
|
941
|
-
|
942
|
-
if (!this.el){
|
943
|
-
var err = new Error("An 'el' must be specified for a region.");
|
944
|
-
err.name = "NoElError";
|
945
|
-
throw err;
|
946
|
-
}
|
947
|
-
|
948
|
-
if (this.initialize){
|
949
|
-
this.initialize.apply(this, arguments);
|
950
|
-
}
|
951
|
-
};
|
952
|
-
|
953
|
-
|
954
|
-
// Region Type methods
|
955
|
-
// -------------------
|
956
|
-
|
957
|
-
_.extend(Marionette.Region, {
|
958
|
-
|
959
|
-
// Build an instance of a region by passing in a configuration object
|
960
|
-
// and a default region type to use if none is specified in the config.
|
961
|
-
//
|
962
|
-
// The config object should either be a string as a jQuery DOM selector,
|
963
|
-
// a Region type directly, or an object literal that specifies both
|
964
|
-
// a selector and regionType:
|
965
|
-
//
|
966
|
-
// ```js
|
967
|
-
// {
|
968
|
-
// selector: "#foo",
|
969
|
-
// regionType: MyCustomRegion
|
970
|
-
// }
|
971
|
-
// ```
|
972
|
-
//
|
973
|
-
buildRegion: function(regionConfig, defaultRegionType){
|
974
|
-
var regionIsString = (typeof regionConfig === "string");
|
975
|
-
var regionSelectorIsString = (typeof regionConfig.selector === "string");
|
976
|
-
var regionTypeIsUndefined = (typeof regionConfig.regionType === "undefined");
|
977
|
-
var regionIsType = (typeof regionConfig === "function");
|
978
|
-
|
979
|
-
if (!regionIsType && !regionIsString && !regionSelectorIsString) {
|
980
|
-
throw new Error("Region must be specified as a Region type, a selector string or an object with selector property");
|
981
|
-
}
|
982
|
-
|
983
|
-
var selector, RegionType;
|
984
|
-
|
985
|
-
// get the selector for the region
|
986
|
-
|
987
|
-
if (regionIsString) {
|
988
|
-
selector = regionConfig;
|
989
|
-
}
|
990
|
-
|
991
|
-
if (regionConfig.selector) {
|
992
|
-
selector = regionConfig.selector;
|
993
|
-
}
|
994
|
-
|
995
|
-
// get the type for the region
|
996
|
-
|
997
|
-
if (regionIsType){
|
998
|
-
RegionType = regionConfig;
|
999
|
-
}
|
1000
|
-
|
1001
|
-
if (!regionIsType && regionTypeIsUndefined) {
|
1002
|
-
RegionType = defaultRegionType;
|
1003
|
-
}
|
1004
|
-
|
1005
|
-
if (regionConfig.regionType) {
|
1006
|
-
RegionType = regionConfig.regionType;
|
1007
|
-
}
|
1008
|
-
|
1009
|
-
// build the region instance
|
1010
|
-
|
1011
|
-
var regionManager = new RegionType({
|
1012
|
-
el: selector
|
1013
|
-
});
|
1014
|
-
|
1015
|
-
return regionManager;
|
1016
|
-
}
|
1017
|
-
|
1018
|
-
});
|
1019
|
-
|
1020
|
-
// Region Instance Methods
|
1021
|
-
// -----------------------
|
1022
|
-
|
1023
|
-
_.extend(Marionette.Region.prototype, Backbone.Events, {
|
1024
|
-
|
1025
|
-
// Displays a backbone view instance inside of the region.
|
1026
|
-
// Handles calling the `render` method for you. Reads content
|
1027
|
-
// directly from the `el` attribute. Also calls an optional
|
1028
|
-
// `onShow` and `close` method on your view, just after showing
|
1029
|
-
// or just before closing the view, respectively.
|
1030
|
-
show: function(view){
|
1031
|
-
|
1032
|
-
this.ensureEl();
|
1033
|
-
this.close();
|
1034
|
-
|
1035
|
-
view.render();
|
1036
|
-
this.open(view);
|
1037
|
-
|
1038
|
-
Marionette.triggerMethod.call(view, "show");
|
1039
|
-
Marionette.triggerMethod.call(this, "show", view);
|
1040
|
-
|
1041
|
-
this.currentView = view;
|
1042
|
-
},
|
1043
|
-
|
1044
|
-
ensureEl: function(){
|
1045
|
-
if (!this.$el || this.$el.length === 0){
|
1046
|
-
this.$el = this.getEl(this.el);
|
1047
|
-
}
|
1048
|
-
},
|
1049
|
-
|
1050
|
-
// Override this method to change how the region finds the
|
1051
|
-
// DOM element that it manages. Return a jQuery selector object.
|
1052
|
-
getEl: function(selector){
|
1053
|
-
return $(selector);
|
1054
|
-
},
|
1055
|
-
|
1056
|
-
// Override this method to change how the new view is
|
1057
|
-
// appended to the `$el` that the region is managing
|
1058
|
-
open: function(view){
|
1059
|
-
this.$el.html(view.el);
|
1060
|
-
},
|
1061
|
-
|
1062
|
-
// Close the current view, if there is one. If there is no
|
1063
|
-
// current view, it does nothing and returns immediately.
|
1064
|
-
close: function(){
|
1065
|
-
var view = this.currentView;
|
1066
|
-
if (!view || view.isClosed){ return; }
|
1067
|
-
|
1068
|
-
if (view.close) { view.close(); }
|
1069
|
-
Marionette.triggerMethod.call(this, "close");
|
1070
|
-
|
1071
|
-
delete this.currentView;
|
1072
|
-
},
|
1073
|
-
|
1074
|
-
// Attach an existing view to the region. This
|
1075
|
-
// will not call `render` or `onShow` for the new view,
|
1076
|
-
// and will not replace the current HTML for the `el`
|
1077
|
-
// of the region.
|
1078
|
-
attachView: function(view){
|
1079
|
-
this.currentView = view;
|
1080
|
-
},
|
1081
|
-
|
1082
|
-
// Reset the region by closing any existing view and
|
1083
|
-
// clearing out the cached `$el`. The next time a view
|
1084
|
-
// is shown via this region, the region will re-query the
|
1085
|
-
// DOM for the region's `el`.
|
1086
|
-
reset: function(){
|
1087
|
-
this.close();
|
1088
|
-
delete this.$el;
|
1089
|
-
}
|
1090
|
-
});
|
1091
|
-
|
1092
|
-
// Copy the `extend` function used by Backbone's classes
|
1093
|
-
Marionette.Region.extend = Marionette.extend;
|
1094
|
-
|
1095
1351
|
// Layout
|
1096
1352
|
// ------
|
1097
1353
|
|
@@ -1180,24 +1436,92 @@ Marionette.Layout = Marionette.ItemView.extend({
|
|
1180
1436
|
}
|
1181
1437
|
},
|
1182
1438
|
|
1183
|
-
// Close all of the regions that have been opened by
|
1184
|
-
// this layout. This method is called when the layout
|
1185
|
-
// itself is closed.
|
1186
|
-
closeRegions: function () {
|
1187
|
-
var that = this;
|
1188
|
-
_.each(this.regionManagers, function (manager, name) {
|
1189
|
-
manager.close();
|
1190
|
-
});
|
1191
|
-
},
|
1439
|
+
// Close all of the regions that have been opened by
|
1440
|
+
// this layout. This method is called when the layout
|
1441
|
+
// itself is closed.
|
1442
|
+
closeRegions: function () {
|
1443
|
+
var that = this;
|
1444
|
+
_.each(this.regionManagers, function (manager, name) {
|
1445
|
+
manager.close();
|
1446
|
+
});
|
1447
|
+
},
|
1448
|
+
|
1449
|
+
// Destroys all of the regions by removing references
|
1450
|
+
// from the Layout
|
1451
|
+
destroyRegions: function(){
|
1452
|
+
var that = this;
|
1453
|
+
_.each(this.regionManagers, function (manager, name) {
|
1454
|
+
delete that[name];
|
1455
|
+
});
|
1456
|
+
this.regionManagers = {};
|
1457
|
+
}
|
1458
|
+
});
|
1459
|
+
|
1460
|
+
|
1461
|
+
|
1462
|
+
// AppRouter
|
1463
|
+
// ---------
|
1464
|
+
|
1465
|
+
// Reduce the boilerplate code of handling route events
|
1466
|
+
// and then calling a single method on another object.
|
1467
|
+
// Have your routers configured to call the method on
|
1468
|
+
// your object, directly.
|
1469
|
+
//
|
1470
|
+
// Configure an AppRouter with `appRoutes`.
|
1471
|
+
//
|
1472
|
+
// App routers can only take one `controller` object.
|
1473
|
+
// It is recommended that you divide your controller
|
1474
|
+
// objects in to smaller peices of related functionality
|
1475
|
+
// and have multiple routers / controllers, instead of
|
1476
|
+
// just one giant router and controller.
|
1477
|
+
//
|
1478
|
+
// You can also add standard routes to an AppRouter.
|
1479
|
+
|
1480
|
+
Marionette.AppRouter = Backbone.Router.extend({
|
1481
|
+
|
1482
|
+
constructor: function(options){
|
1483
|
+
Backbone.Router.prototype.constructor.call(this, options);
|
1484
|
+
|
1485
|
+
if (this.appRoutes){
|
1486
|
+
var controller = this.controller;
|
1487
|
+
if (options && options.controller) {
|
1488
|
+
controller = options.controller;
|
1489
|
+
}
|
1490
|
+
this.processAppRoutes(controller, this.appRoutes);
|
1491
|
+
}
|
1492
|
+
},
|
1493
|
+
|
1494
|
+
// Internal method to process the `appRoutes` for the
|
1495
|
+
// router, and turn them in to routes that trigger the
|
1496
|
+
// specified method on the specified `controller`.
|
1497
|
+
processAppRoutes: function(controller, appRoutes){
|
1498
|
+
var method, methodName;
|
1499
|
+
var route, routesLength, i;
|
1500
|
+
var routes = [];
|
1501
|
+
var router = this;
|
1502
|
+
|
1503
|
+
for(route in appRoutes){
|
1504
|
+
if (appRoutes.hasOwnProperty(route)){
|
1505
|
+
routes.unshift([route, appRoutes[route]]);
|
1506
|
+
}
|
1507
|
+
}
|
1508
|
+
|
1509
|
+
routesLength = routes.length;
|
1510
|
+
for (i = 0; i < routesLength; i++){
|
1511
|
+
route = routes[i][0];
|
1512
|
+
methodName = routes[i][1];
|
1513
|
+
method = controller[methodName];
|
1514
|
+
|
1515
|
+
if (!method){
|
1516
|
+
var msg = "Method '" + methodName + "' was not found on the controller";
|
1517
|
+
var err = new Error(msg);
|
1518
|
+
err.name = "NoMethodError";
|
1519
|
+
throw err;
|
1520
|
+
}
|
1192
1521
|
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
var that = this;
|
1197
|
-
_.each(this.regionManagers, function (manager, name) {
|
1198
|
-
delete that[name];
|
1199
|
-
});
|
1200
|
-
this.regionManagers = {};
|
1522
|
+
method = _.bind(method, controller);
|
1523
|
+
router.route(route, methodName, method);
|
1524
|
+
}
|
1201
1525
|
}
|
1202
1526
|
});
|
1203
1527
|
|
@@ -1285,73 +1609,6 @@ _.extend(Marionette.Application.prototype, Backbone.Events, {
|
|
1285
1609
|
// Copy the `extend` function used by Backbone's classes
|
1286
1610
|
Marionette.Application.extend = Marionette.extend;
|
1287
1611
|
|
1288
|
-
// AppRouter
|
1289
|
-
// ---------
|
1290
|
-
|
1291
|
-
// Reduce the boilerplate code of handling route events
|
1292
|
-
// and then calling a single method on another object.
|
1293
|
-
// Have your routers configured to call the method on
|
1294
|
-
// your object, directly.
|
1295
|
-
//
|
1296
|
-
// Configure an AppRouter with `appRoutes`.
|
1297
|
-
//
|
1298
|
-
// App routers can only take one `controller` object.
|
1299
|
-
// It is recommended that you divide your controller
|
1300
|
-
// objects in to smaller peices of related functionality
|
1301
|
-
// and have multiple routers / controllers, instead of
|
1302
|
-
// just one giant router and controller.
|
1303
|
-
//
|
1304
|
-
// You can also add standard routes to an AppRouter.
|
1305
|
-
|
1306
|
-
Marionette.AppRouter = Backbone.Router.extend({
|
1307
|
-
|
1308
|
-
constructor: function(options){
|
1309
|
-
Backbone.Router.prototype.constructor.call(this, options);
|
1310
|
-
|
1311
|
-
if (this.appRoutes){
|
1312
|
-
var controller = this.controller;
|
1313
|
-
if (options && options.controller) {
|
1314
|
-
controller = options.controller;
|
1315
|
-
}
|
1316
|
-
this.processAppRoutes(controller, this.appRoutes);
|
1317
|
-
}
|
1318
|
-
},
|
1319
|
-
|
1320
|
-
// Internal method to process the `appRoutes` for the
|
1321
|
-
// router, and turn them in to routes that trigger the
|
1322
|
-
// specified method on the specified `controller`.
|
1323
|
-
processAppRoutes: function(controller, appRoutes){
|
1324
|
-
var method, methodName;
|
1325
|
-
var route, routesLength, i;
|
1326
|
-
var routes = [];
|
1327
|
-
var router = this;
|
1328
|
-
|
1329
|
-
for(route in appRoutes){
|
1330
|
-
if (appRoutes.hasOwnProperty(route)){
|
1331
|
-
routes.unshift([route, appRoutes[route]]);
|
1332
|
-
}
|
1333
|
-
}
|
1334
|
-
|
1335
|
-
routesLength = routes.length;
|
1336
|
-
for (i = 0; i < routesLength; i++){
|
1337
|
-
route = routes[i][0];
|
1338
|
-
methodName = routes[i][1];
|
1339
|
-
method = controller[methodName];
|
1340
|
-
|
1341
|
-
if (!method){
|
1342
|
-
var msg = "Method '" + methodName + "' was not found on the controller";
|
1343
|
-
var err = new Error(msg);
|
1344
|
-
err.name = "NoMethodError";
|
1345
|
-
throw err;
|
1346
|
-
}
|
1347
|
-
|
1348
|
-
method = _.bind(method, controller);
|
1349
|
-
router.route(route, methodName, method);
|
1350
|
-
}
|
1351
|
-
}
|
1352
|
-
});
|
1353
|
-
|
1354
|
-
|
1355
1612
|
// Module
|
1356
1613
|
// ------
|
1357
1614
|
|
@@ -1567,169 +1824,6 @@ _.extend(Marionette.Module, {
|
|
1567
1824
|
}
|
1568
1825
|
});
|
1569
1826
|
|
1570
|
-
// Template Cache
|
1571
|
-
// --------------
|
1572
|
-
|
1573
|
-
// Manage templates stored in `<script>` blocks,
|
1574
|
-
// caching them for faster access.
|
1575
|
-
Marionette.TemplateCache = function(templateId){
|
1576
|
-
this.templateId = templateId;
|
1577
|
-
};
|
1578
|
-
|
1579
|
-
// TemplateCache object-level methods. Manage the template
|
1580
|
-
// caches from these method calls instead of creating
|
1581
|
-
// your own TemplateCache instances
|
1582
|
-
_.extend(Marionette.TemplateCache, {
|
1583
|
-
templateCaches: {},
|
1584
|
-
|
1585
|
-
// Get the specified template by id. Either
|
1586
|
-
// retrieves the cached version, or loads it
|
1587
|
-
// from the DOM.
|
1588
|
-
get: function(templateId){
|
1589
|
-
var that = this;
|
1590
|
-
var cachedTemplate = this.templateCaches[templateId];
|
1591
|
-
|
1592
|
-
if (!cachedTemplate){
|
1593
|
-
cachedTemplate = new Marionette.TemplateCache(templateId);
|
1594
|
-
this.templateCaches[templateId] = cachedTemplate;
|
1595
|
-
}
|
1596
|
-
|
1597
|
-
return cachedTemplate.load();
|
1598
|
-
},
|
1599
|
-
|
1600
|
-
// Clear templates from the cache. If no arguments
|
1601
|
-
// are specified, clears all templates:
|
1602
|
-
// `clear()`
|
1603
|
-
//
|
1604
|
-
// If arguments are specified, clears each of the
|
1605
|
-
// specified templates from the cache:
|
1606
|
-
// `clear("#t1", "#t2", "...")`
|
1607
|
-
clear: function(){
|
1608
|
-
var i;
|
1609
|
-
var length = arguments.length;
|
1610
|
-
|
1611
|
-
if (length > 0){
|
1612
|
-
for(i=0; i<length; i++){
|
1613
|
-
delete this.templateCaches[arguments[i]];
|
1614
|
-
}
|
1615
|
-
} else {
|
1616
|
-
this.templateCaches = {};
|
1617
|
-
}
|
1618
|
-
}
|
1619
|
-
});
|
1620
|
-
|
1621
|
-
// TemplateCache instance methods, allowing each
|
1622
|
-
// template cache object to manage it's own state
|
1623
|
-
// and know whether or not it has been loaded
|
1624
|
-
_.extend(Marionette.TemplateCache.prototype, {
|
1625
|
-
|
1626
|
-
// Internal method to load the template asynchronously.
|
1627
|
-
load: function(){
|
1628
|
-
var that = this;
|
1629
|
-
|
1630
|
-
// Guard clause to prevent loading this template more than once
|
1631
|
-
if (this.compiledTemplate){
|
1632
|
-
return this.compiledTemplate;
|
1633
|
-
}
|
1634
|
-
|
1635
|
-
// Load the template and compile it
|
1636
|
-
var template = this.loadTemplate(this.templateId);
|
1637
|
-
this.compiledTemplate = this.compileTemplate(template);
|
1638
|
-
|
1639
|
-
return this.compiledTemplate;
|
1640
|
-
},
|
1641
|
-
|
1642
|
-
// Load a template from the DOM, by default. Override
|
1643
|
-
// this method to provide your own template retrieval,
|
1644
|
-
// such as asynchronous loading from a server.
|
1645
|
-
loadTemplate: function(templateId){
|
1646
|
-
var template = $(templateId).html();
|
1647
|
-
|
1648
|
-
if (!template || template.length === 0){
|
1649
|
-
var msg = "Could not find template: '" + templateId + "'";
|
1650
|
-
var err = new Error(msg);
|
1651
|
-
err.name = "NoTemplateError";
|
1652
|
-
throw err;
|
1653
|
-
}
|
1654
|
-
|
1655
|
-
return template;
|
1656
|
-
},
|
1657
|
-
|
1658
|
-
// Pre-compile the template before caching it. Override
|
1659
|
-
// this method if you do not need to pre-compile a template
|
1660
|
-
// (JST / RequireJS for example) or if you want to change
|
1661
|
-
// the template engine used (Handebars, etc).
|
1662
|
-
compileTemplate: function(rawTemplate){
|
1663
|
-
return _.template(rawTemplate);
|
1664
|
-
}
|
1665
|
-
});
|
1666
|
-
|
1667
|
-
|
1668
|
-
// Renderer
|
1669
|
-
// --------
|
1670
|
-
|
1671
|
-
// Render a template with data by passing in the template
|
1672
|
-
// selector and the data to render.
|
1673
|
-
Marionette.Renderer = {
|
1674
|
-
|
1675
|
-
// Render a template with data. The `template` parameter is
|
1676
|
-
// passed to the `TemplateCache` object to retrieve the
|
1677
|
-
// template function. Override this method to provide your own
|
1678
|
-
// custom rendering and template handling for all of Marionette.
|
1679
|
-
render: function(template, data){
|
1680
|
-
var templateFunc = typeof template === 'function' ? template : Marionette.TemplateCache.get(template);
|
1681
|
-
var html = templateFunc(data);
|
1682
|
-
return html;
|
1683
|
-
}
|
1684
|
-
};
|
1685
|
-
|
1686
|
-
|
1687
|
-
// Callbacks
|
1688
|
-
// ---------
|
1689
|
-
|
1690
|
-
// A simple way of managing a collection of callbacks
|
1691
|
-
// and executing them at a later point in time, using jQuery's
|
1692
|
-
// `Deferred` object.
|
1693
|
-
Marionette.Callbacks = function(){
|
1694
|
-
this._deferred = $.Deferred();
|
1695
|
-
this._callbacks = [];
|
1696
|
-
};
|
1697
|
-
|
1698
|
-
_.extend(Marionette.Callbacks.prototype, {
|
1699
|
-
|
1700
|
-
// Add a callback to be executed. Callbacks added here are
|
1701
|
-
// guaranteed to execute, even if they are added after the
|
1702
|
-
// `run` method is called.
|
1703
|
-
add: function(callback, contextOverride){
|
1704
|
-
this._callbacks.push({cb: callback, ctx: contextOverride});
|
1705
|
-
|
1706
|
-
this._deferred.done(function(context, options){
|
1707
|
-
if (contextOverride){ context = contextOverride; }
|
1708
|
-
callback.call(context, options);
|
1709
|
-
});
|
1710
|
-
},
|
1711
|
-
|
1712
|
-
// Run all registered callbacks with the context specified.
|
1713
|
-
// Additional callbacks can be added after this has been run
|
1714
|
-
// and they will still be executed.
|
1715
|
-
run: function(options, context){
|
1716
|
-
this._deferred.resolve(context, options);
|
1717
|
-
},
|
1718
|
-
|
1719
|
-
// Resets the list of callbacks to be run, allowing the same list
|
1720
|
-
// to be run multiple times - whenever the `run` method is called.
|
1721
|
-
reset: function(){
|
1722
|
-
var that = this;
|
1723
|
-
var callbacks = this._callbacks;
|
1724
|
-
this._deferred = $.Deferred();
|
1725
|
-
this._callbacks = [];
|
1726
|
-
_.each(callbacks, function(cb){
|
1727
|
-
that.add(cb.cb, cb.ctx);
|
1728
|
-
});
|
1729
|
-
}
|
1730
|
-
});
|
1731
|
-
|
1732
|
-
|
1733
1827
|
|
1734
1828
|
return Marionette;
|
1735
1829
|
})(Backbone, _, $ || window.jQuery || window.Zepto || window.ender);
|