• backbone.marionette.js

  • ¶
    /**
    * @license
    * MarionetteJS (Backbone.Marionette)
    * ----------------------------------
    * v4.1.2
    *
    * Copyright (c)2019 Derick Bailey, Muted Solutions, LLC.
    * Distributed under MIT license
    *
    * http://marionettejs.com
    */
    
    
    (function (global, factory) {
      typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('backbone'), require('underscore'), require('backbone.radio')) :
      typeof define === 'function' && define.amd ? define(['exports', 'backbone', 'underscore', 'backbone.radio'], factory) :
      (global = global || self, (function () {
        var current = global.Marionette;
        var exports = global.Marionette = {};
        factory(exports, global.Backbone, global._, global.Backbone.Radio);
        exports.noConflict = function () { global.Marionette = current; return exports; };
      }()));
    }(this, function (exports, Backbone, _, Radio) { 'use strict';
    
      Backbone = Backbone && Backbone.hasOwnProperty('default') ? Backbone['default'] : Backbone;
      _ = _ && _.hasOwnProperty('default') ? _['default'] : _;
      Radio = Radio && Radio.hasOwnProperty('default') ? Radio['default'] : Radio;
    
      var version = "4.1.2";
  • ¶

    Internal utility for creating context style global utils

      var proxy = function proxy(method) {
        return function (context) {
          for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }
    
          return method.apply(context, args);
        };
      };
  • ¶

    Marionette.extend

      var extend = Backbone.Model.extend;
  • ¶

  • ¶

    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;
    
        if (!hash) {
          return;
        }
    
        return _.reduce(hash, function (normalizedHash, method, name) {
          if (!_.isFunction(method)) {
            method = _this[method];
          }
    
          if (method) {
            normalizedHash[name] = method;
          }
    
          return normalizedHash;
        }, {});
      };
  • ¶

    Error

      var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number', 'url'];
      var MarionetteError = extend.call(Error, {
        urlRoot: "http://marionettejs.com/docs/v".concat(version, "/"),
        url: '',
        constructor: function constructor(options) {
          var error = Error.call(this, options.message);
    
          _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps));
    
          if (Error.captureStackTrace) {
            this.captureStackTrace();
          }
    
          this.url = this.urlRoot + this.url;
        },
        captureStackTrace: function captureStackTrace() {
          Error.captureStackTrace(this, MarionetteError);
        },
        toString: function toString() {
          return "".concat(this.name, ": ").concat(this.message, " See: ").concat(this.url);
        }
      });
  • ¶

    Bind Entity Events & Unbind Entity Events

      function normalizeBindings(context, bindings) {
        if (!_.isObject(bindings)) {
          throw new MarionetteError({
            message: 'Bindings must be an object.',
            url: 'common.html#bindevents'
          });
        }
    
        return normalizeMethods.call(context, bindings);
      }
    
      function bindEvents(entity, bindings) {
        if (!entity || !bindings) {
          return this;
        }
    
        this.listenTo(entity, normalizeBindings(this, bindings));
        return this;
      }
    
      function unbindEvents(entity, bindings) {
        if (!entity) {
          return this;
        }
    
        if (!bindings) {
          this.stopListening(entity);
          return this;
        }
    
        this.stopListening(entity, normalizeBindings(this, bindings));
        return this;
      } // Export Public API
  • ¶

    Bind/Unbind Radio Requests

      function normalizeBindings$1(context, bindings) {
        if (!_.isObject(bindings)) {
          throw new MarionetteError({
            message: 'Bindings must be an object.',
            url: 'common.html#bindrequests'
          });
        }
    
        return normalizeMethods.call(context, bindings);
      }
    
      function bindRequests(channel, bindings) {
        if (!channel || !bindings) {
          return this;
        }
    
        channel.reply(normalizeBindings$1(this, bindings), this);
        return this;
      }
    
      function unbindRequests(channel, bindings) {
        if (!channel) {
          return this;
        }
    
        if (!bindings) {
          channel.stopReplying(null, null, this);
          return this;
        }
    
        channel.stopReplying(normalizeBindings$1(this, bindings));
        return this;
      }
  • ¶

    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];
        }
      };
    
      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;
          }
        });
      };
  • ¶

    DOM Refresh

      function triggerMethodChildren(view, event, shouldTrigger) {
        if (!view._getImmediateChildren) {
          return;
        }
    
        _.each(view._getImmediateChildren(), function (child) {
          if (!shouldTrigger(child)) {
            return;
          }
    
          child.triggerMethod(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) {
        view._isAttached = false;
        return true;
      }
    
      function triggerDOMRefresh(view) {
        if (view._isAttached && view._isRendered) {
          view.triggerMethod('dom:refresh', view);
        }
      }
    
      function triggerDOMRemove(view) {
        if (view._isAttached && view._isRendered) {
          view.triggerMethod('dom:remove', view);
        }
      }
    
      function handleBeforeAttach() {
        triggerMethodChildren(this, 'before:attach', shouldTriggerAttach);
      }
    
      function handleAttach() {
        triggerMethodChildren(this, 'attach', shouldAttach);
        triggerDOMRefresh(this);
      }
    
      function handleBeforeDetach() {
        triggerMethodChildren(this, 'before:detach', shouldTriggerDetach);
        triggerDOMRemove(this);
      }
    
      function handleDetach() {
        triggerMethodChildren(this, 'detach', shouldDetach);
      }
    
      function handleBeforeRender() {
        triggerDOMRemove(this);
      }
    
      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 || view.monitorViewEvents === false) {
          return;
        }
    
        view._areViewEventsMonitored = true;
        view.on({
          'before:attach': handleBeforeAttach,
          'attach': handleAttach,
          'before:detach': handleBeforeDetach,
          'detach': handleDetach,
          'before:render': handleBeforeRender,
          'render': handleRender
        });
      }
  • ¶

    Trigger Method

      var splitter = /(^|:)(\w)/gi; // Only calc getOnMethodName once
    
      var methodCache = {}; // 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 = function getOnMethodName(event) {
        if (!methodCache[event]) {
          methodCache[event] = 'on' + event.replace(splitter, getEventName);
        }
    
        return methodCache[event];
      }; // 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) {
  • ¶

    get the method name from the event name

        var methodName = getOnMethodName(event);
        var method = getOption.call(this, methodName);
        var result; // call the onMethodName if it exists
    
        if (_.isFunction(method)) {
  • ¶

    pass all args, except the event name

          result = method.apply(this, _.drop(arguments));
        } // trigger the event
    
    
        this.trigger.apply(this, arguments);
        return result;
      }
    
      var Events = {
        triggerMethod: triggerMethod
      };
    
      var CommonMixin = {
  • ¶

    Imports the “normalizeMethods” to transform hashes of events=>function references/names to a hash of events=>function references

        normalizeMethods: normalizeMethods,
        _setOptions: function _setOptions(options, classOptions) {
          this.options = _.extend({}, _.result(this, 'options'), options);
          this.mergeOptions(options, classOptions);
        },
  • ¶

    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,
  • ¶

    Enable binding view’s requests.

        bindRequests: bindRequests,
  • ¶

    Enable unbinding view’s requests.

        unbindRequests: unbindRequests,
        triggerMethod: triggerMethod
      };
    
      _.extend(CommonMixin, Backbone.Events);
    
      var DestroyMixin = {
        _isDestroyed: false,
        isDestroyed: function isDestroyed() {
          return this._isDestroyed;
        },
        destroy: function destroy(options) {
          if (this._isDestroyed) {
            return this;
          }
    
          this.triggerMethod('before:destroy', this, options);
          this._isDestroyed = true;
          this.triggerMethod('destroy', this, options);
          this.stopListening();
          return this;
        }
      };
  • ¶
    • channelName
    • radioEvents
    • radioRequests
      var RadioMixin = {
        _initRadio: function _initRadio() {
          var channelName = _.result(this, 'channelName');
    
          if (!channelName) {
            return;
          }
          /* istanbul ignore next */
    
    
          if (!Radio) {
            throw new MarionetteError({
              message: 'The dependency "backbone.radio" is missing.',
              url: 'backbone.radio.html#marionette-integration'
            });
          }
    
          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;
        }
      };
  • ¶

    Object

      var ClassOptions = ['channelName', 'radioEvents', 'radioRequests']; // Object borrows many conventions and utilities from Backbone.
    
      var MarionetteObject = function MarionetteObject(options) {
        this._setOptions(options, ClassOptions);
    
        this.cid = _.uniqueId(this.cidPrefix);
    
        this._initRadio();
    
        this.initialize.apply(this, arguments);
      };
    
      MarionetteObject.extend = extend; // Object Methods
  • ¶

  • ¶
      _.extend(MarionetteObject.prototype, CommonMixin, DestroyMixin, RadioMixin, {
        cidPrefix: 'mno',
  • ¶

    This is a noop method intended to be overridden

        initialize: function initialize() {}
      });
  • ¶

    Implementation of the invoke method (http://underscorejs.org/#invoke) with support for

      var _invoke = _.invokeMap || _.invoke;
  • ¶
    • 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 an error is thrown
      function getBehaviorClass(options) {
        if (options.behaviorClass) {
          return {
            BehaviorClass: options.behaviorClass,
            options: options
          };
        } //treat functions as a Behavior constructor
    
    
        if (_.isFunction(options)) {
          return {
            BehaviorClass: options,
            options: {}
          };
        }
    
        throw new MarionetteError({
          message: 'Unable to get behavior class. A Behavior constructor should be passed directly or as behaviorClass property of options',
          url: 'marionette.behavior.html#defining-and-attaching-behaviors'
        });
      } // 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, allBehaviors) {
        return _.reduce(behaviors, function (reducedBehaviors, behaviorDefiniton) {
          var _getBehaviorClass = getBehaviorClass(behaviorDefiniton),
              BehaviorClass = _getBehaviorClass.BehaviorClass,
              options = _getBehaviorClass.options;
    
          var behavior = new BehaviorClass(options, view);
          reducedBehaviors.push(behavior);
          return parseBehaviors(view, _.result(behavior, 'behaviors'), reducedBehaviors);
        }, allBehaviors);
      }
    
      var BehaviorsMixin = {
        _initBehaviors: function _initBehaviors() {
          this._behaviors = parseBehaviors(this, _.result(this, 'behaviors'), []);
        },
        _getBehaviorTriggers: function _getBehaviorTriggers() {
          var triggers = _invoke(this._behaviors, '_getTriggers');
    
          return _.reduce(triggers, function (memo, _triggers) {
            return _.extend(memo, _triggers);
          }, {});
        },
        _getBehaviorEvents: function _getBehaviorEvents() {
          var events = _invoke(this._behaviors, '_getEvents');
    
          return _.reduce(events, function (memo, _events) {
            return _.extend(memo, _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(options) {
  • ¶

    Call destroy on each behavior after destroying the view. This unbinds event listeners that behaviors have registered for.

          _invoke(this._behaviors, 'destroy', options);
        },
  • ¶

    Remove a behavior

        _removeBehavior: function _removeBehavior(behavior) {
  • ¶

    Don’t worry about the clean up if the view is destroyed

          if (this._isDestroyed) {
            return;
          } // Remove behavior-only triggers and events
    
    
          this.undelegate(".trig".concat(behavior.cid, " .").concat(behavior.cid));
          this._behaviors = _.without(this._behaviors, behavior);
        },
        _bindBehaviorUIElements: function _bindBehaviorUIElements() {
          _invoke(this._behaviors, 'bindUIElements');
        },
        _unbindBehaviorUIElements: function _unbindBehaviorUIElements() {
          _invoke(this._behaviors, 'unbindUIElements');
        },
        _triggerEventOnBehaviors: function _triggerEventOnBehaviors(eventName, view, options) {
          _invoke(this._behaviors, 'triggerMethod', eventName, view, options);
        }
      };
  • ¶
    • collectionEvents
    • modelEvents
      var DelegateEntityEventsMixin = {
  • ¶

    Handle modelEvents, and collectionEvents configuration

        _delegateEntityEvents: function _delegateEntityEvents(model, collection) {
          if (model) {
            this._modelEvents = _.result(this, 'modelEvents');
            this.bindEvents(model, this._modelEvents);
          }
    
          if (collection) {
            this._collectionEvents = _.result(this, 'collectionEvents');
            this.bindEvents(collection, this._collectionEvents);
          }
        },
  • ¶

    Remove any previously delegate entity events

        _undelegateEntityEvents: function _undelegateEntityEvents(model, collection) {
          if (this._modelEvents) {
            this.unbindEvents(model, this._modelEvents);
            delete this._modelEvents;
          }
    
          if (this._collectionEvents) {
            this.unbindEvents(collection, this._collectionEvents);
            delete this._collectionEvents;
          }
        },
  • ¶

    Remove cached event handlers

        _deleteEntityEventHandlers: function _deleteEntityEventHandlers() {
          delete this._modelEvents;
          delete this._collectionEvents;
        }
      };
  • ¶
    • template
    • templateContext
      var TemplateRenderMixin = {
  • ¶

    Internal method to render the template with the serialized data and template context

        _renderTemplate: function _renderTemplate(template) {
  • ¶

    Add in entity data and template context

          var data = this.mixinTemplateContext(this.serializeData()) || {}; // Render and add to el
    
          var html = this._renderHtml(template, data);
    
          if (typeof html !== 'undefined') {
            this.attachElContent(html);
          }
        },
  • ¶

    Get the template for this view instance. You can set a template attribute in the view definition or pass a template: TemplateFunction 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(serializedData) {
          var templateContext = _.result(this, 'templateContext');
    
          if (!templateContext) {
            return serializedData;
          }
    
          if (!serializedData) {
            return templateContext;
          }
    
          return _.extend({}, serializedData, templateContext);
        },
  • ¶

    Serialize the view’s model or collection, if it exists, for the template

        serializeData: function serializeData() {
  • ¶

    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

          if (this.collection) {
            return {
              items: this.serializeCollection()
            };
          }
        },
  • ¶

    Prepares the special model property of a view for being displayed in the template. Override this if you need a custom transformation for your view’s model

        serializeModel: function serializeModel() {
          return this.model.attributes;
        },
  • ¶

    Serialize a collection

        serializeCollection: function serializeCollection() {
          return _.map(this.collection.models, function (model) {
            return model.attributes;
          });
        },
  • ¶

    Renders the data into the template

        _renderHtml: function _renderHtml(template, data) {
          return template(data);
        },
  • ¶

    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

    attachElContent(html) {
      this.el.innerHTML = html;
    }
    
        attachElContent: function attachElContent(html) {
          this.Dom.setContents(this.el, html, this.$el);
        }
      };
  • ¶

    Borrow event splitter from Backbone

      var delegateEventSplitter = /^(\S+)\s*(.*)$/; // Set event name to be namespaced using a unique index
  • ¶

    to generate a non colliding event namespace http://api.jquery.com/event.namespace/

      var getNamespacedEventName = function getNamespacedEventName(eventName, namespace) {
        var match = eventName.match(delegateEventSplitter);
        return "".concat(match[1], ".").concat(namespace, " ").concat(match[2]);
      };
  • ¶

    Add Feature flags here e.g. ‘class’ => false

      var FEATURES = {
        childViewEventPrefix: false,
        triggersStopPropagation: true,
        triggersPreventDefault: true,
        DEV_MODE: false
      };
    
      function isEnabled(name) {
        return !!FEATURES[name];
      }
    
      function setEnabled(name, state) {
        return FEATURES[name] = state;
      }
  • ¶

    ‘click:foo’

      function buildViewTrigger(view, triggerDef) {
        if (_.isString(triggerDef)) {
          triggerDef = {
            event: triggerDef
          };
        }
    
        var eventName = triggerDef.event;
        var shouldPreventDefault = !!triggerDef.preventDefault;
    
        if (isEnabled('triggersPreventDefault')) {
          shouldPreventDefault = triggerDef.preventDefault !== false;
        }
    
        var shouldStopPropagation = !!triggerDef.stopPropagation;
    
        if (isEnabled('triggersStopPropagation')) {
          shouldStopPropagation = triggerDef.stopPropagation !== false;
        }
    
        return function (event) {
          if (shouldPreventDefault) {
            event.preventDefault();
          }
    
          if (shouldStopPropagation) {
            event.stopPropagation();
          }
    
          for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }
    
          view.triggerMethod.apply(view, [eventName, view, event].concat(args));
        };
      }
    
      var TriggersMixin = {
  • ¶

    Configure triggers to forward DOM events to view events. triggers: {"click .foo": "do:foo"}

        _getViewTriggers: function _getViewTriggers(view, triggers) {
          var _this = this;
  • ¶

    Configure the triggers, prevent default action and stop propagation of DOM events

          return _.reduce(triggers, function (events, value, key) {
            key = getNamespacedEventName(key, "trig".concat(_this.cid));
            events[key] = buildViewTrigger(view, value);
            return events;
          }, {});
        }
      };
  • ¶

    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;
        }, {});
      };
    
      var uiRegEx = /@ui\.[a-zA-Z-_$0-9]*/g; // utility method for parsing @ui. syntax strings
  • ¶

    into associated selector

      var _normalizeUIString = function normalizeUIString(uiString, ui) {
        return uiString.replace(uiRegEx, 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, property) {
        _.each(hash, function (val, key) {
          if (_.isString(val)) {
            hash[key] = _normalizeUIString(val, ui);
          } else if (val) {
            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, property) {
          var uiBindings = this._getUIBindings();
    
          return _normalizeUIValues(hash, uiBindings, property);
        },
        _getUIBindings: function _getUIBindings() {
          var uiBindings = _.result(this, '_uiBindings');
    
          return uiBindings || _.result(this, '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];
        }
      };
  • ¶

    DomApi

      function _getEl(el) {
        return el instanceof Backbone.$ ? el : Backbone.$(el);
      } // Static setter
    
    
      function setDomApi(mixin) {
        this.prototype.Dom = _.extend({}, this.prototype.Dom, mixin);
        return this;
      }
      var DomApi = {
  • ¶

    Returns a new HTML DOM node instance

        createBuffer: function createBuffer() {
          return document.createDocumentFragment();
        },
  • ¶

    Returns the document element for a given DOM element

        getDocumentEl: function getDocumentEl(el) {
          return el.ownerDocument.documentElement;
        },
  • ¶

    Lookup the selector string Selector may also be a DOM element Returns an array-like object of nodes

        getEl: function getEl(selector) {
          return _getEl(selector);
        },
  • ¶

    Finds the selector string with the el Returns an array-like object of nodes

        findEl: function findEl(el, selector) {
          return _getEl(el).find(selector);
        },
  • ¶

    Returns true if the el contains the node childEl

        hasEl: function hasEl(el, childEl) {
          return el.contains(childEl && childEl.parentNode);
        },
  • ¶

    Detach el from the DOM without removing listeners

        detachEl: function detachEl(el) {
          var _$el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _getEl(el);
    
          _$el.detach();
        },
  • ¶

    Remove oldEl from the DOM and put newEl in its place

        replaceEl: function replaceEl(newEl, oldEl) {
          if (newEl === oldEl) {
            return;
          }
    
          var parent = oldEl.parentNode;
    
          if (!parent) {
            return;
          }
    
          parent.replaceChild(newEl, oldEl);
        },
  • ¶

    Swaps the location of el1 and el2 in the DOM

        swapEl: function swapEl(el1, el2) {
          if (el1 === el2) {
            return;
          }
    
          var parent1 = el1.parentNode;
          var parent2 = el2.parentNode;
    
          if (!parent1 || !parent2) {
            return;
          }
    
          var next1 = el1.nextSibling;
          var next2 = el2.nextSibling;
          parent1.insertBefore(el2, next1);
          parent2.insertBefore(el1, next2);
        },
  • ¶

    Replace the contents of el with the HTML string of html

        setContents: function setContents(el, html) {
          var _$el = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _getEl(el);
    
          _$el.html(html);
        },
  • ¶

    Takes the DOM node el and appends the DOM node contents to the end of the element’s contents.

        appendContents: function appendContents(el, contents) {
          var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
              _ref$_$el = _ref._$el,
              _$el = _ref$_$el === void 0 ? _getEl(el) : _ref$_$el,
              _ref$_$contents = _ref._$contents,
              _$contents = _ref$_$contents === void 0 ? _getEl(contents) : _ref$_$contents;
    
          _$el.append(_$contents);
        },
  • ¶

    Does the el have child nodes

        hasContents: function hasContents(el) {
          return !!el && el.hasChildNodes();
        },
  • ¶

    Remove the inner contents of el from the DOM while leaving el itself in the DOM.

        detachContents: function detachContents(el) {
          var _$el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _getEl(el);
    
          _$el.contents().detach();
        }
      };
  • ¶

    ViewMixin

    • behaviors
    • childViewEventPrefix
    • childViewEvents
    • childViewTriggers
    • collectionEvents
    • modelEvents
    • triggers
    • ui
      var ViewMixin = {
        Dom: DomApi,
        _isElAttached: function _isElAttached() {
          return !!this.el && this.Dom.hasEl(this.Dom.getDocumentEl(this.el), this.el);
        },
        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(events) {
          this._proxyBehaviorViewProperties();
    
          this._buildEventProxies();
    
          var combinedEvents = _.extend({}, this._getBehaviorEvents(), this._getEvents(events), this._getBehaviorTriggers(), this._getTriggers());
    
          Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
          return this;
        },
  • ¶

    Allows Backbone.View events to utilize @ui. selectors

        _getEvents: function _getEvents(events) {
          if (events) {
            return this.normalizeUIKeys(events);
          }
    
          if (!this.events) {
            return;
          }
    
          return this.normalizeUIKeys(_.result(this, '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;
        },
  • ¶

    Handle destroying the view and its children.

        destroy: function destroy(options) {
          if (this._isDestroyed || this._isDestroying) {
            return this;
          }
    
          this._isDestroying = true;
          var shouldTriggerDetach = this._isAttached && !this._disableDetachEvents;
          this.triggerMethod('before:destroy', this, options);
    
          if (shouldTriggerDetach) {
            this.triggerMethod('before:detach', this);
          } // unbind UI elements
    
    
          this.unbindUIElements(); // remove the view from the DOM
    
          this._removeElement();
    
          if (shouldTriggerDetach) {
            this._isAttached = false;
            this.triggerMethod('detach', this);
          } // remove children after the remove to prevent extra paints
    
    
          this._removeChildren();
    
          this._isDestroyed = true;
          this._isRendered = false; // Destroy behaviors after _isDestroyed flag
    
          this._destroyBehaviors(options);
    
          this._deleteEntityEventHandlers();
    
          this.triggerMethod('destroy', this, options);
    
          this._triggerEventOnBehaviors('destroy', this, options);
    
          this.stopListening();
          return this;
        },
  • ¶

    Equates to this.$el.remove

        _removeElement: function _removeElement() {
          this.$el.off().removeData();
          this.Dom.detachEl(this.el, this.$el);
        },
  • ¶

    This method binds the elements specified in the “ui” hash

        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) {
          return this._getUI(name);
        },
  • ¶

    Cache childViewEvents and childViewTriggers

        _buildEventProxies: function _buildEventProxies() {
          this._childViewEvents = this.normalizeMethods(_.result(this, 'childViewEvents'));
          this._childViewTriggers = _.result(this, 'childViewTriggers');
          this._eventPrefix = this._getEventPrefix();
        },
        _getEventPrefix: function _getEventPrefix() {
          var defaultPrefix = isEnabled('childViewEventPrefix') ? 'childview' : false;
    
          var prefix = _.result(this, 'childViewEventPrefix', defaultPrefix);
    
          return prefix === false ? prefix : prefix + ':';
        },
        _proxyChildViewEvents: function _proxyChildViewEvents(view) {
          if (this._childViewEvents || this._childViewTriggers || this._eventPrefix) {
            this.listenTo(view, 'all', this._childViewEventHandler);
          }
        },
        _childViewEventHandler: function _childViewEventHandler(eventName) {
          var childViewEvents = this._childViewEvents; // call collectionView childViewEvent if defined
    
          for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }
    
          if (childViewEvents && 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 && childViewTriggers[eventName]) {
            this.triggerMethod.apply(this, [childViewTriggers[eventName]].concat(args));
          }
    
          if (this._eventPrefix) {
            this.triggerMethod.apply(this, [this._eventPrefix + eventName].concat(args));
          }
        }
      };
    
      _.extend(ViewMixin, BehaviorsMixin, CommonMixin, DelegateEntityEventsMixin, TemplateRenderMixin, TriggersMixin, UIMixin);
    
      function renderView(view) {
        if (view._isRendered) {
          return;
        }
    
        if (!view.supportsRenderLifecycle) {
          view.triggerMethod('before:render', view);
        }
    
        view.render();
        view._isRendered = true;
    
        if (!view.supportsRenderLifecycle) {
          view.triggerMethod('render', view);
        }
      }
      function destroyView(view, disableDetachEvents) {
        if (view.destroy) {
  • ¶

    Attach flag for public destroy function internal check

          view._disableDetachEvents = disableDetachEvents;
          view.destroy();
          return;
        } // Destroy for non-Marionette Views
    
    
        if (!view.supportsDestroyLifecycle) {
          view.triggerMethod('before:destroy', view);
        }
    
        var shouldTriggerDetach = view._isAttached && !disableDetachEvents;
    
        if (shouldTriggerDetach) {
          view.triggerMethod('before:detach', view);
        }
    
        view.remove();
    
        if (shouldTriggerDetach) {
          view._isAttached = false;
          view.triggerMethod('detach', view);
        }
    
        view._isDestroyed = true;
    
        if (!view.supportsDestroyLifecycle) {
          view.triggerMethod('destroy', view);
        }
      }
  • ¶

    Region

      var classErrorName = 'RegionError';
      var ClassOptions$1 = ['allowMissingEl', 'parentEl', 'replaceElement'];
    
      var Region = function Region(options) {
        this._setOptions(options, ClassOptions$1);
    
        this.cid = _.uniqueId(this.cidPrefix); // 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;
        this.$el = this._getEl(this.el);
        this.initialize.apply(this, arguments);
      };
    
      Region.extend = extend;
      Region.setDomApi = setDomApi; // Region Methods
  • ¶

  • ¶
      _.extend(Region.prototype, CommonMixin, {
        Dom: DomApi,
        cidPrefix: 'mnr',
        replaceElement: false,
        _isReplaced: false,
        _isSwappingView: false,
  • ¶

    This is a noop method intended to be overridden

        initialize: function initialize() {},
  • ¶

    Displays a view instance inside of the region. If necessary handles calling the render method for you. Reads content directly from the el attribute.

        show: function show(view, options) {
          if (!this._ensureElement(options)) {
            return;
          }
    
          view = this._getView(view, options);
    
          if (view === this.currentView) {
            return this;
          }
    
          if (view._isShown) {
            throw new MarionetteError({
              name: classErrorName,
              message: 'View is already shown in a Region or CollectionView',
              url: 'marionette.region.html#showing-a-view'
            });
          }
    
          this._isSwappingView = !!this.currentView;
          this.triggerMethod('before:show', this, view, options); // Assume an attached view is already in the region for pre-existing DOM
    
          if (this.currentView || !view._isAttached) {
            this.empty(options);
          }
    
          this._setupChildView(view);
    
          this.currentView = view;
          renderView(view);
    
          this._attachView(view, options);
    
          this.triggerMethod('show', this, view, options);
          this._isSwappingView = false;
          return this;
        },
        _getEl: function _getEl(el) {
          if (!el) {
            throw new MarionetteError({
              name: classErrorName,
              message: 'An "el" must be specified for a region.',
              url: 'marionette.region.html#additional-options'
            });
          }
    
          return this.getEl(el);
        },
        _setEl: function _setEl() {
          this.$el = this._getEl(this.el);
    
          if (this.$el.length) {
            this.el = this.$el[0];
          } // Make sure the $el contains only the el
    
    
          if (this.$el.length > 1) {
            this.$el = this.Dom.getEl(this.el);
          }
        },
  • ¶

    Set the el of the region and move any current view to the new el.

        _setElement: function _setElement(el) {
          if (el === this.el) {
            return this;
          }
    
          var shouldReplace = this._isReplaced;
    
          this._restoreEl();
    
          this.el = el;
    
          this._setEl();
    
          if (this.currentView) {
            var view = this.currentView;
    
            if (shouldReplace) {
              this._replaceEl(view);
            } else {
              this.attachHtml(view);
            }
          }
    
          return this;
        },
        _setupChildView: function _setupChildView(view) {
          monitorViewEvents(view);
    
          this._proxyChildViewEvents(view); // 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);
        },
        _proxyChildViewEvents: function _proxyChildViewEvents(view) {
          var parentView = this._parentView;
    
          if (!parentView) {
            return;
          }
    
          parentView._proxyChildViewEvents(view);
        },
  • ¶

    If the regions parent view is not monitoring its attach/detach events

        _shouldDisableMonitoring: function _shouldDisableMonitoring() {
          return this._parentView && this._parentView.monitorViewEvents === false;
        },
        _isElAttached: function _isElAttached() {
          return this.Dom.hasEl(this.Dom.getDocumentEl(this.el), this.el);
        },
        _attachView: function _attachView(view) {
          var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
              replaceElement = _ref.replaceElement;
    
          var shouldTriggerAttach = !view._isAttached && this._isElAttached() && !this._shouldDisableMonitoring();
          var shouldReplaceEl = typeof replaceElement === 'undefined' ? !!_.result(this, 'replaceElement') : !!replaceElement;
    
          if (shouldTriggerAttach) {
            view.triggerMethod('before:attach', view);
          }
    
          if (shouldReplaceEl) {
            this._replaceEl(view);
          } else {
            this.attachHtml(view);
          }
    
          if (shouldTriggerAttach) {
            view._isAttached = true;
            view.triggerMethod('attach', view);
          } // Corresponds that view is shown in a marionette Region or CollectionView
    
    
          view._isShown = true;
        },
        _ensureElement: function _ensureElement() {
          var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
          if (!_.isObject(this.el)) {
            this._setEl();
          }
    
          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({
                name: classErrorName,
                message: "An \"el\" must exist in DOM for this region ".concat(this.cid),
                url: 'marionette.region.html#additional-options'
              });
            }
          }
    
          return true;
        },
        _getView: function _getView(view) {
          if (!view) {
            throw new MarionetteError({
              name: classErrorName,
              message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.',
              url: 'marionette.region.html#showing-a-view'
            });
          }
    
          if (view._isDestroyed) {
            throw new MarionetteError({
              name: classErrorName,
              message: "View (cid: \"".concat(view.cid, "\") has already been destroyed and cannot be used."),
              url: 'marionette.region.html#showing-a-view'
            });
          }
    
          if (view instanceof Backbone.View) {
            return view;
          }
    
          var viewOptions = this._getViewOptions(view);
    
          return new View(viewOptions);
        },
  • ¶

    This allows for a template or a static string to be used as a template

        _getViewOptions: function _getViewOptions(viewOptions) {
          if (_.isFunction(viewOptions)) {
            return {
              template: viewOptions
            };
          }
    
          if (_.isObject(viewOptions)) {
            return viewOptions;
          }
    
          var template = function template() {
            return viewOptions;
          };
    
          return {
            template: template
          };
        },
  • ¶

    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) {
          var context = _.result(this, 'parentEl');
    
          if (context && _.isString(el)) {
            return this.Dom.findEl(context, el);
          }
    
          return this.Dom.getEl(el);
        },
        _replaceEl: function _replaceEl(view) {
  • ¶

    Always restore the el to ensure the regions el is present before replacing

          this._restoreEl();
    
          view.on('before:destroy', this._restoreEl, this);
          this.Dom.replaceEl(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;
          }
    
          this._detachView(view);
    
          this._isReplaced = false;
        },
  • ¶

    Check to see if the region’s el was replaced.

        isReplaced: function isReplaced() {
          return !!this._isReplaced;
        },
  • ¶

    Check to see if a view is being swapped by another

        isSwappingView: function isSwappingView() {
          return !!this._isSwappingView;
        },
  • ¶

    Override this method to change how the new view is appended to the $el that the region is managing

        attachHtml: function attachHtml(view) {
          this.Dom.appendContents(this.el, view.el, {
            _$el: this.$el,
            _$contents: view.$el
          });
        },
  • ¶

    Destroy the current view, if there is one. If there is no current view, it will detach any html inside the region’s el.

        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;
          }
    
          this._empty(view, true);
    
          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) {
            if (shouldDestroy) {
              this.removeView(view);
            } else {
              this._detachView(view);
            }
    
            view._isShown = false;
    
            this._stopChildViewEvents(view);
          }
    
          this.triggerMethod('empty', this, view);
        },
        _stopChildViewEvents: function _stopChildViewEvents(view) {
          var parentView = this._parentView;
    
          if (!parentView) {
            return;
          }
    
          this._parentView.stopListening(view);
        },
  • ¶

    Non-Marionette safe view.destroy

        destroyView: function destroyView$1(view) {
          if (view._isDestroyed) {
            return view;
          }
    
          destroyView(view, this._shouldDisableMonitoring());
    
          return view;
        },
  • ¶

    Override this method to determine what happens when the view is removed from the region when the view is not being detached

        removeView: function removeView(view) {
          this.destroyView(view);
        },
  • ¶

    Empties the Region without destroying the view Returns the detached view

        detachView: function detachView() {
          var view = this.currentView;
    
          if (!view) {
            return;
          }
    
          this._empty(view);
    
          return view;
        },
        _detachView: function _detachView(view) {
          var shouldTriggerDetach = view._isAttached && !this._shouldDisableMonitoring();
          var shouldRestoreEl = this._isReplaced;
    
          if (shouldTriggerDetach) {
            view.triggerMethod('before:detach', view);
          }
    
          if (shouldRestoreEl) {
            this.Dom.replaceEl(this.el, view.el);
          } else {
            this.detachHtml();
          }
    
          if (shouldTriggerDetach) {
            view._isAttached = false;
            view.triggerMethod('detach', view);
          }
        },
  • ¶

    Override this method to change how the region detaches current content

        detachHtml: function detachHtml() {
          this.Dom.detachContents(this.el, this.$el);
        },
  • ¶

    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);
          this.el = this._initEl;
          delete this.$el;
          return this;
        },
        _isDestroyed: false,
        isDestroyed: function isDestroyed() {
          return this._isDestroyed;
        },
  • ¶

    Destroy the region, remove any child view and remove the region from any associated view

        destroy: function destroy(options) {
          if (this._isDestroyed) {
            return this;
          }
    
          this.triggerMethod('before:destroy', this, options);
          this._isDestroyed = true;
          this.reset(options);
    
          if (this._name) {
            this._parentView._removeReferences(this._name);
          }
    
          delete this._parentView;
          delete this._name;
          this.triggerMethod('destroy', this, options);
          this.stopListening();
          return this;
        }
      });
    
      function buildRegion (definition, defaults) {
        if (definition instanceof Region) {
          return definition;
        }
    
        if (_.isString(definition)) {
          return buildRegionFromObject(defaults, {
            el: definition
          });
        }
    
        if (_.isFunction(definition)) {
          return buildRegionFromObject(defaults, {
            regionClass: definition
          });
        }
    
        if (_.isObject(definition)) {
          return buildRegionFromObject(defaults, definition);
        }
    
        throw new MarionetteError({
          message: 'Improper region configuration type.',
          url: 'marionette.region.html#defining-regions'
        });
      }
    
      function buildRegionFromObject(defaults, definition) {
        var options = _.extend({}, defaults, definition);
    
        var RegionClass = options.regionClass;
        delete options.regionClass;
        return new RegionClass(options);
      }
  • ¶
    • 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, '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._parentView = this;
          region._name = name;
          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, this._removeRegion.bind(this));
    
          return regions;
        },
        _removeRegion: function _removeRegion(region, name) {
          this.triggerMethod('before:remove:region', this, name, region);
          region.destroy();
          this.triggerMethod('remove:region', this, name, region);
        },
  • ¶

    Called in a region’s destroy

        _removeReferences: function _removeReferences(name) {
          delete this.regions[name];
          delete this._regions[name];
        },
  • ¶

    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) {
          if (!this._isRendered) {
            this.render();
          }
    
          return this._regions[name];
        },
        _getRegions: function _getRegions() {
          return _.clone(this._regions);
        },
  • ¶

    Get all regions

        getRegions: function getRegions() {
          if (!this._isRendered) {
            this.render();
          }
    
          return this._getRegions();
        },
        showChildView: function showChildView(name, view, options) {
          var region = this.getRegion(name);
          region.show(view, options);
          return view;
        },
        detachChildView: function detachChildView(name) {
          return this.getRegion(name).detachView();
        },
        getChildView: function getChildView(name) {
          return this.getRegion(name).currentView;
        }
      };
  • ¶

    Static setter for the renderer

      function setRenderer(renderer) {
        this.prototype._renderHtml = renderer;
        return this;
      }
  • ¶

    View

      var ClassOptions$2 = ['behaviors', 'childViewEventPrefix', 'childViewEvents', 'childViewTriggers', 'collectionEvents', 'events', 'modelEvents', 'regionClass', 'regions', 'template', 'templateContext', 'triggers', 'ui']; // Used by _getImmediateChildren
    
      function childReducer(children, region) {
        if (region.currentView) {
          children.push(region.currentView);
        }
    
        return children;
      } // The standard view. Includes view events, automatic rendering
  • ¶

    templates, nested views, and more.

      var View = Backbone.View.extend({
        constructor: function constructor(options) {
          this._setOptions(options, ClassOptions$2);
    
          monitorViewEvents(this);
    
          this._initBehaviors();
    
          this._initRegions();
    
          Backbone.View.prototype.constructor.apply(this, arguments);
          this.delegateEntityEvents();
    
          this._triggerEventOnBehaviors('initialize', this, options);
        },
  • ¶

    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() {
          Backbone.View.prototype.setElement.apply(this, arguments);
          this._isRendered = this.Dom.hasContents(this.el);
          this._isAttached = this._isElAttached();
    
          if (this._isRendered) {
            this.bindUIElements();
          }
    
          return this;
        },
  • ¶

    If a template is available, renders it into the view’s el Re-inits regions and binds UI.

        render: function render() {
          var template = this.getTemplate();
    
          if (template === false || this._isDestroyed) {
            return this;
          }
    
          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(template);
    
          this.bindUIElements();
          this._isRendered = true;
          this.triggerMethod('render', this);
          return this;
        },
  • ¶

    called by ViewMixin destroy

        _removeChildren: function _removeChildren() {
          this.removeRegions();
        },
        _getImmediateChildren: function _getImmediateChildren() {
          return _.reduce(this._regions, childReducer, []);
        }
      }, {
        setRenderer: setRenderer,
        setDomApi: setDomApi
      });
    
      _.extend(View.prototype, ViewMixin, RegionsMixin);
  • ¶

    shut down child views.

      var Container = function Container() {
        this._init();
      }; // Mix in methods from Underscore, for iteration, and other
  • ¶

    collection related features. Borrowing this code from Backbone.Collection: https://github.com/jashkenas/backbone/blob/1.1.2/backbone.js#L962

      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', 'partition'];
    
      _.each(methods, function (method) {
        Container.prototype[method] = function () {
          for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
            args[_key] = arguments[_key];
          }
    
          return _[method].apply(_, [this._views].concat(args));
        };
      });
    
      function stringComparator(comparator, view) {
        return view.model && view.model.get(comparator);
      } // Container Methods
  • ¶

  • ¶
      _.extend(Container.prototype, {
  • ¶

    Initializes an empty container

        _init: function _init() {
          this._views = [];
          this._viewsByCid = {};
          this._indexByModel = {};
    
          this._updateLength();
        },
  • ¶

    Add a view to this container. Stores the view by cid and makes it searchable by the model cid (and model itself). Additionally it stores the view by index in the _views array

        _add: function _add(view) {
          var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._views.length;
    
          this._addViewIndexes(view); // add to end by default
    
    
          this._views.splice(index, 0, view);
    
          this._updateLength();
        },
        _addViewIndexes: function _addViewIndexes(view) {
  • ¶

    store the view

          this._viewsByCid[view.cid] = view; // index it by model
    
          if (view.model) {
            this._indexByModel[view.model.cid] = view;
          }
        },
  • ¶

    Sort (mutate) and return the array of the child views.

        _sort: function _sort(comparator, context) {
          if (typeof comparator === 'string') {
            comparator = _.partial(stringComparator, comparator);
            return this._sortBy(comparator);
          }
    
          if (comparator.length === 1) {
            return this._sortBy(comparator.bind(context));
          }
    
          return this._views.sort(comparator.bind(context));
        },
  • ¶

    Makes _.sortBy mutate the array to match this._views.sort

        _sortBy: function _sortBy(comparator) {
          var sortedViews = _.sortBy(this._views, comparator);
    
          this._set(sortedViews);
    
          return sortedViews;
        },
  • ¶

    Replace array contents without overwriting the reference. Should not add/remove views

        _set: function _set(views, shouldReset) {
          this._views.length = 0;
    
          this._views.push.apply(this._views, views.slice(0));
    
          if (shouldReset) {
            this._viewsByCid = {};
            this._indexByModel = {};
    
            _.each(views, this._addViewIndexes.bind(this));
    
            this._updateLength();
          }
        },
  • ¶

    Swap views by index

        _swap: function _swap(view1, view2) {
          var view1Index = this.findIndexByView(view1);
          var view2Index = this.findIndexByView(view2);
    
          if (view1Index === -1 || view2Index === -1) {
            return;
          }
    
          var swapView = this._views[view1Index];
          this._views[view1Index] = this._views[view2Index];
          this._views[view2Index] = swapView;
        },
  • ¶

    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.

        findByModelCid: function findByModelCid(modelCid) {
          return this._indexByModel[modelCid];
        },
  • ¶

    Find a view by index.

        findByIndex: function findByIndex(index) {
          return this._views[index];
        },
  • ¶

    Find the index of a view instance

        findIndexByView: function findIndexByView(view) {
          return this._views.indexOf(view);
        },
  • ¶

    Retrieve a view by its cid directly

        findByCid: function findByCid(cid) {
          return this._viewsByCid[cid];
        },
        hasView: function hasView(view) {
          return !!this.findByCid(view.cid);
        },
  • ¶

    Remove a view and clean up index references.

        _remove: function _remove(view) {
          if (!this._viewsByCid[view.cid]) {
            return;
          } // delete model index
    
    
          if (view.model) {
            delete this._indexByModel[view.model.cid];
          } // remove the view from the container
    
    
          delete this._viewsByCid[view.cid];
          var index = this.findIndexByView(view);
    
          this._views.splice(index, 1);
    
          this._updateLength();
        },
  • ¶

    Update the .length attribute on this container

        _updateLength: function _updateLength() {
          this.length = this._views.length;
        }
      });
  • ¶

    Collection View

      var classErrorName$1 = 'CollectionViewError';
      var ClassOptions$3 = ['behaviors', 'childView', 'childViewContainer', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'emptyView', 'emptyViewOptions', 'events', 'modelEvents', 'sortWithCollection', 'template', 'templateContext', 'triggers', 'ui', 'viewComparator', 'viewFilter']; // 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

        sortWithCollection: true,
  • ¶

    constructor

        constructor: function constructor(options) {
          this._setOptions(options, ClassOptions$3);
    
          monitorViewEvents(this);
    
          this._initChildViewStorage();
    
          this._initBehaviors();
    
          Backbone.View.prototype.constructor.apply(this, arguments); // Init empty region
    
          this.getEmptyRegion();
          this.delegateEntityEvents();
    
          this._triggerEventOnBehaviors('initialize', this, options);
        },
  • ¶

    Internal method to set up the children object for storing all of the child views _children represents all child views children represents only views filtered to be shown

        _initChildViewStorage: function _initChildViewStorage() {
          this._children = new Container();
          this.children = new Container();
        },
  • ¶

    Create an region to show the emptyView

        getEmptyRegion: function getEmptyRegion() {
          var $emptyEl = this.$container || this.$el;
    
          if (this._emptyRegion && !this._emptyRegion.isDestroyed()) {
            this._emptyRegion._setElement($emptyEl[0]);
    
            return this._emptyRegion;
          }
    
          this._emptyRegion = new Region({
            el: $emptyEl[0],
            replaceElement: false
          });
          this._emptyRegion._parentView = this;
          return this._emptyRegion;
        },
  • ¶

    Configured the initial events that the collection view binds to.

        _initialEvents: function _initialEvents() {
          if (this._isRendered) {
            return;
          }
    
          this.listenTo(this.collection, {
            'sort': this._onCollectionSort,
            'reset': this._onCollectionReset,
            'update': this._onCollectionUpdate
          });
        },
  • ¶

    Internal method. This checks for any changes in the order of the collection. If the index of any view doesn’t match, it will re-sort.

        _onCollectionSort: function _onCollectionSort(collection, _ref) {
          var add = _ref.add,
              merge = _ref.merge,
              remove = _ref.remove;
    
          if (!this.sortWithCollection || this.viewComparator === false) {
            return;
          } // If the data is changing we will handle the sort later in `_onCollectionUpdate`
    
    
          if (add || remove || merge) {
            return;
          } // If the only thing happening here is sorting, sort.
    
    
          this.sort();
        },
        _onCollectionReset: function _onCollectionReset() {
          this._destroyChildren();
    
          this._addChildModels(this.collection.models);
    
          this.sort();
        },
  • ¶

    Handle collection update model additions and removals

        _onCollectionUpdate: function _onCollectionUpdate(collection, options) {
          var changes = options.changes; // Remove first since it'll be a shorter array lookup.
    
          var removedViews = changes.removed.length && this._removeChildModels(changes.removed);
    
          this._addedViews = changes.added.length && this._addChildModels(changes.added);
    
          this._detachChildren(removedViews);
    
          this.sort(); // Destroy removed child views after all of the render is complete
    
          this._removeChildViews(removedViews);
        },
        _removeChildModels: function _removeChildModels(models) {
          var _this = this;
    
          return _.reduce(models, function (views, model) {
            var removeView = _this._removeChildModel(model);
    
            if (removeView) {
              views.push(removeView);
            }
    
            return views;
          }, []);
        },
        _removeChildModel: function _removeChildModel(model) {
          var view = this._children.findByModel(model);
    
          if (view) {
            this._removeChild(view);
          }
    
          return view;
        },
        _removeChild: function _removeChild(view) {
          this.triggerMethod('before:remove:child', this, view);
    
          this.children._remove(view);
    
          this._children._remove(view);
    
          this.triggerMethod('remove:child', this, view);
        },
  • ¶

    Added views are returned for consistency with _removeChildModels

        _addChildModels: function _addChildModels(models) {
          return _.map(models, this._addChildModel.bind(this));
        },
        _addChildModel: function _addChildModel(model) {
          var view = this._createChildView(model);
    
          this._addChild(view);
    
          return view;
        },
        _createChildView: function _createChildView(model) {
          var ChildView = this._getChildView(model);
    
          var childViewOptions = this._getChildViewOptions(model);
    
          var view = this.buildChildView(model, ChildView, childViewOptions);
          return view;
        },
        _addChild: function _addChild(view, index) {
          this.triggerMethod('before:add:child', this, view);
    
          this._setupChildView(view);
    
          this._children._add(view, index);
    
          this.children._add(view, index);
    
          this.triggerMethod('add:child', this, view);
        },
  • ¶

    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: classErrorName$1,
              message: 'A "childView" must be specified',
              url: 'marionette.collectionview.html#collectionviews-childview'
            });
          }
    
          childView = this._getView(childView, child);
    
          if (!childView) {
            throw new MarionetteError({
              name: classErrorName$1,
              message: '"childView" must be a view class or a function that returns a view class',
              url: 'marionette.collectionview.html#collectionviews-childview'
            });
          }
    
          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);
          }
        },
        _getChildViewOptions: function _getChildViewOptions(child) {
          if (_.isFunction(this.childViewOptions)) {
            return this.childViewOptions(child);
          }
    
          return this.childViewOptions;
        },
  • ¶

    Build a childView for a model in the collection. Override to customize the build

        buildChildView: function buildChildView(child, ChildViewClass, childViewOptions) {
          var options = _.extend({
            model: child
          }, childViewOptions);
    
          return new ChildViewClass(options);
        },
        _setupChildView: function _setupChildView(view) {
          monitorViewEvents(view); // We need to listen for if a view is destroyed in a way other
  • ¶

    than through the CollectionView. If this happens we need to remove the reference to the view since once a view has been destroyed we can not reuse it.

          view.on('destroy', this.removeChildView, this); // set up the child view event forwarding
    
          this._proxyChildViewEvents(view);
        },
  • ¶

    used by ViewMixin’s _childViewEventHandler

        _getImmediateChildren: function _getImmediateChildren() {
          return this.children._views;
        },
  • ¶

    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() {
          Backbone.View.prototype.setElement.apply(this, arguments);
          this._isAttached = this._isElAttached();
          return this;
        },
  • ¶

    Render children views.

        render: function render() {
          if (this._isDestroyed) {
            return this;
          }
    
          this.triggerMethod('before:render', this);
    
          this._destroyChildren();
    
          if (this.collection) {
            this._addChildModels(this.collection.models);
    
            this._initialEvents();
          }
    
          var template = this.getTemplate();
    
          if (template) {
            this._renderTemplate(template);
    
            this.bindUIElements();
          }
    
          this._getChildViewContainer();
    
          this.sort();
          this._isRendered = true;
          this.triggerMethod('render', this);
          return this;
        },
  • ¶

    Get a container within the template to add the children within

        _getChildViewContainer: function _getChildViewContainer() {
          var childViewContainer = _.result(this, 'childViewContainer');
    
          this.$container = childViewContainer ? this.$(childViewContainer) : this.$el;
    
          if (!this.$container.length) {
            throw new MarionetteError({
              name: classErrorName$1,
              message: "The specified \"childViewContainer\" was not found: ".concat(childViewContainer),
              url: 'marionette.collectionview.html#defining-the-childviewcontainer'
            });
          }
        },
  • ¶

    Sorts the children then filters and renders the results.

        sort: function sort() {
          this._sortChildren();
    
          this.filter();
          return this;
        },
  • ¶

    Sorts views by viewComparator and sets the children to the new order

        _sortChildren: function _sortChildren() {
          if (!this._children.length) {
            return;
          }
    
          var viewComparator = this.getComparator();
    
          if (!viewComparator) {
            return;
          } // If children are sorted prevent added to end perf
    
    
          delete this._addedViews;
          this.triggerMethod('before:sort', this);
    
          this._children._sort(viewComparator, this);
    
          this.triggerMethod('sort', this);
        },
  • ¶

    Sets the view’s viewComparator and applies the sort if the view is ready. To prevent the render pass { preventRender: true } as the 2nd argument.

        setComparator: function setComparator(comparator) {
          var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
              preventRender = _ref2.preventRender;
    
          var comparatorChanged = this.viewComparator !== comparator;
          var shouldSort = comparatorChanged && !preventRender;
          this.viewComparator = comparator;
    
          if (shouldSort) {
            this.sort();
          }
    
          return this;
        },
  • ¶

    Clears the viewComparator and follows the same rules for rendering as setComparator.

        removeComparator: function removeComparator(options) {
          return this.setComparator(null, options);
        },
  • ¶

    If viewComparator is overriden it will be returned here. Additionally override this function to provide custom viewComparator logic

        getComparator: function getComparator() {
          if (this.viewComparator) {
            return this.viewComparator;
          }
    
          if (!this.sortWithCollection || this.viewComparator === false || !this.collection) {
            return false;
          }
    
          return this._viewComparator;
        },
  • ¶

    Default internal view comparator that order the views by the order of the collection

        _viewComparator: function _viewComparator(view) {
          return this.collection.indexOf(view.model);
        },
  • ¶

    This method filters the children views and renders the results

        filter: function filter() {
          if (this._isDestroyed) {
            return this;
          }
    
          this._filterChildren();
    
          this._renderChildren();
    
          return this;
        },
        _filterChildren: function _filterChildren() {
          var _this2 = this;
    
          if (!this._children.length) {
            return;
          }
    
          var viewFilter = this._getFilter();
    
          if (!viewFilter) {
            var shouldReset = this.children.length !== this._children.length;
    
            this.children._set(this._children._views, shouldReset);
    
            return;
          } // If children are filtered prevent added to end perf
    
    
          delete this._addedViews;
          this.triggerMethod('before:filter', this);
          var attachViews = [];
          var detachViews = [];
    
          _.each(this._children._views, function (view, key, children) {
            (viewFilter.call(_this2, view, key, children) ? attachViews : detachViews).push(view);
          });
    
          this._detachChildren(detachViews); // reset children
    
    
          this.children._set(attachViews, true);
    
          this.triggerMethod('filter', this, attachViews, detachViews);
        },
  • ¶

    This method returns a function for the viewFilter

        _getFilter: function _getFilter() {
          var viewFilter = this.getFilter();
    
          if (!viewFilter) {
            return false;
          }
    
          if (_.isFunction(viewFilter)) {
            return viewFilter;
          } // Support filter predicates `{ fooFlag: true }`
    
    
          if (_.isObject(viewFilter)) {
            var matcher = _.matches(viewFilter);
    
            return function (view) {
              return matcher(view.model && view.model.attributes);
            };
          } // Filter by model attribute
    
    
          if (_.isString(viewFilter)) {
            return function (view) {
              return view.model && view.model.get(viewFilter);
            };
          }
    
          throw new MarionetteError({
            name: classErrorName$1,
            message: '"viewFilter" must be a function, predicate object literal, a string indicating a model attribute, or falsy',
            url: 'marionette.collectionview.html#defining-the-viewfilter'
          });
        },
  • ¶

    Override this function to provide custom viewFilter logic

        getFilter: function getFilter() {
          return this.viewFilter;
        },
  • ¶

    Sets the view’s viewFilter and applies the filter if the view is ready. To prevent the render pass { preventRender: true } as the 2nd argument.

        setFilter: function setFilter(filter) {
          var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
              preventRender = _ref3.preventRender;
    
          var filterChanged = this.viewFilter !== filter;
          var shouldRender = filterChanged && !preventRender;
          this.viewFilter = filter;
    
          if (shouldRender) {
            this.filter();
          }
    
          return this;
        },
  • ¶

    Clears the viewFilter and follows the same rules for rendering as setFilter.

        removeFilter: function removeFilter(options) {
          return this.setFilter(null, options);
        },
        _detachChildren: function _detachChildren(detachingViews) {
          _.each(detachingViews, this._detachChildView.bind(this));
        },
        _detachChildView: function _detachChildView(view) {
          var shouldTriggerDetach = view._isAttached && this.monitorViewEvents !== false;
    
          if (shouldTriggerDetach) {
            view.triggerMethod('before:detach', view);
          }
    
          this.detachHtml(view);
    
          if (shouldTriggerDetach) {
            view._isAttached = false;
            view.triggerMethod('detach', view);
          }
    
          view._isShown = false;
        },
  • ¶

    Override this method to change how the collectionView detaches a child view

        detachHtml: function detachHtml(view) {
          this.Dom.detachEl(view.el, view.$el);
        },
        _renderChildren: function _renderChildren() {
  • ¶

    If there are unrendered views prevent add to end perf

          if (this._hasUnrenderedViews) {
            delete this._addedViews;
            delete this._hasUnrenderedViews;
          }
    
          var views = this._addedViews || this.children._views;
          this.triggerMethod('before:render:children', this, views);
    
          if (this.isEmpty()) {
            this._showEmptyView();
          } else {
            this._destroyEmptyView();
    
            var els = this._getBuffer(views);
    
            this._attachChildren(els, views);
          }
    
          delete this._addedViews;
          this.triggerMethod('render:children', this, views);
        },
  • ¶

    Renders each view and creates a fragment buffer from them

        _getBuffer: function _getBuffer(views) {
          var _this3 = this;
    
          var elBuffer = this.Dom.createBuffer();
    
          _.each(views, function (view) {
            renderView(view); // corresponds that view is shown in a Region or CollectionView
    
            view._isShown = true;
    
            _this3.Dom.appendContents(elBuffer, view.el, {
              _$contents: view.$el
            });
          });
    
          return elBuffer;
        },
        _attachChildren: function _attachChildren(els, views) {
          var shouldTriggerAttach = this._isAttached && this.monitorViewEvents !== false;
          views = shouldTriggerAttach ? views : [];
    
          _.each(views, function (view) {
            if (view._isAttached) {
              return;
            }
    
            view.triggerMethod('before:attach', view);
          });
    
          this.attachHtml(els, this.$container);
    
          _.each(views, function (view) {
            if (view._isAttached) {
              return;
            }
    
            view._isAttached = true;
            view.triggerMethod('attach', view);
          });
        },
  • ¶

    Override this method to do something other than .append. You can attach any HTML at this point including the els.

        attachHtml: function attachHtml(els, $container) {
          this.Dom.appendContents($container[0], els, {
            _$el: $container
          });
        },
        isEmpty: function isEmpty() {
          return !this.children.length;
        },
        _showEmptyView: function _showEmptyView() {
          var EmptyView = this._getEmptyView();
    
          if (!EmptyView) {
            return;
          }
    
          var options = this._getEmptyViewOptions();
    
          var emptyRegion = this.getEmptyRegion();
          emptyRegion.show(new EmptyView(options));
        },
  • ¶

    Retrieve the empty view class

        _getEmptyView: function _getEmptyView() {
          var emptyView = this.emptyView;
    
          if (!emptyView) {
            return;
          }
    
          return this._getView(emptyView);
        },
  • ¶

    Remove the emptyView

        _destroyEmptyView: function _destroyEmptyView() {
          var emptyRegion = this.getEmptyRegion(); // Only empty if a view is show so the region
  • ¶

    doesn’t detach any other unrelated HTML

          if (emptyRegion.hasView()) {
            emptyRegion.empty();
          }
        },
  • ¶
        _getEmptyViewOptions: function _getEmptyViewOptions() {
          var emptyViewOptions = this.emptyViewOptions || this.childViewOptions;
    
          if (_.isFunction(emptyViewOptions)) {
            return emptyViewOptions.call(this);
          }
    
          return emptyViewOptions;
        },
        swapChildViews: function swapChildViews(view1, view2) {
          if (!this._children.hasView(view1) || !this._children.hasView(view2)) {
            throw new MarionetteError({
              name: classErrorName$1,
              message: 'Both views must be children of the collection view to swap.',
              url: 'marionette.collectionview.html#swapping-child-views'
            });
          }
    
          this._children._swap(view1, view2);
    
          this.Dom.swapEl(view1.el, view2.el); // If the views are not filtered the same, refilter
    
          if (this.children.hasView(view1) !== this.children.hasView(view2)) {
            this.filter();
          } else {
            this.children._swap(view1, view2);
          }
    
          return this;
        },
  • ¶

    Render the child’s view and add it to the HTML for the collection view at a given index, based on the current sort

        addChildView: function addChildView(view, index) {
          var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    
          if (!view || view._isDestroyed) {
            return view;
          }
    
          if (view._isShown) {
            throw new MarionetteError({
              name: classErrorName$1,
              message: 'View is already shown in a Region or CollectionView',
              url: 'marionette.region.html#showing-a-view'
            });
          }
    
          if (_.isObject(index)) {
            options = index;
          } // If options has defined index we should use it
    
    
          if (options.index != null) {
            index = options.index;
          }
    
          if (!this._isRendered) {
            this.render();
          }
    
          this._addChild(view, index);
    
          if (options.preventRender) {
            this._hasUnrenderedViews = true;
            return view;
          }
    
          var hasIndex = typeof index !== 'undefined';
          var isAddedToEnd = !hasIndex || index >= this._children.length; // Only cache views if added to the end and there is no unrendered views
    
          if (isAddedToEnd && !this._hasUnrenderedViews) {
            this._addedViews = [view];
          }
    
          if (hasIndex) {
            this._renderChildren();
          } else {
            this.sort();
          }
    
          return view;
        },
  • ¶

    Detach a view from the children. Best used when adding a childView from addChildView

        detachChildView: function detachChildView(view) {
          this.removeChildView(view, {
            shouldDetach: true
          });
          return view;
        },
  • ¶

    Remove the child view and destroy it. Best used when adding a childView from addChildView The options argument is for internal use only

        removeChildView: function removeChildView(view, options) {
          if (!view) {
            return view;
          }
    
          this._removeChildView(view, options);
    
          this._removeChild(view);
    
          if (this.isEmpty()) {
            this._showEmptyView();
          }
    
          return view;
        },
        _removeChildViews: function _removeChildViews(views) {
          _.each(views, this._removeChildView.bind(this));
        },
        _removeChildView: function _removeChildView(view) {
          var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
              shouldDetach = _ref4.shouldDetach;
    
          view.off('destroy', this.removeChildView, this);
    
          if (shouldDetach) {
            this._detachChildView(view);
          } else {
            this._destroyChildView(view);
          }
    
          this.stopListening(view);
        },
        _destroyChildView: function _destroyChildView(view) {
          if (view._isDestroyed) {
            return;
          }
    
          var shouldDisableEvents = this.monitorViewEvents === false;
          destroyView(view, shouldDisableEvents);
        },
  • ¶

    called by ViewMixin destroy

        _removeChildren: function _removeChildren() {
          this._destroyChildren();
    
          var emptyRegion = this.getEmptyRegion();
          emptyRegion.destroy();
          delete this._addedViews;
        },
  • ¶

    Destroy the child views that this collection view is holding on to, if any

        _destroyChildren: function _destroyChildren() {
          if (!this._children.length) {
            return;
          }
    
          this.triggerMethod('before:destroy:children', this);
    
          if (this.monitorViewEvents === false) {
            this.Dom.detachContents(this.el, this.$el);
          }
    
          this._removeChildViews(this._children._views); // After all children have been destroyed re-init the container
    
    
          this._children._init();
    
          this.children._init();
    
          this.triggerMethod('destroy:children', this);
        }
      }, {
        setDomApi: setDomApi,
        setRenderer: setRenderer
      });
    
      _.extend(CollectionView.prototype, ViewMixin);
  • ¶

    Behavior

      var ClassOptions$4 = ['collectionEvents', 'events', 'modelEvents', 'triggers', 'ui'];
    
      var Behavior = function Behavior(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._setOptions(options, ClassOptions$4);
    
        this.cid = _.uniqueId(this.cidPrefix); // Construct an internal UI hash using the behaviors UI
  • ¶

    hash combined and overridden by 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 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')); // Proxy view triggers
    
        this.listenTo(view, 'all', this.triggerMethod);
        this.initialize.apply(this, arguments);
      };
    
      Behavior.extend = extend; // Behavior Methods
  • ¶

  • ¶
      _.extend(Behavior.prototype, CommonMixin, DelegateEntityEventsMixin, TriggersMixin, UIMixin, {
        cidPrefix: 'mnb',
  • ¶

    This is a noop method intended to be overridden

        initialize: function initialize() {},
  • ¶

    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.

        destroy: function destroy() {
          this.stopListening();
    
          this.view._removeBehavior(this);
    
          this._deleteEntityEventHandlers();
    
          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) {
          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;
    
          if (!this.events) {
            return;
          } // 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 events;
            }
    
            key = getNamespacedEventName(key, _this.cid);
            events[key] = behaviorHandler.bind(_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);
        }
      });
  • ¶

    Application

      var ClassOptions$5 = ['channelName', 'radioEvents', 'radioRequests', 'region', 'regionClass'];
    
      var Application = function Application(options) {
        this._setOptions(options, ClassOptions$5);
    
        this.cid = _.uniqueId(this.cidPrefix);
    
        this._initRegion();
    
        this._initRadio();
    
        this.initialize.apply(this, arguments);
      };
    
      Application.extend = extend; // Application Methods
  • ¶

  • ¶
      _.extend(Application.prototype, CommonMixin, DestroyMixin, RadioMixin, {
        cidPrefix: 'mna',
  • ¶

    This is a noop method intended to be overridden

        initialize: function initialize() {},
  • ¶

    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;
        },
        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 = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
          }
    
          region.show.apply(region, [view].concat(args));
          return view;
        },
        getView: function getView() {
          return this.getRegion().currentView;
        }
      });
    
      var bindEvents$1 = proxy(bindEvents);
      var unbindEvents$1 = proxy(unbindEvents);
      var bindRequests$1 = proxy(bindRequests);
      var unbindRequests$1 = proxy(unbindRequests);
      var mergeOptions$1 = proxy(mergeOptions);
      var getOption$1 = proxy(getOption);
      var normalizeMethods$1 = proxy(normalizeMethods);
      var triggerMethod$1 = proxy(triggerMethod); // Configuration
    
      var setDomApi$1 = function setDomApi(mixin) {
        CollectionView.setDomApi(mixin);
        Region.setDomApi(mixin);
        View.setDomApi(mixin);
      };
      var setRenderer$1 = function setRenderer(renderer) {
        CollectionView.setRenderer(renderer);
        View.setRenderer(renderer);
      };
      var backbone_marionette = {
        View: View,
        CollectionView: CollectionView,
        MnObject: MarionetteObject,
        Object: MarionetteObject,
        Region: Region,
        Behavior: Behavior,
        Application: Application,
        isEnabled: isEnabled,
        setEnabled: setEnabled,
        monitorViewEvents: monitorViewEvents,
        Events: Events,
        extend: extend,
        DomApi: DomApi,
        VERSION: version
      };
    
      exports.Application = Application;
      exports.Behavior = Behavior;
      exports.CollectionView = CollectionView;
      exports.DomApi = DomApi;
      exports.Events = Events;
      exports.MnObject = MarionetteObject;
      exports.Region = Region;
      exports.VERSION = version;
      exports.View = View;
      exports.bindEvents = bindEvents$1;
      exports.bindRequests = bindRequests$1;
      exports.default = backbone_marionette;
      exports.extend = extend;
      exports.getOption = getOption$1;
      exports.isEnabled = isEnabled;
      exports.mergeOptions = mergeOptions$1;
      exports.monitorViewEvents = monitorViewEvents;
      exports.normalizeMethods = normalizeMethods$1;
      exports.setDomApi = setDomApi$1;
      exports.setEnabled = setEnabled;
      exports.setRenderer = setRenderer$1;
      exports.triggerMethod = triggerMethod$1;
      exports.unbindEvents = unbindEvents$1;
      exports.unbindRequests = unbindRequests$1;
    
      Object.defineProperty(exports, '__esModule', { value: true });
    
    }));
    this && this.Marionette && (this.Mn = this.Marionette);
  • ¶

    sourceMappingURL=backbone.marionette.js.map