Marionette.Region = Marionette.Object.extend({
constructor: function(options) {
this.options = options || {};
this.el = this.getOption('el');
this.el = this.el instanceof Backbone.$ ? this.el[0] : this.el;
throw new Marionette.Error({
message: 'An "el" must be specified for a region.'
this.$el = this.getEl(this.el);
Marionette.Object.call(this, options);
show: function(view, options) {
if (!this._ensureElement()) {
this._ensureViewIsIntact(view);
Marionette.MonitorDOMRefresh(view);
var showOptions = options || {};
var isDifferentView = view !== this.currentView;
var preventDestroy = !!showOptions.preventDestroy;
var forceShow = !!showOptions.forceShow;
var isChangingView = !!this.currentView;
var _shouldDestroyView = isDifferentView && !preventDestroy;
var _shouldShowView = isDifferentView || forceShow;
this.triggerMethod('before:swapOut', this.currentView, this, options);
if (this.currentView && isDifferentView) {
delete this.currentView._parent;
if (_shouldDestroyView) {
} else if (isChangingView && _shouldShowView) {
this.currentView.off('destroy', this.empty, this);
view.once('destroy', this.empty, this);
this.triggerMethod('before:swap', view, this, options);
this.triggerMethod('before:show', view, this, options);
Marionette.triggerMethodOn(view, 'before:show', view, this, options);
this.triggerMethod('swapOut', this.currentView, this, options);
var attachedRegion = Marionette.isNodeAttached(this.el);
var attachOptions = _.extend({
triggerBeforeAttach: this.triggerBeforeAttach,
triggerAttach: this.triggerAttach
if (attachedRegion && attachOptions.triggerBeforeAttach) {
displayedViews = this._displayedViews(view);
this._triggerAttach(displayedViews, 'before:');
if (attachedRegion && attachOptions.triggerAttach) {
displayedViews = this._displayedViews(view);
this._triggerAttach(displayedViews);
this.triggerMethod('swap', view, this, options);
this.triggerMethod('show', view, this, options);
Marionette.triggerMethodOn(view, 'show', view, this, options);
triggerBeforeAttach: true,
_triggerAttach: function(views, prefix) {
var eventName = (prefix || '') + 'attach';
_.each(views, function(view) {
Marionette.triggerMethodOn(view, eventName, view, this);
_displayedViews: function(view) {
return _.union([view], _.result(view, '_getNestedViews') || []);
_renderView: function(view) {
if (!view.supportsRenderLifecycle) {
Marionette.triggerMethodOn(view, 'before:render', view);
if (!view.supportsRenderLifecycle) {
Marionette.triggerMethodOn(view, 'render', view);
_ensureElement: function() {
if (!_.isObject(this.el)) {
this.$el = this.getEl(this.el);
if (!this.$el || this.$el.length === 0) {
if (this.getOption('allowMissingEl')) {
throw new Marionette.Error('An "el" ' + this.$el.selector + ' must exist in DOM');
_ensureViewIsIntact: function(view) {
throw new Marionette.Error({
message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.'
throw new Marionette.Error({
name: 'ViewDestroyedError',
message: 'View (cid: "' + view.cid + '") has already been destroyed and cannot be used.'
return Backbone.$(el, Marionette._getValue(this.options.parentEl, this));
attachHtml: function(view) {
this.$el.contents().detach();
this.el.appendChild(view.el);
empty: function(options) {
var view = this.currentView;
var emptyOptions = options || {};
var preventDestroy = !!emptyOptions.preventDestroy;
if (!view) { return this; }
view.off('destroy', this.empty, this);
this.triggerMethod('before:empty', view);
this.triggerMethod('empty', view);
this.$el.contents().detach();
_destroyView: function() {
var view = this.currentView;
if (view.isDestroyed) { return; }
if (!view.supportsDestroyLifecycle) {
Marionette.triggerMethodOn(view, 'before:destroy', view);
if (!view.supportsDestroyLifecycle) {
Marionette.triggerMethodOn(view, 'destroy', view);
attachView: function(view) {
delete this.currentView._parent;
return !!this.currentView;
this.el = this.$el.selector;
buildRegion: function(regionConfig, DefaultRegionClass) {
if (_.isString(regionConfig)) {
return this._buildRegionFromSelector(regionConfig, DefaultRegionClass);
if (regionConfig.selector || regionConfig.el || regionConfig.regionClass) {
return this._buildRegionFromObject(regionConfig, DefaultRegionClass);
if (_.isFunction(regionConfig)) {
return this._buildRegionFromRegionClass(regionConfig);
throw new Marionette.Error({
message: 'Improper region configuration type.',
url: 'marionette.region.html#region-configuration-types'
_buildRegionFromSelector: function(selector, DefaultRegionClass) {
return new DefaultRegionClass({el: selector});
_buildRegionFromObject: function(regionConfig, DefaultRegionClass) {
var RegionClass = regionConfig.regionClass || DefaultRegionClass;
var options = _.omit(regionConfig, 'selector', 'regionClass');
if (regionConfig.selector && !options.el) {
options.el = regionConfig.selector;
return new RegionClass(options);
_buildRegionFromRegionClass: function(RegionClass) {
return new RegionClass();