// MarionetteJS (Backbone.Marionette)
// ----------------------------------
// v3.1.0
//
// Copyright (c)2016 Derick Bailey, Muted Solutions, LLC.
// Distributed under MIT license
//
// http://marionettejs.com


(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone'), require('underscore'), require('backbone.radio')) :
	typeof define === 'function' && define.amd ? define(['backbone', 'underscore', 'backbone.radio'], factory) :
	(global.Marionette = global['Mn'] = factory(global.Backbone,global._,global.Backbone.Radio));
}(this, function (Backbone,_,Radio) { 'use strict';

	Backbone = 'default' in Backbone ? Backbone['default'] : Backbone;
	_ = 'default' in _ ? _['default'] : _;
	Radio = 'default' in Radio ? Radio['default'] : Radio;

	var version = "3.1.0";

	//Internal utility for creating context style global utils
	var proxy = function proxy(method) {
	  return function (context) {
	    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
	      args[_key - 1] = arguments[_key];
	    }

	    return method.apply(context, args);
	  };
	};

	// Borrow the Backbone `extend` method so we can use it as needed
	var extend = Backbone.Model.extend;

	var deprecate = function deprecate(message, test) {
	  if (_.isObject(message)) {
	    message = message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : '');
	  }

	  if (!Marionette.DEV_MODE) {
	    return;
	  }

	  if ((test === undefined || !test) && !deprecate._cache[message]) {
	    deprecate._warn('Deprecation warning: ' + message);
	    deprecate._cache[message] = true;
	  }
	};

	deprecate._console = typeof console !== 'undefined' ? console : {};
	deprecate._warn = function () {
	  var warn = deprecate._console.warn || deprecate._console.log || _.noop;
	  return warn.apply(deprecate._console, arguments);
	};
	deprecate._cache = {};

	// Determine if `el` is a child of the document
	var isNodeAttached = function isNodeAttached(el) {
	  return Backbone.$.contains(document.documentElement, el);
	};

	// Merge `keys` from `options` onto `this`
	var mergeOptions = function mergeOptions(options, keys) {
	  var _this = this;

	  if (!options) {
	    return;
	  }

	  _.each(keys, function (key) {
	    var option = options[key];
	    if (option !== undefined) {
	      _this[key] = option;
	    }
	  });
	};

	// Marionette.getOption
	// --------------------

	// Retrieve an object, function or other value from the
	// object or its `options`, with `options` taking precedence.
	var getOption = function getOption(optionName) {
	  if (!optionName) {
	    return;
	  }
	  if (this.options && this.options[optionName] !== undefined) {
	    return this.options[optionName];
	  } else {
	    return this[optionName];
	  }
	};

	// Marionette.normalizeMethods
	// ----------------------

	// Pass in a mapping of events => functions or function names
	// and return a mapping of events => functions
	var normalizeMethods = function normalizeMethods(hash) {
	  var _this = this;

	  return _.reduce(hash, function (normalizedHash, method, name) {
	    if (!_.isFunction(method)) {
	      method = _this[method];
	    }
	    if (method) {
	      normalizedHash[name] = method;
	    }
	    return normalizedHash;
	  }, {});
	};

	// split the event name on the ":"
	var splitter = /(^|:)(\w)/gi;

	// take the event section ("section1:section2:section3")
	// and turn it in to uppercase name onSection1Section2Section3
	function getEventName(match, prefix, eventName) {
	  return eventName.toUpperCase();
	}

	var getOnMethodName = _.memoize(function (event) {
	  return 'on' + event.replace(splitter, getEventName);
	});

	// Trigger an event and/or a corresponding method name. Examples:
	//
	// `this.triggerMethod("foo")` will trigger the "foo" event and
	// call the "onFoo" method.
	//
	// `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
	// call the "onFooBar" method.
	function triggerMethod(event) {
	  for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
	    args[_key - 1] = arguments[_key];
	  }

	  // get the method name from the event name
	  var methodName = getOnMethodName(event);
	  var method = getOption.call(this, methodName);
	  var result = void 0;

	  // call the onMethodName if it exists
	  if (_.isFunction(method)) {
	    // pass all args, except the event name
	    result = method.apply(this, args);
	  }

	  // trigger the event
	  this.trigger.apply(this, arguments);

	  return result;
	}

	// triggerMethodOn invokes triggerMethod on a specific context
	//
	// e.g. `Marionette.triggerMethodOn(view, 'show')`
	// will trigger a "show" event or invoke onShow the view.
	function triggerMethodOn(context) {
	  for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
	    args[_key2 - 1] = arguments[_key2];
	  }

	  if (_.isFunction(context.triggerMethod)) {
	    return context.triggerMethod.apply(context, args);
	  }

	  return triggerMethod.apply(context, args);
	}

	// Trigger method on children unless a pure Backbone.View
	function triggerMethodChildren(view, event, shouldTrigger) {
	  if (!view._getImmediateChildren) {
	    return;
	  }
	  _.each(view._getImmediateChildren(), function (child) {
	    if (!shouldTrigger(child)) {
	      return;
	    }
	    triggerMethodOn(child, event, child);
	  });
	}

	function shouldTriggerAttach(view) {
	  return !view._isAttached;
	}

	function shouldAttach(view) {
	  if (!shouldTriggerAttach(view)) {
	    return false;
	  }
	  view._isAttached = true;
	  return true;
	}

	function shouldTriggerDetach(view) {
	  return view._isAttached;
	}

	function shouldDetach(view) {
	  if (!shouldTriggerDetach(view)) {
	    return false;
	  }
	  view._isAttached = false;
	  return true;
	}

	function triggerDOMRefresh(view) {
	  if (view._isAttached && view._isRendered) {
	    triggerMethodOn(view, 'dom:refresh', view);
	  }
	}

	function handleBeforeAttach() {
	  triggerMethodChildren(this, 'before:attach', shouldTriggerAttach);
	}

	function handleAttach() {
	  triggerMethodChildren(this, 'attach', shouldAttach);
	  triggerDOMRefresh(this);
	}

	function handleBeforeDetach() {
	  triggerMethodChildren(this, 'before:detach', shouldTriggerDetach);
	}

	function handleDetach() {
	  triggerMethodChildren(this, 'detach', shouldDetach);
	}

	function handleRender() {
	  triggerDOMRefresh(this);
	}

	// Monitor a view's state, propagating attach/detach events to children and firing dom:refresh
	// whenever a rendered view is attached or an attached view is rendered.
	function monitorViewEvents(view) {
	  if (view._areViewEventsMonitored) {
	    return;
	  }

	  view._areViewEventsMonitored = true;

	  view.on({
	    'before:attach': handleBeforeAttach,
	    'attach': handleAttach,
	    'before:detach': handleBeforeDetach,
	    'detach': handleDetach,
	    'render': handleRender
	  });
	}

	var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number'];

	var MarionetteError = extend.call(Error, {
	  urlRoot: 'http://marionettejs.com/docs/v' + version + '/',

	  constructor: function constructor(message, options) {
	    if (_.isObject(message)) {
	      options = message;
	      message = options.message;
	    } else if (!options) {
	      options = {};
	    }

	    var error = Error.call(this, message);
	    _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps));

	    this.captureStackTrace();

	    if (options.url) {
	      this.url = this.urlRoot + options.url;
	    }
	  },
	  captureStackTrace: function captureStackTrace() {
	    if (Error.captureStackTrace) {
	      Error.captureStackTrace(this, MarionetteError);
	    }
	  },
	  toString: function toString() {
	    return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : '');
	  }
	});

	MarionetteError.extend = extend;

	// Bind/unbind the event to handlers specified as a string of
	// handler names on the target object
	function bindFromStrings(target, entity, evt, methods, actionName) {
	  var methodNames = methods.split(/\s+/);

	  _.each(methodNames, function (methodName) {
	    var method = target[methodName];
	    if (!method) {
	      throw new MarionetteError('Method "' + methodName + '" was configured as an event handler, but does not exist.');
	    }

	    target[actionName](entity, evt, method);
	  });
	}

	// generic looping function
	function iterateEvents(target, entity, bindings, actionName) {
	  if (!entity || !bindings) {
	    return;
	  }

	  // type-check bindings
	  if (!_.isObject(bindings)) {
	    throw new MarionetteError({
	      message: 'Bindings must be an object.',
	      url: 'marionette.functions.html#marionettebindevents'
	    });
	  }

	  // iterate the bindings and bind/unbind them
	  _.each(bindings, function (method, evt) {

	    // allow for a list of method names as a string
	    if (_.isString(method)) {
	      bindFromStrings(target, entity, evt, method, actionName);
	      return;
	    }

	    target[actionName](entity, evt, method);
	  });
	}

	function bindEvents(entity, bindings) {
	  iterateEvents(this, entity, bindings, 'listenTo');
	  return this;
	}

	function unbindEvents(entity, bindings) {
	  iterateEvents(this, entity, bindings, 'stopListening');
	  return this;
	}

	function iterateReplies(target, channel, bindings, actionName) {
	  if (!channel || !bindings) {
	    return;
	  }

	  // type-check bindings
	  if (!_.isObject(bindings)) {
	    throw new MarionetteError({
	      message: 'Bindings must be an object.',
	      url: 'marionette.functions.html#marionettebindrequests'
	    });
	  }

	  var normalizedRadioRequests = normalizeMethods.call(target, bindings);

	  channel[actionName](normalizedRadioRequests, target);
	}

	function bindRequests(channel, bindings) {
	  iterateReplies(this, channel, bindings, 'reply');
	  return this;
	}

	function unbindRequests(channel, bindings) {
	  iterateReplies(this, channel, bindings, 'stopReplying');
	  return this;
	}

	// Internal utility for setting options consistently across Mn
	var setOptions = function setOptions() {
	  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
	    args[_key] = arguments[_key];
	  }

	  this.options = _.extend.apply(_, [{}, _.result(this, 'options')].concat(args));
	};

	var CommonMixin = {

	  // Imports the "normalizeMethods" to transform hashes of
	  // events=>function references/names to a hash of events=>function references
	  normalizeMethods: normalizeMethods,

	  _setOptions: setOptions,

	  // A handy way to merge passed-in options onto the instance
	  mergeOptions: mergeOptions,

	  // Enable getting options from this or this.options by name.
	  getOption: getOption,

	  // Enable binding view's events from another entity.
	  bindEvents: bindEvents,

	  // Enable unbinding view's events from another entity.
	  unbindEvents: unbindEvents
	};

	// MixinOptions
	// - channelName
	// - radioEvents
	// - radioRequests

	var RadioMixin = {
	  _initRadio: function _initRadio() {
	    var channelName = _.result(this, 'channelName');

	    if (!channelName) {
	      return;
	    }

	    /* istanbul ignore next */
	    if (!Radio) {
	      throw new MarionetteError({
	        name: 'BackboneRadioMissing',
	        message: 'The dependency "backbone.radio" is missing.'
	      });
	    }

	    var channel = this._channel = Radio.channel(channelName);

	    var radioEvents = _.result(this, 'radioEvents');
	    this.bindEvents(channel, radioEvents);

	    var radioRequests = _.result(this, 'radioRequests');
	    this.bindRequests(channel, radioRequests);

	    this.on('destroy', this._destroyRadio);
	  },
	  _destroyRadio: function _destroyRadio() {
	    this._channel.stopReplying(null, null, this);
	  },
	  getChannel: function getChannel() {
	    return this._channel;
	  },


	  // Proxy `bindEvents`
	  bindEvents: bindEvents,

	  // Proxy `unbindEvents`
	  unbindEvents: unbindEvents,

	  // Proxy `bindRequests`
	  bindRequests: bindRequests,

	  // Proxy `unbindRequests`
	  unbindRequests: unbindRequests

	};

	var ClassOptions = ['channelName', 'radioEvents', 'radioRequests'];

	// A Base Class that other Classes should descend from.
	// Object borrows many conventions and utilities from Backbone.
	var MarionetteObject = function MarionetteObject(options) {
	  this._setOptions(options);
	  this.mergeOptions(options, ClassOptions);
	  this.cid = _.uniqueId(this.cidPrefix);
	  this._initRadio();
	  this.initialize.apply(this, arguments);
	};

	MarionetteObject.extend = extend;

	// Object Methods
	// --------------

	// Ensure it can trigger events with Backbone.Events
	_.extend(MarionetteObject.prototype, Backbone.Events, CommonMixin, RadioMixin, {
	  cidPrefix: 'mno',

	  // for parity with Marionette.AbstractView lifecyle
	  _isDestroyed: false,

	  isDestroyed: function isDestroyed() {
	    return this._isDestroyed;
	  },


	  //this is a noop method intended to be overridden by classes that extend from this base
	  initialize: function initialize() {},
	  destroy: function destroy() {
	    if (this._isDestroyed) {
	      return this;
	    }

	    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
	      args[_key] = arguments[_key];
	    }

	    this.triggerMethod.apply(this, ['before:destroy', this].concat(args));

	    this._isDestroyed = true;
	    this.triggerMethod.apply(this, ['destroy', this].concat(args));
	    this.stopListening();

	    return this;
	  },


	  triggerMethod: triggerMethod
	});

	// Manage templates stored in `<script>` blocks,
	// caching them for faster access.
	var TemplateCache = function TemplateCache(templateId) {
	  this.templateId = templateId;
	};

	// TemplateCache object-level methods. Manage the template
	// caches from these method calls instead of creating
	// your own TemplateCache instances
	_.extend(TemplateCache, {
	  templateCaches: {},

	  // Get the specified template by id. Either
	  // retrieves the cached version, or loads it
	  // from the DOM.
	  get: function get(templateId, options) {
	    var cachedTemplate = this.templateCaches[templateId];

	    if (!cachedTemplate) {
	      cachedTemplate = new TemplateCache(templateId);
	      this.templateCaches[templateId] = cachedTemplate;
	    }

	    return cachedTemplate.load(options);
	  },


	  // Clear templates from the cache. If no arguments
	  // are specified, clears all templates:
	  // `clear()`
	  //
	  // If arguments are specified, clears each of the
	  // specified templates from the cache:
	  // `clear("#t1", "#t2", "...")`
	  clear: function clear() {
	    var i = void 0;

	    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
	      args[_key] = arguments[_key];
	    }

	    var length = args.length;

	    if (length > 0) {
	      for (i = 0; i < length; i++) {
	        delete this.templateCaches[args[i]];
	      }
	    } else {
	      this.templateCaches = {};
	    }
	  }
	});

	// TemplateCache instance methods, allowing each
	// template cache object to manage its own state
	// and know whether or not it has been loaded
	_.extend(TemplateCache.prototype, {

	  // Internal method to load the template
	  load: function load(options) {
	    // Guard clause to prevent loading this template more than once
	    if (this.compiledTemplate) {
	      return this.compiledTemplate;
	    }

	    // Load the template and compile it
	    var template = this.loadTemplate(this.templateId, options);
	    this.compiledTemplate = this.compileTemplate(template, options);

	    return this.compiledTemplate;
	  },


	  // Load a template from the DOM, by default. Override
	  // this method to provide your own template retrieval
	  // For asynchronous loading with AMD/RequireJS, consider
	  // using a template-loader plugin as described here:
	  // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
	  loadTemplate: function loadTemplate(templateId, options) {
	    var $template = Backbone.$(templateId);

	    if (!$template.length) {
	      throw new MarionetteError({
	        name: 'NoTemplateError',
	        message: 'Could not find template: "' + templateId + '"'
	      });
	    }
	    return $template.html();
	  },


	  // Pre-compile the template before caching it. Override
	  // this method if you do not need to pre-compile a template
	  // (JST / RequireJS for example) or if you want to change
	  // the template engine used (Handebars, etc).
	  compileTemplate: function compileTemplate(rawTemplate, options) {
	    return _.template(rawTemplate, options);
	  }
	});

	var _invoke = _.invokeMap || _.invoke;

	function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

	// MixinOptions
	// - behaviors

	// Takes care of getting the behavior class
	// given options and a key.
	// If a user passes in options.behaviorClass
	// default to using that.
	// If a user passes in a Behavior Class directly, use that
	// Otherwise delegate the lookup to the users `behaviorsLookup` implementation.
	function getBehaviorClass(options, key) {
	  if (options.behaviorClass) {
	    return options.behaviorClass;
	    //treat functions as a Behavior constructor
	  } else if (_.isFunction(options)) {
	    return options;
	  }

	  // behaviorsLookup can be either a flat object or a method
	  if (_.isFunction(Marionette.Behaviors.behaviorsLookup)) {
	    return Marionette.Behaviors.behaviorsLookup(options, key)[key];
	  }

	  return Marionette.Behaviors.behaviorsLookup[key];
	}

	// Iterate over the behaviors object, for each behavior
	// instantiate it and get its grouped behaviors.
	// This accepts a list of behaviors in either an object or array form
	function parseBehaviors(view, behaviors) {
	  return _.chain(behaviors).map(function (options, key) {
	    var BehaviorClass = getBehaviorClass(options, key);
	    //if we're passed a class directly instead of an object
	    var _options = options === BehaviorClass ? {} : options;
	    var behavior = new BehaviorClass(_options, view);
	    var nestedBehaviors = parseBehaviors(view, _.result(behavior, 'behaviors'));

	    return [behavior].concat(nestedBehaviors);
	  }).flatten().value();
	}

	var BehaviorsMixin = {
	  _initBehaviors: function _initBehaviors() {
	    var behaviors = _.result(this, 'behaviors');

	    // Behaviors defined on a view can be a flat object literal
	    // or it can be a function that returns an object.
	    this._behaviors = _.isObject(behaviors) ? parseBehaviors(this, behaviors) : {};
	  },
	  _getBehaviorTriggers: function _getBehaviorTriggers() {
	    var triggers = _invoke(this._behaviors, 'getTriggers');
	    return _.extend.apply(_, [{}].concat(_toConsumableArray(triggers)));
	  },
	  _getBehaviorEvents: function _getBehaviorEvents() {
	    var events = _invoke(this._behaviors, 'getEvents');
	    return _.extend.apply(_, [{}].concat(_toConsumableArray(events)));
	  },


	  // proxy behavior $el to the view's $el.
	  _proxyBehaviorViewProperties: function _proxyBehaviorViewProperties() {
	    _invoke(this._behaviors, 'proxyViewProperties');
	  },


	  // delegate modelEvents and collectionEvents
	  _delegateBehaviorEntityEvents: function _delegateBehaviorEntityEvents() {
	    _invoke(this._behaviors, 'delegateEntityEvents');
	  },


	  // undelegate modelEvents and collectionEvents
	  _undelegateBehaviorEntityEvents: function _undelegateBehaviorEntityEvents() {
	    _invoke(this._behaviors, 'undelegateEntityEvents');
	  },
	  _destroyBehaviors: function _destroyBehaviors(args) {
	    // Call destroy on each behavior after
	    // destroying the view.
	    // This unbinds event listeners
	    // that behaviors have registered for.
	    _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(_toConsumableArray(args)));
	  },
	  _bindBehaviorUIElements: function _bindBehaviorUIElements() {
	    _invoke(this._behaviors, 'bindUIElements');
	  },
	  _unbindBehaviorUIElements: function _unbindBehaviorUIElements() {
	    _invoke(this._behaviors, 'unbindUIElements');
	  },
	  _triggerEventOnBehaviors: function _triggerEventOnBehaviors() {
	    var behaviors = this._behaviors;
	    // Use good ol' for as this is a very hot function
	    for (var i = 0, length = behaviors && behaviors.length; i < length; i++) {
	      triggerMethod.apply(behaviors[i], arguments);
	    }
	  }
	};

	// MixinOptions
	// - collectionEvents
	// - modelEvents

	var DelegateEntityEventsMixin = {
	  // Handle `modelEvents`, and `collectionEvents` configuration
	  _delegateEntityEvents: function _delegateEntityEvents(model, collection) {
	    this._undelegateEntityEvents(model, collection);

	    var modelEvents = _.result(this, 'modelEvents');
	    bindEvents.call(this, model, modelEvents);

	    var collectionEvents = _.result(this, 'collectionEvents');
	    bindEvents.call(this, collection, collectionEvents);
	  },
	  _undelegateEntityEvents: function _undelegateEntityEvents(model, collection) {
	    var modelEvents = _.result(this, 'modelEvents');
	    unbindEvents.call(this, model, modelEvents);

	    var collectionEvents = _.result(this, 'collectionEvents');
	    unbindEvents.call(this, collection, collectionEvents);
	  }
	};

	// Borrow event splitter from Backbone
	var delegateEventSplitter = /^(\S+)\s*(.*)$/;

	function uniqueName(eventName, selector) {
	  return [eventName + _.uniqueId('.evt'), selector].join(' ');
	}

	// Set event name to be namespaced using a unique index
	// to generate a non colliding event namespace
	// http://api.jquery.com/event.namespace/
	var getUniqueEventName = function getUniqueEventName(eventName) {
	  var match = eventName.match(delegateEventSplitter);
	  return uniqueName(match[1], match[2]);
	};

	// Internal method to create an event handler for a given `triggerDef` like
	// 'click:foo'
	function buildViewTrigger(view, triggerDef) {
	  if (_.isString(triggerDef)) {
	    triggerDef = { event: triggerDef };
	  }

	  var eventName = triggerDef.event;
	  var shouldPreventDefault = triggerDef.preventDefault !== false;
	  var shouldStopPropagation = triggerDef.stopPropagation !== false;

	  return function (e) {
	    if (shouldPreventDefault) {
	      e.preventDefault();
	    }

	    if (shouldStopPropagation) {
	      e.stopPropagation();
	    }

	    view.triggerMethod(eventName, view);
	  };
	}

	var TriggersMixin = {

	  // Configure `triggers` to forward DOM events to view
	  // events. `triggers: {"click .foo": "do:foo"}`
	  _getViewTriggers: function _getViewTriggers(view, triggers) {
	    // Configure the triggers, prevent default
	    // action and stop propagation of DOM events
	    return _.reduce(triggers, function (events, value, key) {
	      key = getUniqueEventName(key);
	      events[key] = buildViewTrigger(view, value);
	      return events;
	    }, {});
	  }
	};

	// allows for the use of the @ui. syntax within
	// a given key for triggers and events
	// swaps the @ui with the associated selector.
	// Returns a new, non-mutated, parsed events hash.
	var _normalizeUIKeys = function _normalizeUIKeys(hash, ui) {
	  return _.reduce(hash, function (memo, val, key) {
	    var normalizedKey = _normalizeUIString(key, ui);
	    memo[normalizedKey] = val;
	    return memo;
	  }, {});
	};

	// utility method for parsing @ui. syntax strings
	// into associated selector
	var _normalizeUIString = function _normalizeUIString(uiString, ui) {
	  return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function (r) {
	    return ui[r.slice(4)];
	  });
	};

	// allows for the use of the @ui. syntax within
	// a given value for regions
	// swaps the @ui with the associated selector
	var _normalizeUIValues = function _normalizeUIValues(hash, ui, properties) {
	  _.each(hash, function (val, key) {
	    if (_.isString(val)) {
	      hash[key] = _normalizeUIString(val, ui);
	    } else if (_.isObject(val) && _.isArray(properties)) {
	      _.extend(val, _normalizeUIValues(_.pick(val, properties), ui));
	      /* Value is an object, and we got an array of embedded property names to normalize. */
	      _.each(properties, function (property) {
	        var propertyVal = val[property];
	        if (_.isString(propertyVal)) {
	          val[property] = _normalizeUIString(propertyVal, ui);
	        }
	      });
	    }
	  });
	  return hash;
	};

	var UIMixin = {

	  // normalize the keys of passed hash with the views `ui` selectors.
	  // `{"@ui.foo": "bar"}`
	  normalizeUIKeys: function normalizeUIKeys(hash) {
	    var uiBindings = this._getUIBindings();
	    return _normalizeUIKeys(hash, uiBindings);
	  },


	  // normalize the passed string with the views `ui` selectors.
	  // `"@ui.bar"`
	  normalizeUIString: function normalizeUIString(uiString) {
	    var uiBindings = this._getUIBindings();
	    return _normalizeUIString(uiString, uiBindings);
	  },


	  // normalize the values of passed hash with the views `ui` selectors.
	  // `{foo: "@ui.bar"}`
	  normalizeUIValues: function normalizeUIValues(hash, properties) {
	    var uiBindings = this._getUIBindings();
	    return _normalizeUIValues(hash, uiBindings, properties);
	  },
	  _getUIBindings: function _getUIBindings() {
	    var uiBindings = _.result(this, '_uiBindings');
	    var ui = _.result(this, 'ui');
	    return uiBindings || ui;
	  },


	  // This method binds the elements specified in the "ui" hash inside the view's code with
	  // the associated jQuery selectors.
	  _bindUIElements: function _bindUIElements() {
	    var _this = this;

	    if (!this.ui) {
	      return;
	    }

	    // store the ui hash in _uiBindings so they can be reset later
	    // and so re-rendering the view will be able to find the bindings
	    if (!this._uiBindings) {
	      this._uiBindings = this.ui;
	    }

	    // get the bindings result, as a function or otherwise
	    var bindings = _.result(this, '_uiBindings');

	    // empty the ui so we don't have anything to start with
	    this._ui = {};

	    // bind each of the selectors
	    _.each(bindings, function (selector, key) {
	      _this._ui[key] = _this.$(selector);
	    });

	    this.ui = this._ui;
	  },
	  _unbindUIElements: function _unbindUIElements() {
	    var _this2 = this;

	    if (!this.ui || !this._uiBindings) {
	      return;
	    }

	    // delete all of the existing ui bindings
	    _.each(this.ui, function ($el, name) {
	      delete _this2.ui[name];
	    });

	    // reset the ui element to the original bindings configuration
	    this.ui = this._uiBindings;
	    delete this._uiBindings;
	    delete this._ui;
	  },
	  _getUI: function _getUI(name) {
	    return this._ui[name];
	  }
	};

	// MixinOptions
	// - behaviors
	// - childViewEventPrefix
	// - childViewEvents
	// - childViewTriggers
	// - collectionEvents
	// - modelEvents
	// - triggers
	// - ui


	var ViewMixin = {
	  supportsRenderLifecycle: true,
	  supportsDestroyLifecycle: true,

	  _isDestroyed: false,

	  isDestroyed: function isDestroyed() {
	    return !!this._isDestroyed;
	  },


	  _isRendered: false,

	  isRendered: function isRendered() {
	    return !!this._isRendered;
	  },


	  _isAttached: false,

	  isAttached: function isAttached() {
	    return !!this._isAttached;
	  },


	  // Overriding Backbone.View's `delegateEvents` to handle
	  // `events` and `triggers`
	  delegateEvents: function delegateEvents(eventsArg) {

	    this._proxyBehaviorViewProperties();
	    this._buildEventProxies();

	    var viewEvents = this._getEvents(eventsArg);

	    if (typeof eventsArg === 'undefined') {
	      this.events = viewEvents;
	    }

	    var combinedEvents = _.extend({}, this._getBehaviorEvents(), viewEvents, this._getBehaviorTriggers(), this.getTriggers());

	    Backbone.View.prototype.delegateEvents.call(this, combinedEvents);

	    return this;
	  },
	  _getEvents: function _getEvents(eventsArg) {
	    var events = eventsArg || this.events;

	    if (_.isFunction(events)) {
	      return this.normalizeUIKeys(events.call(this));
	    }

	    return this.normalizeUIKeys(events);
	  },


	  // Configure `triggers` to forward DOM events to view
	  // events. `triggers: {"click .foo": "do:foo"}`
	  getTriggers: function getTriggers() {
	    if (!this.triggers) {
	      return;
	    }

	    // Allow `triggers` to be configured as a function
	    var triggers = this.normalizeUIKeys(_.result(this, 'triggers'));

	    // Configure the triggers, prevent default
	    // action and stop propagation of DOM events
	    return this._getViewTriggers(this, triggers);
	  },


	  // Handle `modelEvents`, and `collectionEvents` configuration
	  delegateEntityEvents: function delegateEntityEvents() {
	    this._delegateEntityEvents(this.model, this.collection);

	    // bind each behaviors model and collection events
	    this._delegateBehaviorEntityEvents();

	    return this;
	  },


	  // Handle unbinding `modelEvents`, and `collectionEvents` configuration
	  undelegateEntityEvents: function undelegateEntityEvents() {
	    this._undelegateEntityEvents(this.model, this.collection);

	    // unbind each behaviors model and collection events
	    this._undelegateBehaviorEntityEvents();

	    return this;
	  },


	  // Internal helper method to verify whether the view hasn't been destroyed
	  _ensureViewIsIntact: function _ensureViewIsIntact() {
	    if (this._isDestroyed) {
	      throw new MarionetteError({
	        name: 'ViewDestroyedError',
	        message: 'View (cid: "' + this.cid + '") has already been destroyed and cannot be used.'
	      });
	    }
	  },


	  // Handle destroying the view and its children.
	  destroy: function destroy() {
	    if (this._isDestroyed) {
	      return this;
	    }
	    var shouldTriggerDetach = !!this._isAttached;

	    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
	      args[_key] = arguments[_key];
	    }

	    this.triggerMethod.apply(this, ['before:destroy', this].concat(args));
	    if (shouldTriggerDetach) {
	      this.triggerMethod('before:detach', this);
	    }

	    // unbind UI elements
	    this.unbindUIElements();

	    // remove the view from the DOM
	    // https://github.com/jashkenas/backbone/blob/1.2.3/backbone.js#L1235
	    this._removeElement();

	    if (shouldTriggerDetach) {
	      this._isAttached = false;
	      this.triggerMethod('detach', this);
	    }

	    // remove children after the remove to prevent extra paints
	    this._removeChildren();

	    this._destroyBehaviors(args);

	    this._isDestroyed = true;
	    this._isRendered = false;
	    this.triggerMethod.apply(this, ['destroy', this].concat(args));

	    this.stopListening();

	    return this;
	  },
	  bindUIElements: function bindUIElements() {
	    this._bindUIElements();
	    this._bindBehaviorUIElements();

	    return this;
	  },


	  // This method unbinds the elements specified in the "ui" hash
	  unbindUIElements: function unbindUIElements() {
	    this._unbindUIElements();
	    this._unbindBehaviorUIElements();

	    return this;
	  },
	  getUI: function getUI(name) {
	    this._ensureViewIsIntact();
	    return this._getUI(name);
	  },


	  // used as the prefix for child view events
	  // that are forwarded through the layoutview
	  childViewEventPrefix: 'childview',

	  // import the `triggerMethod` to trigger events with corresponding
	  // methods if the method exists
	  triggerMethod: function triggerMethod$$() {
	    var ret = triggerMethod.apply(this, arguments);

	    this._triggerEventOnBehaviors.apply(this, arguments);
	    this._triggerEventOnParentLayout.apply(this, arguments);

	    return ret;
	  },


	  // Cache `childViewEvents` and `childViewTriggers`
	  _buildEventProxies: function _buildEventProxies() {
	    this._childViewEvents = _.result(this, 'childViewEvents');
	    this._childViewTriggers = _.result(this, 'childViewTriggers');
	  },
	  _triggerEventOnParentLayout: function _triggerEventOnParentLayout() {
	    var layoutView = this._parentView();
	    if (!layoutView) {
	      return;
	    }

	    layoutView._childViewEventHandler.apply(layoutView, arguments);
	  },


	  // Walk the _parent tree until we find a view (if one exists).
	  // Returns the parent view hierarchically closest to this view.
	  _parentView: function _parentView() {
	    var parent = this._parent;

	    while (parent) {
	      if (parent instanceof View) {
	        return parent;
	      }
	      parent = parent._parent;
	    }
	  },
	  _childViewEventHandler: function _childViewEventHandler(eventName) {
	    var childViewEvents = this.normalizeMethods(this._childViewEvents);

	    // call collectionView childViewEvent if defined

	    for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
	      args[_key2 - 1] = arguments[_key2];
	    }

	    if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) {
	      childViewEvents[eventName].apply(this, args);
	    }

	    // use the parent view's proxyEvent handlers
	    var childViewTriggers = this._childViewTriggers;

	    // Call the event with the proxy name on the parent layout
	    if (childViewTriggers && _.isString(childViewTriggers[eventName])) {
	      this.triggerMethod.apply(this, [childViewTriggers[eventName]].concat(args));
	    }

	    var prefix = _.result(this, 'childViewEventPrefix');

	    if (prefix !== false) {
	      var childEventName = prefix + ':' + eventName;

	      this.triggerMethod.apply(this, [childEventName].concat(args));
	    }
	  }
	};

	_.extend(ViewMixin, BehaviorsMixin, CommonMixin, DelegateEntityEventsMixin, TriggersMixin, UIMixin);

	function destroyBackboneView(view) {
	  if (!view.supportsDestroyLifecycle) {
	    triggerMethodOn(view, 'before:destroy', view);
	  }

	  var shouldTriggerDetach = !!view._isAttached;

	  if (shouldTriggerDetach) {
	    triggerMethodOn(view, 'before:detach', view);
	  }

	  view.remove();

	  if (shouldTriggerDetach) {
	    view._isAttached = false;
	    triggerMethodOn(view, 'detach', view);
	  }

	  view._isDestroyed = true;

	  if (!view.supportsDestroyLifecycle) {
	    triggerMethodOn(view, 'destroy', view);
	  }
	}

	var ClassOptions$2 = ['allowMissingEl', 'parentEl', 'replaceElement'];

	var Region = MarionetteObject.extend({
	  cidPrefix: 'mnr',
	  replaceElement: false,
	  _isReplaced: false,

	  constructor: function constructor(options) {
	    this._setOptions(options);

	    this.mergeOptions(options, ClassOptions$2);

	    // getOption necessary because options.el may be passed as undefined
	    this._initEl = this.el = this.getOption('el');

	    // Handle when this.el is passed in as a $ wrapped element.
	    this.el = this.el instanceof Backbone.$ ? this.el[0] : this.el;

	    if (!this.el) {
	      throw new MarionetteError({
	        name: 'NoElError',
	        message: 'An "el" must be specified for a region.'
	      });
	    }

	    this.$el = this.getEl(this.el);
	    MarionetteObject.call(this, options);
	  },


	  // Displays a backbone view instance inside of the region. Handles calling the `render`
	  // method for you. Reads content directly from the `el` attribute. The `preventDestroy`
	  // option can be used to prevent a view from the old view being destroyed on show.
	  show: function show(view, options) {
	    if (!this._ensureElement(options)) {
	      return;
	    }
	    this._ensureView(view);
	    if (view === this.currentView) {
	      return this;
	    }

	    this.triggerMethod('before:show', this, view, options);

	    monitorViewEvents(view);

	    this.empty(options);

	    // We need to listen for if a view is destroyed in a way other than through the region.
	    // If this happens we need to remove the reference to the currentView since once a view
	    // has been destroyed we can not reuse it.
	    view.on('destroy', this._empty, this);

	    // Make this region the view's parent.
	    // It's important that this parent binding happens before rendering so that any events
	    // the child may trigger during render can also be triggered on the child's ancestor views.
	    view._parent = this;

	    this._renderView(view);

	    this._attachView(view, options);

	    this.triggerMethod('show', this, view, options);
	    return this;
	  },
	  _renderView: function _renderView(view) {
	    if (view._isRendered) {
	      return;
	    }

	    if (!view.supportsRenderLifecycle) {
	      triggerMethodOn(view, 'before:render', view);
	    }

	    view.render();

	    if (!view.supportsRenderLifecycle) {
	      view._isRendered = true;
	      triggerMethodOn(view, 'render', view);
	    }
	  },
	  _attachView: function _attachView(view) {
	    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

	    var shouldTriggerAttach = !view._isAttached && isNodeAttached(this.el);
	    var shouldReplaceEl = typeof options.replaceElement === 'undefined' ? !!_.result(this, 'replaceElement') : !!options.replaceElement;

	    if (shouldTriggerAttach) {
	      triggerMethodOn(view, 'before:attach', view);
	    }

	    if (shouldReplaceEl) {
	      this._replaceEl(view);
	    } else {
	      this.attachHtml(view);
	    }

	    if (shouldTriggerAttach) {
	      view._isAttached = true;
	      triggerMethodOn(view, 'attach', view);
	    }

	    this.currentView = view;
	  },
	  _ensureElement: function _ensureElement() {
	    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

	    if (!_.isObject(this.el)) {
	      this.$el = this.getEl(this.el);
	      this.el = this.$el[0];
	    }

	    if (!this.$el || this.$el.length === 0) {
	      var allowMissingEl = typeof options.allowMissingEl === 'undefined' ? !!_.result(this, 'allowMissingEl') : !!options.allowMissingEl;

	      if (allowMissingEl) {
	        return false;
	      } else {
	        throw new MarionetteError('An "el" must exist in DOM for this region ' + this.cid);
	      }
	    }
	    return true;
	  },
	  _ensureView: function _ensureView(view) {
	    if (!view) {
	      throw new MarionetteError({
	        name: 'ViewNotValid',
	        message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.'
	      });
	    }

	    if (view._isDestroyed) {
	      throw new MarionetteError({
	        name: 'ViewDestroyedError',
	        message: 'View (cid: "' + view.cid + '") has already been destroyed and cannot be used.'
	      });
	    }
	  },


	  // Override this method to change how the region finds the DOM element that it manages. Return
	  // a jQuery selector object scoped to a provided parent el or the document if none exists.
	  getEl: function getEl(el) {
	    return Backbone.$(el, _.result(this, 'parentEl'));
	  },
	  _replaceEl: function _replaceEl(view) {
	    // always restore the el to ensure the regions el is present before replacing
	    this._restoreEl();

	    var parent = this.el.parentNode;

	    parent.replaceChild(view.el, this.el);
	    this._isReplaced = true;
	  },


	  // Restore the region's element in the DOM.
	  _restoreEl: function _restoreEl() {
	    // There is nothing to replace
	    if (!this._isReplaced) {
	      return;
	    }

	    var view = this.currentView;

	    if (!view) {
	      return;
	    }

	    var parent = view.el.parentNode;

	    if (!parent) {
	      return;
	    }

	    parent.replaceChild(this.el, view.el);
	    this._isReplaced = false;
	  },


	  // Check to see if the region's el was replaced.
	  isReplaced: function isReplaced() {
	    return !!this._isReplaced;
	  },


	  // Override this method to change how the new view is appended to the `$el` that the
	  // region is managing
	  attachHtml: function attachHtml(view) {
	    this.el.appendChild(view.el);
	  },


	  // Destroy the current view, if there is one. If there is no current view, it does
	  // nothing and returns immediately.
	  empty: function empty() {
	    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { allowMissingEl: true };

	    var view = this.currentView;

	    // If there is no view in the region we should only detach current html
	    if (!view) {
	      if (this._ensureElement(options)) {
	        this.detachHtml();
	      }
	      return this;
	    }

	    var shouldDestroy = !options.preventDestroy;

	    if (!shouldDestroy) {
	      deprecate('The preventDestroy option is deprecated. Use Region#detachView');
	    }

	    this._empty(view, shouldDestroy);
	    return this;
	  },
	  _empty: function _empty(view, shouldDestroy) {
	    view.off('destroy', this._empty, this);
	    this.triggerMethod('before:empty', this, view);

	    this._restoreEl();

	    delete this.currentView;

	    if (!view._isDestroyed) {
	      this._removeView(view, shouldDestroy);
	      delete view._parent;
	    }

	    this.triggerMethod('empty', this, view);
	  },
	  _removeView: function _removeView(view, shouldDestroy) {
	    if (!shouldDestroy) {
	      this._detachView(view);
	      return;
	    }

	    if (view.destroy) {
	      view.destroy();
	    } else {
	      destroyBackboneView(view);
	    }
	  },
	  detachView: function detachView() {
	    var view = this.currentView;

	    if (!view) {
	      return;
	    }

	    this._empty(view);

	    return view;
	  },
	  _detachView: function _detachView(view) {
	    var shouldTriggerDetach = !!view._isAttached;
	    if (shouldTriggerDetach) {
	      triggerMethodOn(view, 'before:detach', view);
	    }

	    this.detachHtml();

	    if (shouldTriggerDetach) {
	      view._isAttached = false;
	      triggerMethodOn(view, 'detach', view);
	    }
	  },


	  // Override this method to change how the region detaches current content
	  detachHtml: function detachHtml() {
	    this.$el.contents().detach();
	  },


	  // Checks whether a view is currently present within the region. Returns `true` if there is
	  // and `false` if no view is present.
	  hasView: function hasView() {
	    return !!this.currentView;
	  },


	  // Reset the region by destroying any existing view and clearing out the cached `$el`.
	  // The next time a view is shown via this region, the region will re-query the DOM for
	  // the region's `el`.
	  reset: function reset(options) {
	    this.empty(options);

	    if (this.$el) {
	      this.el = this._initEl;
	    }

	    delete this.$el;
	    return this;
	  },
	  destroy: function destroy(options) {
	    this.reset(options);
	    return MarionetteObject.prototype.destroy.apply(this, arguments);
	  }
	});

	// return the region instance from the definition
	function buildRegion (definition, defaults) {
	  if (definition instanceof Region) {
	    return definition;
	  }

	  return buildRegionFromDefinition(definition, defaults);
	}

	function buildRegionFromDefinition(definition, defaults) {
	  var opts = _.extend({}, defaults);

	  if (_.isString(definition)) {
	    _.extend(opts, { el: definition });

	    return buildRegionFromObject(opts);
	  }

	  if (_.isFunction(definition)) {
	    _.extend(opts, { regionClass: definition });

	    return buildRegionFromObject(opts);
	  }

	  if (_.isObject(definition)) {
	    if (definition.selector) {
	      deprecate('The selector option on a Region definition object is deprecated. Use el to pass a selector string');
	    }

	    _.extend(opts, { el: definition.selector }, definition);

	    return buildRegionFromObject(opts);
	  }

	  throw new MarionetteError({
	    message: 'Improper region configuration type.',
	    url: 'marionette.region.html#region-configuration-types'
	  });
	}

	function buildRegionFromObject(definition) {
	  var RegionClass = definition.regionClass;

	  var options = _.omit(definition, 'regionClass');

	  return new RegionClass(options);
	}

	// MixinOptions
	// - regions
	// - regionClass

	var RegionsMixin = {
	  regionClass: Region,

	  // Internal method to initialize the regions that have been defined in a
	  // `regions` attribute on this View.
	  _initRegions: function _initRegions() {

	    // init regions hash
	    this.regions = this.regions || {};
	    this._regions = {};

	    this.addRegions(_.result(this, 'regions'));
	  },


	  // Internal method to re-initialize all of the regions by updating
	  // the `el` that they point to
	  _reInitRegions: function _reInitRegions() {
	    _invoke(this._regions, 'reset');
	  },


	  // Add a single region, by name, to the View
	  addRegion: function addRegion(name, definition) {
	    var regions = {};
	    regions[name] = definition;
	    return this.addRegions(regions)[name];
	  },


	  // Add multiple regions as a {name: definition, name2: def2} object literal
	  addRegions: function addRegions(regions) {
	    // If there's nothing to add, stop here.
	    if (_.isEmpty(regions)) {
	      return;
	    }

	    // Normalize region selectors hash to allow
	    // a user to use the @ui. syntax.
	    regions = this.normalizeUIValues(regions, ['selector', 'el']);

	    // Add the regions definitions to the regions property
	    this.regions = _.extend({}, this.regions, regions);

	    return this._addRegions(regions);
	  },


	  // internal method to build and add regions
	  _addRegions: function _addRegions(regionDefinitions) {
	    var _this = this;

	    var defaults = {
	      regionClass: this.regionClass,
	      parentEl: _.partial(_.result, this, 'el')
	    };

	    return _.reduce(regionDefinitions, function (regions, definition, name) {
	      regions[name] = buildRegion(definition, defaults);
	      _this._addRegion(regions[name], name);
	      return regions;
	    }, {});
	  },
	  _addRegion: function _addRegion(region, name) {
	    this.triggerMethod('before:add:region', this, name, region);

	    region._parent = this;

	    this._regions[name] = region;

	    this.triggerMethod('add:region', this, name, region);
	  },


	  // Remove a single region from the View, by name
	  removeRegion: function removeRegion(name) {
	    var region = this._regions[name];

	    this._removeRegion(region, name);

	    return region;
	  },


	  // Remove all regions from the View
	  removeRegions: function removeRegions() {
	    var regions = this.getRegions();

	    _.each(this._regions, _.bind(this._removeRegion, this));

	    return regions;
	  },
	  _removeRegion: function _removeRegion(region, name) {
	    this.triggerMethod('before:remove:region', this, name, region);

	    region.destroy();

	    delete this.regions[name];
	    delete this._regions[name];

	    this.triggerMethod('remove:region', this, name, region);
	  },


	  // Empty all regions in the region manager, but
	  // leave them attached
	  emptyRegions: function emptyRegions() {
	    var regions = this.getRegions();
	    _invoke(regions, 'empty');
	    return regions;
	  },


	  // Checks to see if view contains region
	  // Accepts the region name
	  // hasRegion('main')
	  hasRegion: function hasRegion(name) {
	    return !!this.getRegion(name);
	  },


	  // Provides access to regions
	  // Accepts the region name
	  // getRegion('main')
	  getRegion: function getRegion(name) {
	    return this._regions[name];
	  },


	  // Get all regions
	  getRegions: function getRegions() {
	    return _.clone(this._regions);
	  },
	  showChildView: function showChildView(name, view) {
	    var region = this.getRegion(name);

	    for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
	      args[_key - 2] = arguments[_key];
	    }

	    return region.show.apply(region, [view].concat(args));
	  },
	  detachChildView: function detachChildView(name) {
	    return this.getRegion(name).detachView();
	  },
	  getChildView: function getChildView(name) {
	    return this.getRegion(name).currentView;
	  }
	};

	// Render a template with data by passing in the template
	// selector and the data to render.
	var Renderer = {

	  // Render a template with data. The `template` parameter is
	  // passed to the `TemplateCache` object to retrieve the
	  // template function. Override this method to provide your own
	  // custom rendering and template handling for all of Marionette.
	  render: function render(template, data) {
	    if (!template) {
	      throw new MarionetteError({
	        name: 'TemplateNotFoundError',
	        message: 'Cannot render the template since its false, null or undefined.'
	      });
	    }

	    var templateFunc = _.isFunction(template) ? template : TemplateCache.get(template);

	    return templateFunc(data);
	  }
	};

	var ClassOptions$1 = ['behaviors', 'childViewEventPrefix', 'childViewEvents', 'childViewTriggers', 'collectionEvents', 'events', 'modelEvents', 'regionClass', 'regions', 'template', 'templateContext', 'triggers', 'ui'];

	// The standard view. Includes view events, automatic rendering
	// of Underscore templates, nested views, and more.
	var View = Backbone.View.extend({
	  constructor: function constructor(options) {
	    this.render = _.bind(this.render, this);

	    this._setOptions(options);

	    this.mergeOptions(options, ClassOptions$1);

	    monitorViewEvents(this);

	    this._initBehaviors();
	    this._initRegions();

	    var args = Array.prototype.slice.call(arguments);
	    args[0] = this.options;
	    Backbone.View.prototype.constructor.apply(this, args);

	    this.delegateEntityEvents();
	  },


	  // Serialize the view's model *or* collection, if
	  // it exists, for the template
	  serializeData: function serializeData() {
	    if (!this.model && !this.collection) {
	      return {};
	    }

	    // If we have a model, we serialize that
	    if (this.model) {
	      return this.serializeModel();
	    }

	    // Otherwise, we serialize the collection,
	    // making it available under the `items` property
	    return {
	      items: this.serializeCollection()
	    };
	  },


	  // Prepares the special `model` property of a view
	  // for being displayed in the template. By default
	  // we simply clone the attributes. Override this if
	  // you need a custom transformation for your view's model
	  serializeModel: function serializeModel() {
	    if (!this.model) {
	      return {};
	    }
	    return _.clone(this.model.attributes);
	  },


	  // Serialize a collection by cloning each of
	  // its model's attributes
	  serializeCollection: function serializeCollection() {
	    if (!this.collection) {
	      return {};
	    }
	    return this.collection.map(function (model) {
	      return _.clone(model.attributes);
	    });
	  },


	  // Overriding Backbone.View's `setElement` to handle
	  // if an el was previously defined. If so, the view might be
	  // rendered or attached on setElement.
	  setElement: function setElement() {
	    var hasEl = !!this.el;

	    Backbone.View.prototype.setElement.apply(this, arguments);

	    if (hasEl) {
	      this._isRendered = !!this.$el.length;
	      this._isAttached = isNodeAttached(this.el);
	    }

	    if (this._isRendered) {
	      this.bindUIElements();
	    }

	    return this;
	  },


	  // Render the view, defaulting to underscore.js templates.
	  // You can override this in your view definition to provide
	  // a very specific rendering for your view. In general, though,
	  // you should override the `Marionette.Renderer` object to
	  // change how Marionette renders views.
	  // Subsequent renders after the first will re-render all nested
	  // views.
	  render: function render() {
	    this._ensureViewIsIntact();

	    this.triggerMethod('before:render', this);

	    // If this is not the first render call, then we need to
	    // re-initialize the `el` for each region
	    if (this._isRendered) {
	      this._reInitRegions();
	    }

	    this._renderTemplate();
	    this.bindUIElements();

	    this._isRendered = true;
	    this.triggerMethod('render', this);

	    return this;
	  },


	  // Internal method to render the template with the serialized data
	  // and template context via the `Marionette.Renderer` object.
	  _renderTemplate: function _renderTemplate() {
	    var template = this.getTemplate();

	    // Allow template-less views
	    if (template === false) {
	      return;
	    }

	    // Add in entity data and template context
	    var data = this.mixinTemplateContext(this.serializeData());

	    // Render and add to el
	    var html = Renderer.render(template, data, this);
	    this.attachElContent(html);
	  },


	  // Get the template for this view
	  // instance. You can set a `template` attribute in the view
	  // definition or pass a `template: "whatever"` parameter in
	  // to the constructor options.
	  getTemplate: function getTemplate() {
	    return this.template;
	  },


	  // Mix in template context methods. Looks for a
	  // `templateContext` attribute, which can either be an
	  // object literal, or a function that returns an object
	  // literal. All methods and attributes from this object
	  // are copies to the object passed in.
	  mixinTemplateContext: function mixinTemplateContext() {
	    var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

	    var templateContext = _.result(this, 'templateContext');
	    return _.extend(target, templateContext);
	  },


	  // Attaches the content of a given view.
	  // This method can be overridden to optimize rendering,
	  // or to render in a non standard way.
	  //
	  // For example, using `innerHTML` instead of `$el.html`
	  //
	  // ```js
	  // attachElContent(html) {
	  //   this.el.innerHTML = html;
	  //   return this;
	  // }
	  // ```
	  attachElContent: function attachElContent(html) {
	    this.$el.html(html);

	    return this;
	  },


	  // called by ViewMixin destroy
	  _removeChildren: function _removeChildren() {
	    this.removeRegions();
	  },
	  _getImmediateChildren: function _getImmediateChildren() {
	    return _.chain(this.getRegions()).map('currentView').compact().value();
	  }
	});

	_.extend(View.prototype, ViewMixin, RegionsMixin);

	var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest', 'last', 'without', 'isEmpty', 'pluck', 'reduce'];

	var emulateCollection = function emulateCollection(object, listProperty) {
	  _.each(methods, function (method) {
	    object[method] = function () {
	      var list = _.values(_.result(this, listProperty));
	      var args = [list].concat(_.toArray(arguments));
	      return _[method].apply(_, args);
	    };
	  });
	};

	// Provide a container to store, retrieve and
	// shut down child views.
	var Container = function Container(views) {
	  this._views = {};
	  this._indexByModel = {};
	  this._indexByCustom = {};
	  this._updateLength();

	  _.each(views, _.bind(this.add, this));
	};

	emulateCollection(Container.prototype, '_views');

	// Container Methods
	// -----------------

	_.extend(Container.prototype, {

	  // Add a view to this container. Stores the view
	  // by `cid` and makes it searchable by the model
	  // cid (and model itself). Optionally specify
	  // a custom key to store an retrieve the view.
	  add: function add(view, customIndex) {
	    return this._add(view, customIndex)._updateLength();
	  },


	  // To be used when avoiding call _updateLength
	  // When you are done adding all your new views
	  // call _updateLength
	  _add: function _add(view, customIndex) {
	    var viewCid = view.cid;

	    // store the view
	    this._views[viewCid] = view;

	    // index it by model
	    if (view.model) {
	      this._indexByModel[view.model.cid] = viewCid;
	    }

	    // index by custom
	    if (customIndex) {
	      this._indexByCustom[customIndex] = viewCid;
	    }

	    return this;
	  },


	  // Find a view by the model that was attached to
	  // it. Uses the model's `cid` to find it.
	  findByModel: function findByModel(model) {
	    return this.findByModelCid(model.cid);
	  },


	  // Find a view by the `cid` of the model that was attached to
	  // it. Uses the model's `cid` to find the view `cid` and
	  // retrieve the view using it.
	  findByModelCid: function findByModelCid(modelCid) {
	    var viewCid = this._indexByModel[modelCid];
	    return this.findByCid(viewCid);
	  },


	  // Find a view by a custom indexer.
	  findByCustom: function findByCustom(index) {
	    var viewCid = this._indexByCustom[index];
	    return this.findByCid(viewCid);
	  },


	  // Find by index. This is not guaranteed to be a
	  // stable index.
	  findByIndex: function findByIndex(index) {
	    return _.values(this._views)[index];
	  },


	  // retrieve a view by its `cid` directly
	  findByCid: function findByCid(cid) {
	    return this._views[cid];
	  },


	  // Remove a view
	  remove: function remove(view) {
	    return this._remove(view)._updateLength();
	  },


	  // To be used when avoiding call _updateLength
	  // When you are done adding all your new views
	  // call _updateLength
	  _remove: function _remove(view) {
	    var viewCid = view.cid;

	    // delete model index
	    if (view.model) {
	      delete this._indexByModel[view.model.cid];
	    }

	    // delete custom index
	    _.some(this._indexByCustom, _.bind(function (cid, key) {
	      if (cid === viewCid) {
	        delete this._indexByCustom[key];
	        return true;
	      }
	    }, this));

	    // remove the view from the container
	    delete this._views[viewCid];

	    return this;
	  },


	  // Update the `.length` attribute on this container
	  _updateLength: function _updateLength() {
	    this.length = _.size(this._views);

	    return this;
	  }
	});

	var ClassOptions$3 = ['behaviors', 'childView', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'events', 'filter', 'emptyView', 'emptyViewOptions', 'modelEvents', 'reorderOnSort', 'sort', 'triggers', 'ui', 'viewComparator'];

	// A view that iterates over a Backbone.Collection
	// and renders an individual child view for each model.
	var CollectionView = Backbone.View.extend({

	  // flag for maintaining the sorted order of the collection
	  sort: true,

	  // constructor
	  // option to pass `{sort: false}` to prevent the `CollectionView` from
	  // maintaining the sorted order of the collection.
	  // This will fallback onto appending childView's to the end.
	  //
	  // option to pass `{viewComparator: compFunction()}` to allow the `CollectionView`
	  // to use a custom sort order for the collection.
	  constructor: function constructor(options) {
	    this.render = _.bind(this.render, this);

	    this._setOptions(options);

	    this.mergeOptions(options, ClassOptions$3);

	    monitorViewEvents(this);

	    this._initBehaviors();
	    this.once('render', this._initialEvents);
	    this._initChildViewStorage();
	    this._bufferedChildren = [];

	    var args = Array.prototype.slice.call(arguments);
	    args[0] = this.options;
	    Backbone.View.prototype.constructor.apply(this, args);

	    this.delegateEntityEvents();
	  },


	  // Instead of inserting elements one by one into the page, it's much more performant to insert
	  // elements into a document fragment and then insert that document fragment into the page
	  _startBuffering: function _startBuffering() {
	    this._isBuffering = true;
	  },
	  _endBuffering: function _endBuffering() {
	    var shouldTriggerAttach = !!this._isAttached;
	    var triggerOnChildren = shouldTriggerAttach ? this._getImmediateChildren() : [];

	    this._isBuffering = false;

	    _.each(triggerOnChildren, function (child) {
	      triggerMethodOn(child, 'before:attach', child);
	    });

	    this.attachBuffer(this, this._createBuffer());

	    _.each(triggerOnChildren, function (child) {
	      child._isAttached = true;
	      triggerMethodOn(child, 'attach', child);
	    });

	    this._bufferedChildren = [];
	  },
	  _getImmediateChildren: function _getImmediateChildren() {
	    return _.values(this.children._views);
	  },


	  // Configured the initial events that the collection view binds to.
	  _initialEvents: function _initialEvents() {
	    if (this.collection) {
	      this.listenTo(this.collection, 'add', this._onCollectionAdd);
	      this.listenTo(this.collection, 'update', this._onCollectionUpdate);
	      this.listenTo(this.collection, 'reset', this.render);

	      if (this.sort) {
	        this.listenTo(this.collection, 'sort', this._sortViews);
	      }
	    }
	  },


	  // Handle a child added to the collection
	  _onCollectionAdd: function _onCollectionAdd(child, collection, opts) {
	    // `index` is present when adding with `at` since BB 1.2; indexOf fallback for < 1.2
	    var index = opts.at !== undefined && (opts.index || collection.indexOf(child));

	    // When filtered or when there is no initial index, calculate index.
	    if (this.filter || index === false) {
	      index = _.indexOf(this._filteredSortedModels(index), child);
	    }

	    if (this._shouldAddChild(child, index)) {
	      this._destroyEmptyView();
	      this._addChild(child, index);
	    }
	  },


	  // Handle collection update model removals
	  _onCollectionUpdate: function _onCollectionUpdate(collection, options) {
	    var changes = options.changes;
	    this._removeChildModels(changes.removed);
	  },


	  // Remove the child views and destroy them.
	  // This function also updates the indices of later views
	  // in the collection in order to keep the children in sync with the collection.
	  // "models" is an array of models and the corresponding views
	  // will be removed and destroyed from the CollectionView
	  _removeChildModels: function _removeChildModels(models) {
	    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

	    var checkEmpty = _ref.checkEmpty;

	    var shouldCheckEmpty = checkEmpty !== false;

	    // Used to determine where to update the remaining
	    // sibling view indices after these views are removed.
	    var removedViews = this._getRemovedViews(models);

	    if (!removedViews.length) {
	      return;
	    }

	    this.children._updateLength();

	    // decrement the index of views after this one
	    this._updateIndices(removedViews, false);

	    if (shouldCheckEmpty) {
	      this._checkEmpty();
	    }
	  },


	  // Returns the views that will be used for re-indexing
	  // through CollectionView#_updateIndices.
	  _getRemovedViews: function _getRemovedViews(models) {
	    var _this = this;

	    // Returning a view means something was removed.
	    return _.reduce(models, function (removingViews, model) {
	      var view = _this.children.findByModel(model);

	      if (!view || view._isDestroyed) {
	        return removingViews;
	      }

	      _this._removeChildView(view);

	      removingViews.push(view);

	      return removingViews;
	    }, []);
	  },
	  _findGreatestIndexedView: function _findGreatestIndexedView(views) {

	    return _.reduce(views, function (greatestIndexedView, view) {
	      // Even if the index is `undefined`, a view will get returned.
	      if (!greatestIndexedView || greatestIndexedView._index < view._index) {
	        return view;
	      }

	      return greatestIndexedView;
	    }, undefined);
	  },
	  _removeChildView: function _removeChildView(view) {
	    this.triggerMethod('before:remove:child', this, view);

	    this.children._remove(view);
	    if (view.destroy) {
	      view.destroy();
	    } else {
	      destroyBackboneView(view);
	    }

	    delete view._parent;
	    this.stopListening(view);
	    this.triggerMethod('remove:child', this, view);
	  },


	  // Overriding Backbone.View's `setElement` to handle
	  // if an el was previously defined. If so, the view might be
	  // attached on setElement.
	  setElement: function setElement() {
	    var hasEl = !!this.el;

	    Backbone.View.prototype.setElement.apply(this, arguments);

	    if (hasEl) {
	      this._isAttached = isNodeAttached(this.el);
	    }

	    return this;
	  },


	  // Render children views. Override this method to provide your own implementation of a
	  // render function for the collection view.
	  render: function render() {
	    this._ensureViewIsIntact();
	    this.triggerMethod('before:render', this);
	    this._renderChildren();
	    this._isRendered = true;
	    this.triggerMethod('render', this);
	    return this;
	  },


	  // An efficient rendering used for filtering. Instead of modifying the whole DOM for the
	  // collection view, we are only adding or removing the related childrenViews.
	  setFilter: function setFilter(filter) {
	    var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

	    var preventRender = _ref2.preventRender;

	    var canBeRendered = this._isRendered && !this._isDestroyed;
	    var filterChanged = this.filter !== filter;
	    var shouldRender = canBeRendered && filterChanged && !preventRender;

	    if (shouldRender) {
	      var previousModels = this._filteredSortedModels();
	      this.filter = filter;
	      var models = this._filteredSortedModels();
	      this._applyModelDeltas(models, previousModels);
	    } else {
	      this.filter = filter;
	    }

	    return this;
	  },


	  // `removeFilter` is actually an alias for removing filters.
	  removeFilter: function removeFilter(options) {
	    return this.setFilter(null, options);
	  },


	  // Calculate and apply difference by cid between `models` and `previousModels`.
	  _applyModelDeltas: function _applyModelDeltas(models, previousModels) {
	    var _this2 = this;

	    var currentIds = {};
	    _.each(models, function (model, index) {
	      var addedChildNotExists = !_this2.children.findByModel(model);
	      if (addedChildNotExists) {
	        _this2._onCollectionAdd(model, _this2.collection, { at: index });
	      }
	      currentIds[model.cid] = true;
	    });

	    var removeModels = _.filter(previousModels, function (prevModel) {
	      return !currentIds[prevModel.cid] && _this2.children.findByModel(prevModel);
	    });

	    this._removeChildModels(removeModels);
	  },


	  // Reorder DOM after sorting. When your element's rendering do not use their index,
	  // you can pass reorderOnSort: true to only reorder the DOM after a sort instead of
	  // rendering all the collectionView.
	  reorder: function reorder() {
	    var _this3 = this;

	    var children = this.children;
	    var models = this._filteredSortedModels();

	    if (!models.length && this._showingEmptyView) {
	      return this;
	    }

	    var anyModelsAdded = _.some(models, function (model) {
	      return !children.findByModel(model);
	    });

	    // If there are any new models added due to filtering we need to add child views,
	    // so render as normal.
	    if (anyModelsAdded) {
	      this.render();
	    } else {
	      (function () {

	        var filteredOutModels = [];

	        // Get the DOM nodes in the same order as the models and
	        // find the model that were children before but aren't in this new order.
	        var elsToReorder = children.reduce(function (viewEls, view) {
	          var index = _.indexOf(models, view.model);

	          if (index === -1) {
	            filteredOutModels.push(view.model);
	            return viewEls;
	          }

	          view._index = index;

	          viewEls[index] = view.el;

	          return viewEls;
	        }, new Array(models.length));

	        _this3.triggerMethod('before:reorder', _this3);

	        // Since append moves elements that are already in the DOM, appending the elements
	        // will effectively reorder them.
	        _this3._appendReorderedChildren(elsToReorder);

	        // remove any views that have been filtered out
	        _this3._removeChildModels(filteredOutModels);

	        _this3.triggerMethod('reorder', _this3);
	      })();
	    }
	    return this;
	  },


	  // Render view after sorting. Override this method to change how the view renders
	  // after a `sort` on the collection.
	  resortView: function resortView() {
	    if (this.reorderOnSort) {
	      this.reorder();
	    } else {
	      this._renderChildren();
	    }
	    return this;
	  },


	  // Internal method. This checks for any changes in the order of the collection.
	  // If the index of any view doesn't match, it will render.
	  _sortViews: function _sortViews() {
	    var _this4 = this;

	    var models = this._filteredSortedModels();

	    // check for any changes in sort order of views
	    var orderChanged = _.find(models, function (item, index) {
	      var view = _this4.children.findByModel(item);
	      return !view || view._index !== index;
	    });

	    if (orderChanged) {
	      this.resortView();
	    }
	  },


	  // Internal reference to what index a `emptyView` is.
	  _emptyViewIndex: -1,

	  // Internal method. Separated so that CompositeView can append to the childViewContainer
	  // if necessary
	  _appendReorderedChildren: function _appendReorderedChildren(children) {
	    this.$el.append(children);
	  },


	  // Internal method. Separated so that CompositeView can have more control over events
	  // being triggered, around the rendering process
	  _renderChildren: function _renderChildren() {
	    if (this._isRendered) {
	      this._destroyEmptyView();
	      this._destroyChildren({ checkEmpty: false });
	    }

	    var models = this._filteredSortedModels();
	    if (this.isEmpty({ processedModels: models })) {
	      this._showEmptyView();
	    } else {
	      this.triggerMethod('before:render:children', this);
	      this._startBuffering();
	      this._showCollection(models);
	      this._endBuffering();
	      this.triggerMethod('render:children', this);
	    }
	  },
	  _createView: function _createView(model, index) {
	    var ChildView = this._getChildView(model);
	    var childViewOptions = this._getChildViewOptions(model, index);
	    var view = this.buildChildView(model, ChildView, childViewOptions);
	    return view;
	  },
	  _setupChildView: function _setupChildView(view, index) {
	    view._parent = this;

	    monitorViewEvents(view);

	    // set up the child view event forwarding
	    this._proxyChildEvents(view);

	    if (this.sort) {
	      view._index = index;
	    }
	  },


	  // Internal method to loop through collection and show each child view.
	  _showCollection: function _showCollection(models) {
	    _.each(models, _.bind(this._addChild, this));
	    this.children._updateLength();
	  },


	  // Allow the collection to be sorted by a custom view comparator
	  _filteredSortedModels: function _filteredSortedModels(addedAt) {
	    if (!this.collection || !this.collection.length) {
	      return [];
	    }

	    var viewComparator = this.getViewComparator();
	    var models = this.collection.models;
	    addedAt = Math.min(Math.max(addedAt, 0), models.length - 1);

	    if (viewComparator) {
	      var addedModel = void 0;
	      // Preserve `at` location, even for a sorted view
	      if (addedAt) {
	        addedModel = models[addedAt];
	        models = models.slice(0, addedAt).concat(models.slice(addedAt + 1));
	      }
	      models = this._sortModelsBy(models, viewComparator);
	      if (addedModel) {
	        models.splice(addedAt, 0, addedModel);
	      }
	    }

	    // Filter after sorting in case the filter uses the index
	    models = this._filterModels(models);

	    return models;
	  },
	  getViewComparator: function getViewComparator() {
	    return this.viewComparator;
	  },


	  // Filter an array of models, if a filter exists
	  _filterModels: function _filterModels(models) {
	    var _this5 = this;

	    if (this.filter) {
	      models = _.filter(models, function (model, index) {
	        return _this5._shouldAddChild(model, index);
	      });
	    }
	    return models;
	  },
	  _sortModelsBy: function _sortModelsBy(models, comparator) {
	    if (typeof comparator === 'string') {
	      return _.sortBy(models, function (model) {
	        return model.get(comparator);
	      });
	    } else if (comparator.length === 1) {
	      return _.sortBy(models, _.bind(comparator, this));
	    } else {
	      return models.sort(_.bind(comparator, this));
	    }
	  },


	  // Internal method to show an empty view in place of a collection of child views,
	  // when the collection is empty
	  _showEmptyView: function _showEmptyView() {
	    var EmptyView = this._getEmptyView();

	    if (EmptyView && !this._showingEmptyView) {
	      this._showingEmptyView = true;

	      var model = new Backbone.Model();
	      var emptyViewOptions = this.emptyViewOptions || this.childViewOptions;
	      if (_.isFunction(emptyViewOptions)) {
	        emptyViewOptions = emptyViewOptions.call(this, model, this._emptyViewIndex);
	      }

	      var view = this.buildChildView(model, EmptyView, emptyViewOptions);

	      this.triggerMethod('before:render:empty', this, view);
	      this.addChildView(view, 0);
	      this.triggerMethod('render:empty', this, view);
	    }
	  },


	  // Internal method to destroy an existing emptyView instance if one exists. Called when
	  // a collection view has been rendered empty, and then a child is added to the collection.
	  _destroyEmptyView: function _destroyEmptyView() {
	    if (this._showingEmptyView) {
	      this.triggerMethod('before:remove:empty', this);

	      this._destroyChildren();
	      delete this._showingEmptyView;

	      this.triggerMethod('remove:empty', this);
	    }
	  },


	  // Retrieve the empty view class
	  _getEmptyView: function _getEmptyView() {
	    var emptyView = this.emptyView;

	    if (!emptyView) {
	      return;
	    }

	    return this._getView(emptyView);
	  },


	  // Retrieve the `childView` class
	  // The `childView` property can be either a view class or a function that
	  // returns a view class. If it is a function, it will receive the model that
	  // will be passed to the view instance (created from the returned view class)
	  _getChildView: function _getChildView(child) {
	    var childView = this.childView;

	    if (!childView) {
	      throw new MarionetteError({
	        name: 'NoChildViewError',
	        message: 'A "childView" must be specified'
	      });
	    }

	    childView = this._getView(childView, child);

	    if (!childView) {
	      throw new MarionetteError({
	        name: 'InvalidChildViewError',
	        message: '"childView" must be a view class or a function that returns a view class'
	      });
	    }

	    return childView;
	  },


	  // First check if the `view` is a view class (the common case)
	  // Then check if it's a function (which we assume that returns a view class)
	  _getView: function _getView(view, child) {
	    if (view.prototype instanceof Backbone.View || view === Backbone.View) {
	      return view;
	    } else if (_.isFunction(view)) {
	      return view.call(this, child);
	    }
	  },


	  // Internal method for building and adding a child view
	  _addChild: function _addChild(child, index) {
	    var view = this._createView(child, index);
	    this.addChildView(view, index);

	    return view;
	  },
	  _getChildViewOptions: function _getChildViewOptions(child, index) {
	    if (_.isFunction(this.childViewOptions)) {
	      return this.childViewOptions(child, index);
	    }

	    return this.childViewOptions;
	  },


	  // Render the child's view and add it to the HTML for the collection view at a given index.
	  // This will also update the indices of later views in the collection in order to keep the
	  // children in sync with the collection.
	  addChildView: function addChildView(view, index) {
	    this.triggerMethod('before:add:child', this, view);
	    this._setupChildView(view, index);

	    // Store the child view itself so we can properly remove and/or destroy it later
	    if (this._isBuffering) {
	      // Add to children, but don't update children's length.
	      this.children._add(view);
	    } else {
	      // increment indices of views after this one
	      this._updateIndices(view, true);
	      this.children.add(view);
	    }

	    this._renderView(view);

	    this._attachView(view, index);

	    this.triggerMethod('add:child', this, view);

	    return view;
	  },


	  // Internal method. This decrements or increments the indices of views after the added/removed
	  // view to keep in sync with the collection.
	  _updateIndices: function _updateIndices(views, increment) {
	    if (!this.sort) {
	      return;
	    }

	    var view = _.isArray(views) ? this._findGreatestIndexedView(views) : views;

	    // update the indexes of views after this one
	    this.children.each(function (laterView) {
	      if (laterView._index >= view._index) {
	        laterView._index += increment ? 1 : -1;
	      }
	    });
	  },
	  _renderView: function _renderView(view) {
	    if (view._isRendered) {
	      return;
	    }

	    if (!view.supportsRenderLifecycle) {
	      triggerMethodOn(view, 'before:render', view);
	    }

	    view.render();

	    if (!view.supportsRenderLifecycle) {
	      view._isRendered = true;
	      triggerMethodOn(view, 'render', view);
	    }
	  },
	  _attachView: function _attachView(view, index) {
	    // Only trigger attach if already attached and not buffering,
	    // otherwise _endBuffering() or Region#show() handles this.
	    var shouldTriggerAttach = !view._isAttached && !this._isBuffering && this._isAttached;

	    if (shouldTriggerAttach) {
	      triggerMethodOn(view, 'before:attach', view);
	    }

	    this.attachHtml(this, view, index);

	    if (shouldTriggerAttach) {
	      view._isAttached = true;
	      triggerMethodOn(view, 'attach', view);
	    }
	  },


	  // Build a `childView` for a model in the collection.
	  buildChildView: function buildChildView(child, ChildViewClass, childViewOptions) {
	    var options = _.extend({ model: child }, childViewOptions);
	    return new ChildViewClass(options);
	  },


	  // Remove the child view and destroy it. This function also updates the indices of later views
	  // in the collection in order to keep the children in sync with the collection.
	  removeChildView: function removeChildView(view) {
	    if (!view || view._isDestroyed) {
	      return view;
	    }

	    this._removeChildView(view);
	    this.children._updateLength();
	    // decrement the index of views after this one
	    this._updateIndices(view, false);
	    return view;
	  },


	  // check if the collection is empty or optionally whether an array of pre-processed models is empty
	  isEmpty: function isEmpty(options) {
	    var models = void 0;
	    if (_.result(options, 'processedModels')) {
	      models = options.processedModels;
	    } else {
	      models = this.collection ? this.collection.models : [];
	      models = this._filterModels(models);
	    }
	    return models.length === 0;
	  },


	  // If empty, show the empty view
	  _checkEmpty: function _checkEmpty() {
	    if (this.isEmpty()) {
	      this._showEmptyView();
	    }
	  },


	  // You might need to override this if you've overridden attachHtml
	  attachBuffer: function attachBuffer(collectionView, buffer) {
	    collectionView.$el.append(buffer);
	  },


	  // Create a fragment buffer from the currently buffered children
	  _createBuffer: function _createBuffer() {
	    var elBuffer = document.createDocumentFragment();
	    _.each(this._bufferedChildren, function (b) {
	      elBuffer.appendChild(b.el);
	    });
	    return elBuffer;
	  },


	  // Append the HTML to the collection's `el`. Override this method to do something other
	  // than `.append`.
	  attachHtml: function attachHtml(collectionView, childView, index) {
	    if (collectionView._isBuffering) {
	      // buffering happens on reset events and initial renders
	      // in order to reduce the number of inserts into the
	      // document, which are expensive.
	      collectionView._bufferedChildren.splice(index, 0, childView);
	    } else {
	      // If we've already rendered the main collection, append
	      // the new child into the correct order if we need to. Otherwise
	      // append to the end.
	      if (!collectionView._insertBefore(childView, index)) {
	        collectionView._insertAfter(childView);
	      }
	    }
	  },


	  // Internal method. Check whether we need to insert the view into the correct position.
	  _insertBefore: function _insertBefore(childView, index) {
	    var currentView = void 0;
	    var findPosition = this.sort && index < this.children.length - 1;
	    if (findPosition) {
	      // Find the view after this one
	      currentView = this.children.find(function (view) {
	        return view._index === index + 1;
	      });
	    }

	    if (currentView) {
	      currentView.$el.before(childView.el);
	      return true;
	    }

	    return false;
	  },


	  // Internal method. Append a view to the end of the $el
	  _insertAfter: function _insertAfter(childView) {
	    this.$el.append(childView.el);
	  },


	  // Internal method to set up the `children` object for storing all of the child views
	  _initChildViewStorage: function _initChildViewStorage() {
	    this.children = new Container();
	  },


	  // called by ViewMixin destroy
	  _removeChildren: function _removeChildren() {
	    this._destroyChildren({ checkEmpty: false });
	  },


	  // Destroy the child views that this collection view is holding on to, if any
	  _destroyChildren: function _destroyChildren(options) {
	    if (!this.children.length) {
	      return;
	    }

	    this.triggerMethod('before:destroy:children', this);
	    var childModels = this.children.map('model');
	    this._removeChildModels(childModels, options);
	    this.triggerMethod('destroy:children', this);
	  },


	  // Return true if the given child should be shown. Return false otherwise.
	  // The filter will be passed (child, index, collection), where
	  //  'child' is the given model
	  //  'index' is the index of that model in the collection
	  //  'collection' is the collection referenced by this CollectionView
	  _shouldAddChild: function _shouldAddChild(child, index) {
	    var filter = this.filter;
	    return !_.isFunction(filter) || filter.call(this, child, index, this.collection);
	  },


	  // Set up the child view event forwarding. Uses a "childview:" prefix in front of all forwarded events.
	  _proxyChildEvents: function _proxyChildEvents(view) {
	    this.listenTo(view, 'all', this._childViewEventHandler);
	  }
	});

	_.extend(CollectionView.prototype, ViewMixin);

	var ClassOptions$4 = ['childViewContainer', 'template', 'templateContext'];

	// Used for rendering a branch-leaf, hierarchical structure.
	// Extends directly from CollectionView
	// @deprecated
	var CompositeView = CollectionView.extend({

	  // Setting up the inheritance chain which allows changes to
	  // Marionette.CollectionView.prototype.constructor which allows overriding
	  // option to pass '{sort: false}' to prevent the CompositeView from
	  // maintaining the sorted order of the collection.
	  // This will fallback onto appending childView's to the end.
	  constructor: function constructor(options) {
	    deprecate('CompositeView is deprecated. Convert to View at your earliest convenience');

	    this.mergeOptions(options, ClassOptions$4);

	    CollectionView.prototype.constructor.apply(this, arguments);
	  },


	  // Configured the initial events that the composite view
	  // binds to. Override this method to prevent the initial
	  // events, or to add your own initial events.
	  _initialEvents: function _initialEvents() {

	    // Bind only after composite view is rendered to avoid adding child views
	    // to nonexistent childViewContainer

	    if (this.collection) {
	      this.listenTo(this.collection, 'add', this._onCollectionAdd);
	      this.listenTo(this.collection, 'update', this._onCollectionUpdate);
	      this.listenTo(this.collection, 'reset', this.renderChildren);

	      if (this.sort) {
	        this.listenTo(this.collection, 'sort', this._sortViews);
	      }
	    }
	  },


	  // Retrieve the `childView` to be used when rendering each of
	  // the items in the collection. The default is to return
	  // `this.childView` or Marionette.CompositeView if no `childView`
	  // has been defined. As happens in CollectionView, `childView` can
	  // be a function (which should return a view class).
	  _getChildView: function _getChildView(child) {
	    var childView = this.childView;

	    // for CompositeView, if `childView` is not specified, we'll get the same
	    // composite view class rendered for each child in the collection
	    // then check if the `childView` is a view class (the common case)
	    // finally check if it's a function (which we assume that returns a view class)
	    if (!childView) {
	      return this.constructor;
	    }

	    childView = this._getView(childView, child);

	    if (!childView) {
	      throw new MarionetteError({
	        name: 'InvalidChildViewError',
	        message: '"childView" must be a view class or a function that returns a view class'
	      });
	    }

	    return childView;
	  },


	  // Return the serialized model
	  serializeData: function serializeData() {
	    return this.serializeModel();
	  },


	  // Renders the model and the collection.
	  render: function render() {
	    this._ensureViewIsIntact();
	    this._isRendering = true;
	    this.resetChildViewContainer();

	    this.triggerMethod('before:render', this);

	    this._renderTemplate();
	    this.bindUIElements();
	    this.renderChildren();

	    this._isRendering = false;
	    this._isRendered = true;
	    this.triggerMethod('render', this);
	    return this;
	  },
	  renderChildren: function renderChildren() {
	    if (this._isRendered || this._isRendering) {
	      CollectionView.prototype._renderChildren.call(this);
	    }
	  },


	  // You might need to override this if you've overridden attachHtml
	  attachBuffer: function attachBuffer(compositeView, buffer) {
	    var $container = this.getChildViewContainer(compositeView);
	    $container.append(buffer);
	  },


	  // Internal method. Append a view to the end of the $el.
	  // Overidden from CollectionView to ensure view is appended to
	  // childViewContainer
	  _insertAfter: function _insertAfter(childView) {
	    var $container = this.getChildViewContainer(this, childView);
	    $container.append(childView.el);
	  },


	  // Internal method. Append reordered childView'.
	  // Overidden from CollectionView to ensure reordered views
	  // are appended to childViewContainer
	  _appendReorderedChildren: function _appendReorderedChildren(children) {
	    var $container = this.getChildViewContainer(this);
	    $container.append(children);
	  },


	  // Internal method to ensure an `$childViewContainer` exists, for the
	  // `attachHtml` method to use.
	  getChildViewContainer: function getChildViewContainer(containerView, childView) {
	    if (!!containerView.$childViewContainer) {
	      return containerView.$childViewContainer;
	    }

	    var container = void 0;
	    var childViewContainer = containerView.childViewContainer;
	    if (childViewContainer) {

	      var selector = _.result(containerView, 'childViewContainer');

	      if (selector.charAt(0) === '@' && containerView.ui) {
	        container = containerView.ui[selector.substr(4)];
	      } else {
	        container = containerView.$(selector);
	      }

	      if (container.length <= 0) {
	        throw new MarionetteError({
	          name: 'ChildViewContainerMissingError',
	          message: 'The specified "childViewContainer" was not found: ' + containerView.childViewContainer
	        });
	      }
	    } else {
	      container = containerView.$el;
	    }

	    containerView.$childViewContainer = container;
	    return container;
	  },


	  // Internal method to reset the `$childViewContainer` on render
	  resetChildViewContainer: function resetChildViewContainer() {
	    if (this.$childViewContainer) {
	      this.$childViewContainer = undefined;
	    }
	  }
	});

	// To prevent duplication but allow the best View organization
	// Certain View methods are mixed directly into the deprecated CompositeView
	var MixinFromView = _.pick(View.prototype, 'serializeModel', 'getTemplate', '_renderTemplate', 'mixinTemplateContext', 'attachElContent');
	_.extend(CompositeView.prototype, MixinFromView);

	var ClassOptions$5 = ['collectionEvents', 'events', 'modelEvents', 'triggers', 'ui'];

	var Behavior = MarionetteObject.extend({
	  cidPrefix: 'mnb',

	  constructor: function constructor(options, view) {
	    // Setup reference to the view.
	    // this comes in handle when a behavior
	    // wants to directly talk up the chain
	    // to the view.
	    this.view = view;
	    this.defaults = _.clone(_.result(this, 'defaults', {}));
	    this._setOptions(this.defaults, options);
	    this.mergeOptions(this.options, ClassOptions$5);

	    // Construct an internal UI hash using
	    // the behaviors UI hash and then the view UI hash.
	    // This allows the user to use UI hash elements
	    // defined in the parent view as well as those
	    // defined in the given behavior.
	    // This order will help the reuse and share of a behavior
	    // between multiple views, while letting a view override a
	    // selector under an UI key.
	    this.ui = _.extend({}, _.result(this, 'ui'), _.result(view, 'ui'));

	    MarionetteObject.apply(this, arguments);
	  },


	  // proxy behavior $ method to the view
	  // this is useful for doing jquery DOM lookups
	  // scoped to behaviors view.
	  $: function $() {
	    return this.view.$.apply(this.view, arguments);
	  },


	  // Stops the behavior from listening to events.
	  // Overrides Object#destroy to prevent additional events from being triggered.
	  destroy: function destroy() {
	    this.stopListening();

	    return this;
	  },
	  proxyViewProperties: function proxyViewProperties() {
	    this.$el = this.view.$el;
	    this.el = this.view.el;

	    return this;
	  },
	  bindUIElements: function bindUIElements() {
	    this._bindUIElements();

	    return this;
	  },
	  unbindUIElements: function unbindUIElements() {
	    this._unbindUIElements();

	    return this;
	  },
	  getUI: function getUI(name) {
	    this.view._ensureViewIsIntact();
	    return this._getUI(name);
	  },


	  // Handle `modelEvents`, and `collectionEvents` configuration
	  delegateEntityEvents: function delegateEntityEvents() {
	    this._delegateEntityEvents(this.view.model, this.view.collection);

	    return this;
	  },
	  undelegateEntityEvents: function undelegateEntityEvents() {
	    this._undelegateEntityEvents(this.view.model, this.view.collection);

	    return this;
	  },
	  getEvents: function getEvents() {
	    var _this = this;

	    // Normalize behavior events hash to allow
	    // a user to use the @ui. syntax.
	    var behaviorEvents = this.normalizeUIKeys(_.result(this, 'events'));

	    // binds the handler to the behavior and builds a unique eventName
	    return _.reduce(behaviorEvents, function (events, behaviorHandler, key) {
	      if (!_.isFunction(behaviorHandler)) {
	        behaviorHandler = _this[behaviorHandler];
	      }
	      if (!behaviorHandler) {
	        return;
	      }
	      key = getUniqueEventName(key);
	      events[key] = _.bind(behaviorHandler, _this);
	      return events;
	    }, {});
	  },


	  // Internal method to build all trigger handlers for a given behavior
	  getTriggers: function getTriggers() {
	    if (!this.triggers) {
	      return;
	    }

	    // Normalize behavior triggers hash to allow
	    // a user to use the @ui. syntax.
	    var behaviorTriggers = this.normalizeUIKeys(_.result(this, 'triggers'));

	    return this._getViewTriggers(this.view, behaviorTriggers);
	  }
	});

	_.extend(Behavior.prototype, DelegateEntityEventsMixin, TriggersMixin, UIMixin);

	var ClassOptions$6 = ['region', 'regionClass'];

	// A container for a Marionette application.
	var Application = MarionetteObject.extend({
	  cidPrefix: 'mna',

	  constructor: function constructor(options) {
	    this._setOptions(options);

	    this.mergeOptions(options, ClassOptions$6);

	    this._initRegion();

	    MarionetteObject.prototype.constructor.apply(this, arguments);
	  },


	  regionClass: Region,

	  _initRegion: function _initRegion() {
	    var region = this.region;

	    if (!region) {
	      return;
	    }

	    var defaults = {
	      regionClass: this.regionClass
	    };

	    this._region = buildRegion(region, defaults);
	  },
	  getRegion: function getRegion() {
	    return this._region;
	  },
	  showView: function showView(view) {
	    var region = this.getRegion();

	    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
	      args[_key - 1] = arguments[_key];
	    }

	    return region.show.apply(region, [view].concat(args));
	  },
	  getView: function getView() {
	    return this.getRegion().currentView;
	  },


	  // kick off all of the application's processes.
	  start: function start(options) {
	    this.triggerMethod('before:start', this, options);
	    this.triggerMethod('start', this, options);
	    return this;
	  }
	});

	var ClassOptions$7 = ['appRoutes', 'controller'];

	var AppRouter = Backbone.Router.extend({
	  constructor: function constructor(options) {
	    this._setOptions(options);

	    this.mergeOptions(options, ClassOptions$7);

	    Backbone.Router.apply(this, arguments);

	    var appRoutes = this.appRoutes;
	    var controller = this._getController();
	    this.processAppRoutes(controller, appRoutes);
	    this.on('route', this._processOnRoute, this);
	  },


	  // Similar to route method on a Backbone Router but
	  // method is called on the controller
	  appRoute: function appRoute(route, methodName) {
	    var controller = this._getController();
	    this._addAppRoute(controller, route, methodName);
	    return this;
	  },


	  // process the route event and trigger the onRoute
	  // method call, if it exists
	  _processOnRoute: function _processOnRoute(routeName, routeArgs) {
	    // make sure an onRoute before trying to call it
	    if (_.isFunction(this.onRoute)) {
	      // find the path that matches the current route
	      var routePath = _.invert(this.appRoutes)[routeName];
	      this.onRoute(routeName, routePath, routeArgs);
	    }
	  },


	  // Internal method to process the `appRoutes` for the
	  // router, and turn them in to routes that trigger the
	  // specified method on the specified `controller`.
	  processAppRoutes: function processAppRoutes(controller, appRoutes) {
	    var _this = this;

	    if (!appRoutes) {
	      return this;
	    }

	    var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes

	    _.each(routeNames, function (route) {
	      _this._addAppRoute(controller, route, appRoutes[route]);
	    });

	    return this;
	  },
	  _getController: function _getController() {
	    return this.controller;
	  },
	  _addAppRoute: function _addAppRoute(controller, route, methodName) {
	    var method = controller[methodName];

	    if (!method) {
	      throw new MarionetteError('Method "' + methodName + '" was not found on the controller');
	    }

	    this.route(route, methodName, _.bind(method, controller));
	  },


	  triggerMethod: triggerMethod
	});

	_.extend(AppRouter.prototype, CommonMixin);

	// Placeholder method to be extended by the user.
	// The method should define the object that stores the behaviors.
	// i.e.
	//
	// ```js
	// Marionette.Behaviors.behaviorsLookup: function() {
	//   return App.Behaviors
	// }
	// ```
	function behaviorsLookup() {
	  throw new MarionetteError({
	    message: 'You must define where your behaviors are stored.',
	    url: 'marionette.behaviors.md#behaviorslookup'
	  });
	}

	// Add Feature flags here
	// e.g. 'class' => false
	var FEATURES = {};

	function isEnabled(name) {
	  return !!FEATURES[name];
	}

	function setEnabled(name, state) {
	  return FEATURES[name] = state;
	}

	var previousMarionette = Backbone.Marionette;
	var Marionette = Backbone.Marionette = {};

	// This allows you to run multiple instances of Marionette on the same
	// webapp. After loading the new version, call `noConflict()` to
	// get a reference to it. At the same time the old version will be
	// returned to Backbone.Marionette.
	Marionette.noConflict = function () {
	  Backbone.Marionette = previousMarionette;
	  return this;
	};

	// Utilities
	Marionette.bindEvents = proxy(bindEvents);
	Marionette.unbindEvents = proxy(unbindEvents);
	Marionette.bindRequests = proxy(bindRequests);
	Marionette.unbindRequests = proxy(unbindRequests);
	Marionette.mergeOptions = proxy(mergeOptions);
	Marionette.getOption = proxy(getOption);
	Marionette.normalizeMethods = proxy(normalizeMethods);
	Marionette.extend = extend;
	Marionette.isNodeAttached = isNodeAttached;
	Marionette.deprecate = deprecate;
	Marionette.triggerMethod = proxy(triggerMethod);
	Marionette.triggerMethodOn = triggerMethodOn;
	Marionette.isEnabled = isEnabled;
	Marionette.setEnabled = setEnabled;
	Marionette.monitorViewEvents = monitorViewEvents;

	Marionette.Behaviors = {};
	Marionette.Behaviors.behaviorsLookup = behaviorsLookup;

	// Classes
	Marionette.Application = Application;
	Marionette.AppRouter = AppRouter;
	Marionette.Renderer = Renderer;
	Marionette.TemplateCache = TemplateCache;
	Marionette.View = View;
	Marionette.CollectionView = CollectionView;
	Marionette.CompositeView = CompositeView;
	Marionette.Behavior = Behavior;
	Marionette.Region = Region;
	Marionette.Error = MarionetteError;
	Marionette.Object = MarionetteObject;

	// Configuration
	Marionette.DEV_MODE = false;
	Marionette.FEATURES = FEATURES;
	Marionette.VERSION = version;

	return Marionette;

}));

//# sourceMappingURL=backbone.marionette.js.map
