/
var
/
www
/
barefootlaw.org
/
wp-content
/
plugins
/
pods
/
ui
/
js
/
marionette
/
Upload File
HOME
// MarionetteJS (Backbone.Marionette) // ---------------------------------- // v3.3.1 // // Copyright (c)2017 Derick Bailey, Muted Solutions, LLC. // Distributed under MIT license // // http://marionettejs.com (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone'), require('underscore'), require('backbone.radio')) : typeof define === 'function' && define.amd ? define(['backbone', 'underscore', 'backbone.radio'], factory) : (global.Marionette = global['Mn'] = factory(global.Backbone,global._,global.Backbone.Radio)); }(this, (function (Backbone,_,Radio) { 'use strict'; Backbone = 'default' in Backbone ? Backbone['default'] : Backbone; _ = 'default' in _ ? _['default'] : _; Radio = 'default' in Radio ? Radio['default'] : Radio; var version = "3.3.1"; //Internal utility for creating context style global utils var proxy = function proxy(method) { return function (context) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return method.apply(context, args); }; }; // Marionette.extend // ----------------- // Borrow the Backbone `extend` method so we can use it as needed var extend = Backbone.Model.extend; /* global console */ var deprecate = function deprecate(message, test) { if (_.isObject(message)) { message = message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : ''); } if (!Marionette.DEV_MODE) { return; } if ((test === undefined || !test) && !deprecate._cache[message]) { deprecate._warn('Deprecation warning: ' + message); deprecate._cache[message] = true; } }; deprecate._console = typeof console !== 'undefined' ? console : {}; deprecate._warn = function () { var warn = deprecate._console.warn || deprecate._console.log || _.noop; return warn.apply(deprecate._console, arguments); }; deprecate._cache = {}; // Marionette.isNodeAttached // ------------------------- // Determine if `el` is a child of the document var isNodeAttached = function isNodeAttached(el) { return document.documentElement.contains(el && el.parentNode); }; // Merge `keys` from `options` onto `this` var mergeOptions = function mergeOptions(options, keys) { var _this = this; if (!options) { return; } _.each(keys, function (key) { var option = options[key]; if (option !== undefined) { _this[key] = option; } }); }; // Marionette.getOption // -------------------- // Retrieve an object, function or other value from the // object or its `options`, with `options` taking precedence. var getOption = function getOption(optionName) { if (!optionName) { return; } if (this.options && this.options[optionName] !== undefined) { return this.options[optionName]; } else { return this[optionName]; } }; // Marionette.normalizeMethods // ---------------------- // Pass in a mapping of events => functions or function names // and return a mapping of events => functions var normalizeMethods = function normalizeMethods(hash) { var _this = this; return _.reduce(hash, function (normalizedHash, method, name) { if (!_.isFunction(method)) { method = _this[method]; } if (method) { normalizedHash[name] = method; } return normalizedHash; }, {}); }; // Trigger Method // -------------- // split the event name on the ":" var splitter = /(^|:)(\w)/gi; // take the event section ("section1:section2:section3") // and turn it in to uppercase name onSection1Section2Section3 function getEventName(match, prefix, eventName) { return eventName.toUpperCase(); } var getOnMethodName = _.memoize(function (event) { return 'on' + event.replace(splitter, getEventName); }); // Trigger an event and/or a corresponding method name. Examples: // // `this.triggerMethod("foo")` will trigger the "foo" event and // call the "onFoo" method. // // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and // call the "onFooBar" method. function triggerMethod$1(event) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } // get the method name from the event name var methodName = getOnMethodName(event); var method = getOption.call(this, methodName); var result = void 0; // call the onMethodName if it exists if (_.isFunction(method)) { // pass all args, except the event name result = method.apply(this, args); } // trigger the event this.trigger.apply(this, arguments); return result; } // triggerMethodOn invokes triggerMethod on a specific context // // e.g. `Marionette.triggerMethodOn(view, 'show')` // will trigger a "show" event or invoke onShow the view. function triggerMethodOn(context) { for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } if (_.isFunction(context.triggerMethod)) { return context.triggerMethod.apply(context, args); } return triggerMethod$1.apply(context, args); } // DOM Refresh // ----------- // Trigger method on children unless a pure Backbone.View function triggerMethodChildren(view, event, shouldTrigger) { if (!view._getImmediateChildren) { return; } _.each(view._getImmediateChildren(), function (child) { if (!shouldTrigger(child)) { return; } triggerMethodOn(child, event, child); }); } function shouldTriggerAttach(view) { return !view._isAttached; } function shouldAttach(view) { if (!shouldTriggerAttach(view)) { return false; } view._isAttached = true; return true; } function shouldTriggerDetach(view) { return view._isAttached; } function shouldDetach(view) { if (!shouldTriggerDetach(view)) { return false; } view._isAttached = false; return true; } function triggerDOMRefresh(view) { if (view._isAttached && view._isRendered) { triggerMethodOn(view, 'dom:refresh', view); } } function triggerDOMRemove(view) { if (view._isAttached && view._isRendered) { triggerMethodOn(view, '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) { return; } view._areViewEventsMonitored = true; view.on({ 'before:attach': handleBeforeAttach, 'attach': handleAttach, 'before:detach': handleBeforeDetach, 'detach': handleDetach, 'before:render': handleBeforeRender, 'render': handleRender }); } // Error // ----- var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number']; var MarionetteError = extend.call(Error, { urlRoot: 'http://marionettejs.com/docs/v' + version + '/', constructor: function constructor(message, options) { if (_.isObject(message)) { options = message; message = options.message; } else if (!options) { options = {}; } var error = Error.call(this, message); _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps)); this.captureStackTrace(); if (options.url) { this.url = this.urlRoot + options.url; } }, captureStackTrace: function captureStackTrace() { if (Error.captureStackTrace) { Error.captureStackTrace(this, MarionetteError); } }, toString: function toString() { return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : ''); } }); MarionetteError.extend = extend; // Bind Entity Events & Unbind Entity Events // ----------------------------------------- // // These methods are used to bind/unbind a backbone "entity" (e.g. collection/model) // to methods on a target object. // // The first parameter, `target`, must have the Backbone.Events module mixed in. // // The second parameter is the `entity` (Backbone.Model, Backbone.Collection or // any object that has Backbone.Events mixed in) to bind the events from. // // The third parameter is a hash of { "event:name": "eventHandler" } // configuration. Multiple handlers can be separated by a space. A // function can be supplied instead of a string handler name. // Bind/unbind the event to handlers specified as a string of // handler names on the target object function bindFromStrings(target, entity, evt, methods, actionName) { var methodNames = methods.split(/\s+/); _.each(methodNames, function (methodName) { var method = target[methodName]; if (!method) { throw new MarionetteError('Method "' + methodName + '" was configured as an event handler, but does not exist.'); } target[actionName](entity, evt, method); }); } // generic looping function function iterateEvents(target, entity, bindings, actionName) { if (!entity || !bindings) { return; } // type-check bindings if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', url: 'marionette.functions.html#marionettebindevents' }); } // iterate the bindings and bind/unbind them _.each(bindings, function (method, evt) { // allow for a list of method names as a string if (_.isString(method)) { bindFromStrings(target, entity, evt, method, actionName); return; } target[actionName](entity, evt, method); }); } function bindEvents(entity, bindings) { iterateEvents(this, entity, bindings, 'listenTo'); return this; } function unbindEvents(entity, bindings) { iterateEvents(this, entity, bindings, 'stopListening'); return this; } // Bind/Unbind Radio Requests // ----------------------------------------- // // These methods are used to bind/unbind a backbone.radio request // to methods on a target object. // // The first parameter, `target`, will set the context of the reply method // // The second parameter is the `Radio.channel` to bind the reply to. // // The third parameter is a hash of { "request:name": "replyHandler" } // configuration. A function can be supplied instead of a string handler name. function iterateReplies(target, channel, bindings, actionName) { if (!channel || !bindings) { return; } // type-check bindings if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', url: 'marionette.functions.html#marionettebindrequests' }); } var normalizedRadioRequests = normalizeMethods.call(target, bindings); channel[actionName](normalizedRadioRequests, target); } function bindRequests(channel, bindings) { iterateReplies(this, channel, bindings, 'reply'); return this; } function unbindRequests(channel, bindings) { iterateReplies(this, channel, bindings, 'stopReplying'); return this; } // Internal utility for setting options consistently across Mn var setOptions = function setOptions() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } this.options = _.extend.apply(_, [{}, _.result(this, 'options')].concat(args)); }; var CommonMixin = { // Imports the "normalizeMethods" to transform hashes of // events=>function references/names to a hash of events=>function references normalizeMethods: normalizeMethods, _setOptions: setOptions, // A handy way to merge passed-in options onto the instance mergeOptions: mergeOptions, // Enable getting options from this or this.options by name. getOption: getOption, // Enable binding view's events from another entity. bindEvents: bindEvents, // Enable unbinding view's events from another entity. unbindEvents: unbindEvents }; // MixinOptions // - channelName // - radioEvents // - radioRequests var RadioMixin = { _initRadio: function _initRadio() { var channelName = _.result(this, 'channelName'); if (!channelName) { return; } /* istanbul ignore next */ if (!Radio) { throw new MarionetteError({ name: 'BackboneRadioMissing', message: 'The dependency "backbone.radio" is missing.' }); } var channel = this._channel = Radio.channel(channelName); var radioEvents = _.result(this, 'radioEvents'); this.bindEvents(channel, radioEvents); var radioRequests = _.result(this, 'radioRequests'); this.bindRequests(channel, radioRequests); this.on('destroy', this._destroyRadio); }, _destroyRadio: function _destroyRadio() { this._channel.stopReplying(null, null, this); }, getChannel: function getChannel() { return this._channel; }, // Proxy `bindEvents` bindEvents: bindEvents, // Proxy `unbindEvents` unbindEvents: unbindEvents, // Proxy `bindRequests` bindRequests: bindRequests, // Proxy `unbindRequests` unbindRequests: unbindRequests }; // Object // ------ var ClassOptions = ['channelName', 'radioEvents', 'radioRequests']; // A Base Class that other Classes should descend from. // Object borrows many conventions and utilities from Backbone. var MarionetteObject = function MarionetteObject(options) { this._setOptions(options); this.mergeOptions(options, ClassOptions); this.cid = _.uniqueId(this.cidPrefix); this._initRadio(); this.initialize.apply(this, arguments); }; MarionetteObject.extend = extend; // Object Methods // -------------- // Ensure it can trigger events with Backbone.Events _.extend(MarionetteObject.prototype, Backbone.Events, CommonMixin, RadioMixin, { cidPrefix: 'mno', // for parity with Marionette.AbstractView lifecyle _isDestroyed: false, isDestroyed: function isDestroyed() { return this._isDestroyed; }, //this is a noop method intended to be overridden by classes that extend from this base initialize: function initialize() {}, destroy: function destroy() { if (this._isDestroyed) { return this; } for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } this.triggerMethod.apply(this, ['before:destroy', this].concat(args)); this._isDestroyed = true; this.triggerMethod.apply(this, ['destroy', this].concat(args)); this.stopListening(); return this; }, triggerMethod: triggerMethod$1 }); // DomMixin // --------- var DomMixin = { createBuffer: function createBuffer() { return document.createDocumentFragment(); }, appendChildren: function appendChildren(el, children) { Backbone.$(el).append(children); }, beforeEl: function beforeEl(el, sibling) { Backbone.$(el).before(sibling); }, replaceEl: function replaceEl(newEl, oldEl) { if (newEl === oldEl) { return; } var parent = oldEl.parentNode; if (!parent) { return; } parent.replaceChild(newEl, oldEl); }, detachContents: function detachContents(el) { Backbone.$(el).contents().detach(); }, setInnerContent: function setInnerContent(el, html) { Backbone.$(el).html(html); }, detachEl: function detachEl(el) { Backbone.$(el).detach(); }, removeEl: function removeEl(el) { Backbone.$(el).remove(); }, findEls: function findEls(selector, context) { return Backbone.$(selector, context); } }; // Template Cache // -------------- // Manage templates stored in `<script>` blocks, // caching them for faster access. var TemplateCache = function TemplateCache(templateId) { this.templateId = templateId; }; // TemplateCache object-level methods. Manage the template // caches from these method calls instead of creating // your own TemplateCache instances _.extend(TemplateCache, { templateCaches: {}, // Get the specified template by id. Either // retrieves the cached version, or loads it // from the DOM. get: function get(templateId, options) { var cachedTemplate = this.templateCaches[templateId]; if (!cachedTemplate) { cachedTemplate = new TemplateCache(templateId); this.templateCaches[templateId] = cachedTemplate; } return cachedTemplate.load(options); }, // Clear templates from the cache. If no arguments // are specified, clears all templates: // `clear()` // // If arguments are specified, clears each of the // specified templates from the cache: // `clear("#t1", "#t2", "...")` clear: function clear() { var i = void 0; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var length = args.length; if (length > 0) { for (i = 0; i < length; i++) { delete this.templateCaches[args[i]]; } } else { this.templateCaches = {}; } } }); // TemplateCache instance methods, allowing each // template cache object to manage its own state // and know whether or not it has been loaded _.extend(TemplateCache.prototype, DomMixin, { // Internal method to load the template load: function load(options) { // Guard clause to prevent loading this template more than once if (this.compiledTemplate) { return this.compiledTemplate; } // Load the template and compile it var template = this.loadTemplate(this.templateId, options); this.compiledTemplate = this.compileTemplate(template, options); return this.compiledTemplate; }, // Load a template from the DOM, by default. Override // this method to provide your own template retrieval // For asynchronous loading with AMD/RequireJS, consider // using a template-loader plugin as described here: // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs loadTemplate: function loadTemplate(templateId, options) { var $template = this.findEls(templateId); if (!$template.length) { throw new MarionetteError({ name: 'NoTemplateError', message: 'Could not find template: "' + templateId + '"' }); } return $template.html(); }, // Pre-compile the template before caching it. Override // this method if you do not need to pre-compile a template // (JST / RequireJS for example) or if you want to change // the template engine used (Handebars, etc). compileTemplate: function compileTemplate(rawTemplate, options) { return _.template(rawTemplate, options); } }); // Implementation of the invoke method (http://underscorejs.org/#invoke) with support for // lodash v3, v4, and underscore.js var _invoke = _.invokeMap || _.invoke; // MixinOptions // - behaviors // Takes care of getting the behavior class // given options and a key. // If a user passes in options.behaviorClass // default to using that. // If a user passes in a Behavior Class directly, use that // Otherwise delegate the lookup to the users `behaviorsLookup` implementation. function getBehaviorClass(options, key) { if (options.behaviorClass) { return options.behaviorClass; //treat functions as a Behavior constructor } else if (_.isFunction(options)) { return options; } // behaviorsLookup can be either a flat object or a method if (_.isFunction(Marionette.Behaviors.behaviorsLookup)) { return Marionette.Behaviors.behaviorsLookup(options, key)[key]; } return Marionette.Behaviors.behaviorsLookup[key]; } // Iterate over the behaviors object, for each behavior // instantiate it and get its grouped behaviors. // This accepts a list of behaviors in either an object or array form function parseBehaviors(view, behaviors) { return _.chain(behaviors).map(function (options, key) { var BehaviorClass = getBehaviorClass(options, key); //if we're passed a class directly instead of an object var _options = options === BehaviorClass ? {} : options; var behavior = new BehaviorClass(_options, view); var nestedBehaviors = parseBehaviors(view, _.result(behavior, 'behaviors')); return [behavior].concat(nestedBehaviors); }).flatten().value(); } var BehaviorsMixin = { _initBehaviors: function _initBehaviors() { this._behaviors = this._getBehaviors(); }, _getBehaviors: function _getBehaviors() { var behaviors = _.result(this, 'behaviors'); // Behaviors defined on a view can be a flat object literal // or it can be a function that returns an object. return _.isObject(behaviors) ? parseBehaviors(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() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } // Call destroy on each behavior after // destroying the view. // This unbinds event listeners // that behaviors have registered for. _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(args)); }, // Remove a behavior _removeBehavior: function _removeBehavior(behavior) { // Don't worry about the clean up if the view is destroyed if (this._isDestroyed) { return; } this._behaviors = _.without(this._behaviors, behavior); }, _bindBehaviorUIElements: function _bindBehaviorUIElements() { _invoke(this._behaviors, 'bindUIElements'); }, _unbindBehaviorUIElements: function _unbindBehaviorUIElements() { _invoke(this._behaviors, 'unbindUIElements'); }, _triggerEventOnBehaviors: function _triggerEventOnBehaviors() { var behaviors = this._behaviors; // Use good ol' for as this is a very hot function for (var i = 0, length = behaviors && behaviors.length; i < length; i++) { triggerMethod$1.apply(behaviors[i], arguments); } } }; // MixinOptions // - collectionEvents // - modelEvents var DelegateEntityEventsMixin = { // Handle `modelEvents`, and `collectionEvents` configuration _delegateEntityEvents: function _delegateEntityEvents(model, collection) { this._undelegateEntityEvents(model, collection); var modelEvents = _.result(this, 'modelEvents'); bindEvents.call(this, model, modelEvents); var collectionEvents = _.result(this, 'collectionEvents'); bindEvents.call(this, collection, collectionEvents); }, _undelegateEntityEvents: function _undelegateEntityEvents(model, collection) { var modelEvents = _.result(this, 'modelEvents'); unbindEvents.call(this, model, modelEvents); var collectionEvents = _.result(this, 'collectionEvents'); unbindEvents.call(this, collection, collectionEvents); } }; // Borrow event splitter from Backbone var delegateEventSplitter = /^(\S+)\s*(.*)$/; function uniqueName(eventName, selector) { return [eventName + _.uniqueId('.evt'), selector].join(' '); } // Set event name to be namespaced using a unique index // to generate a non colliding event namespace // http://api.jquery.com/event.namespace/ var getUniqueEventName = function getUniqueEventName(eventName) { var match = eventName.match(delegateEventSplitter); return uniqueName(match[1], match[2]); }; // Add Feature flags here // e.g. 'class' => false var FEATURES = { childViewEventPrefix: true, triggersStopPropagation: true, triggersPreventDefault: true }; function isEnabled(name) { return !!FEATURES[name]; } function setEnabled(name, state) { return FEATURES[name] = state; } // Internal method to create an event handler for a given `triggerDef` like // 'click:foo' function buildViewTrigger(view, triggerDef) { if (_.isString(triggerDef)) { triggerDef = { event: triggerDef }; } var eventName = triggerDef.event; var shouldPreventDefault = !!triggerDef.preventDefault; 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(); } view.triggerMethod(eventName, view, event); }; } var TriggersMixin = { // Configure `triggers` to forward DOM events to view // events. `triggers: {"click .foo": "do:foo"}` _getViewTriggers: function _getViewTriggers(view, triggers) { // Configure the triggers, prevent default // action and stop propagation of DOM events return _.reduce(triggers, function (events, value, key) { key = getUniqueEventName(key); events[key] = buildViewTrigger(view, value); return events; }, {}); } }; // allows for the use of the @ui. syntax within // a given key for triggers and events // swaps the @ui with the associated selector. // Returns a new, non-mutated, parsed events hash. var _normalizeUIKeys = function _normalizeUIKeys(hash, ui) { return _.reduce(hash, function (memo, val, key) { var normalizedKey = _normalizeUIString(key, ui); memo[normalizedKey] = val; return memo; }, {}); }; // utility method for parsing @ui. syntax strings // into associated selector var _normalizeUIString = function _normalizeUIString(uiString, ui) { return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function (r) { return ui[r.slice(4)]; }); }; // allows for the use of the @ui. syntax within // a given value for regions // swaps the @ui with the associated selector var _normalizeUIValues = function _normalizeUIValues(hash, ui, properties) { _.each(hash, function (val, key) { if (_.isString(val)) { hash[key] = _normalizeUIString(val, ui); } else if (_.isObject(val) && _.isArray(properties)) { _.extend(val, _normalizeUIValues(_.pick(val, properties), ui)); /* Value is an object, and we got an array of embedded property names to normalize. */ _.each(properties, function (property) { var propertyVal = val[property]; if (_.isString(propertyVal)) { val[property] = _normalizeUIString(propertyVal, ui); } }); } }); return hash; }; var UIMixin = { // normalize the keys of passed hash with the views `ui` selectors. // `{"@ui.foo": "bar"}` normalizeUIKeys: function normalizeUIKeys(hash) { var uiBindings = this._getUIBindings(); return _normalizeUIKeys(hash, uiBindings); }, // normalize the passed string with the views `ui` selectors. // `"@ui.bar"` normalizeUIString: function normalizeUIString(uiString) { var uiBindings = this._getUIBindings(); return _normalizeUIString(uiString, uiBindings); }, // normalize the values of passed hash with the views `ui` selectors. // `{foo: "@ui.bar"}` normalizeUIValues: function normalizeUIValues(hash, properties) { var uiBindings = this._getUIBindings(); return _normalizeUIValues(hash, uiBindings, properties); }, _getUIBindings: function _getUIBindings() { var uiBindings = _.result(this, '_uiBindings'); var ui = _.result(this, 'ui'); return uiBindings || ui; }, // This method binds the elements specified in the "ui" hash inside the view's code with // the associated jQuery selectors. _bindUIElements: function _bindUIElements() { var _this = this; if (!this.ui) { return; } // store the ui hash in _uiBindings so they can be reset later // and so re-rendering the view will be able to find the bindings if (!this._uiBindings) { this._uiBindings = this.ui; } // get the bindings result, as a function or otherwise var bindings = _.result(this, '_uiBindings'); // empty the ui so we don't have anything to start with this._ui = {}; // bind each of the selectors _.each(bindings, function (selector, key) { _this._ui[key] = _this.$(selector); }); this.ui = this._ui; }, _unbindUIElements: function _unbindUIElements() { var _this2 = this; if (!this.ui || !this._uiBindings) { return; } // delete all of the existing ui bindings _.each(this.ui, function ($el, name) { delete _this2.ui[name]; }); // reset the ui element to the original bindings configuration this.ui = this._uiBindings; delete this._uiBindings; delete this._ui; }, _getUI: function _getUI(name) { return this._ui[name]; } }; // ViewMixin // --------- // MixinOptions // - behaviors // - childViewEventPrefix // - childViewEvents // - childViewTriggers // - collectionEvents // - modelEvents // - triggers // - ui var ViewMixin = { supportsRenderLifecycle: true, supportsDestroyLifecycle: true, _isDestroyed: false, isDestroyed: function isDestroyed() { return !!this._isDestroyed; }, _isRendered: false, isRendered: function isRendered() { return !!this._isRendered; }, _isAttached: false, isAttached: function isAttached() { return !!this._isAttached; }, // Overriding Backbone.View's `delegateEvents` to handle // `events` and `triggers` delegateEvents: function delegateEvents(eventsArg) { this._proxyBehaviorViewProperties(); this._buildEventProxies(); var viewEvents = this._getEvents(eventsArg); if (typeof eventsArg === 'undefined') { this.events = viewEvents; } var combinedEvents = _.extend({}, this._getBehaviorEvents(), viewEvents, this._getBehaviorTriggers(), this.getTriggers()); Backbone.View.prototype.delegateEvents.call(this, combinedEvents); return this; }, _getEvents: function _getEvents(eventsArg) { var events = eventsArg || this.events; if (_.isFunction(events)) { return this.normalizeUIKeys(events.call(this)); } return this.normalizeUIKeys(events); }, // Configure `triggers` to forward DOM events to view // events. `triggers: {"click .foo": "do:foo"}` getTriggers: function getTriggers() { if (!this.triggers) { return; } // Allow `triggers` to be configured as a function var triggers = this.normalizeUIKeys(_.result(this, 'triggers')); // Configure the triggers, prevent default // action and stop propagation of DOM events return this._getViewTriggers(this, triggers); }, // Handle `modelEvents`, and `collectionEvents` configuration delegateEntityEvents: function delegateEntityEvents() { this._delegateEntityEvents(this.model, this.collection); // bind each behaviors model and collection events this._delegateBehaviorEntityEvents(); return this; }, // Handle unbinding `modelEvents`, and `collectionEvents` configuration undelegateEntityEvents: function undelegateEntityEvents() { this._undelegateEntityEvents(this.model, this.collection); // unbind each behaviors model and collection events this._undelegateBehaviorEntityEvents(); return this; }, // Handle destroying the view and its children. destroy: function destroy() { if (this._isDestroyed) { return this; } var shouldTriggerDetach = !!this._isAttached; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } this.triggerMethod.apply(this, ['before:destroy', this].concat(args)); if (shouldTriggerDetach) { this.triggerMethod('before:detach', this); } // unbind UI elements this.unbindUIElements(); // remove the view from the DOM this.removeEl(this.el); 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.apply(this, args); this.triggerMethod.apply(this, ['destroy', this].concat(args)); this.stopListening(); return this; }, bindUIElements: function bindUIElements() { this._bindUIElements(); this._bindBehaviorUIElements(); return this; }, // This method unbinds the elements specified in the "ui" hash unbindUIElements: function unbindUIElements() { this._unbindUIElements(); this._unbindBehaviorUIElements(); return this; }, getUI: function getUI(name) { return this._getUI(name); }, // used as the prefix for child view events // that are forwarded through the layoutview childViewEventPrefix: function childViewEventPrefix() { return isEnabled('childViewEventPrefix') ? 'childview' : false; }, // import the `triggerMethod` to trigger events with corresponding // methods if the method exists triggerMethod: function triggerMethod() { var ret = triggerMethod$1.apply(this, arguments); this._triggerEventOnBehaviors.apply(this, arguments); return ret; }, // Cache `childViewEvents` and `childViewTriggers` _buildEventProxies: function _buildEventProxies() { this._childViewEvents = _.result(this, 'childViewEvents'); this._childViewTriggers = _.result(this, 'childViewTriggers'); }, _proxyChildViewEvents: function _proxyChildViewEvents(view) { this.listenTo(view, 'all', this._childViewEventHandler); }, _childViewEventHandler: function _childViewEventHandler(eventName) { var childViewEvents = this.normalizeMethods(this._childViewEvents); // call collectionView childViewEvent if defined for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) { childViewEvents[eventName].apply(this, args); } // use the parent view's proxyEvent handlers var childViewTriggers = this._childViewTriggers; // Call the event with the proxy name on the parent layout if (childViewTriggers && _.isString(childViewTriggers[eventName])) { this.triggerMethod.apply(this, [childViewTriggers[eventName]].concat(args)); } var prefix = _.result(this, 'childViewEventPrefix'); if (prefix !== false) { var childEventName = prefix + ':' + eventName; this.triggerMethod.apply(this, [childEventName].concat(args)); } } }; _.extend(ViewMixin, DomMixin, BehaviorsMixin, CommonMixin, DelegateEntityEventsMixin, TriggersMixin, UIMixin); function destroyBackboneView(view) { if (!view.supportsDestroyLifecycle) { triggerMethodOn(view, 'before:destroy', view); } var shouldTriggerDetach = !!view._isAttached; if (shouldTriggerDetach) { triggerMethodOn(view, 'before:detach', view); } view.remove(); if (shouldTriggerDetach) { view._isAttached = false; triggerMethodOn(view, 'detach', view); } view._isDestroyed = true; if (!view.supportsDestroyLifecycle) { triggerMethodOn(view, 'destroy', view); } } // Region // ------ var ClassOptions$2 = ['allowMissingEl', 'parentEl', 'replaceElement']; var Region = MarionetteObject.extend({ cidPrefix: 'mnr', replaceElement: false, _isReplaced: false, _isSwappingView: false, constructor: function constructor(options) { this._setOptions(options); this.mergeOptions(options, ClassOptions$2); // getOption necessary because options.el may be passed as undefined this._initEl = this.el = this.getOption('el'); // Handle when this.el is passed in as a $ wrapped element. this.el = this.el instanceof Backbone.$ ? this.el[0] : this.el; if (!this.el) { throw new MarionetteError({ name: 'NoElError', message: 'An "el" must be specified for a region.' }); } this.$el = this.getEl(this.el); MarionetteObject.call(this, options); }, // Displays a backbone view instance inside of the region. Handles calling the `render` // method for you. Reads content directly from the `el` attribute. The `preventDestroy` // option can be used to prevent a view from the old view being destroyed on show. show: function show(view, options) { if (!this._ensureElement(options)) { return; } view = this._getView(view, options); if (view === this.currentView) { return this; } 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 (!view._isAttached) { this.empty(options); } this._setupChildView(view); this._renderView(view); this._attachView(view, options); this.currentView = view; this.triggerMethod('show', this, view, options); this._isSwappingView = false; 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); }, _renderView: function _renderView(view) { if (view._isRendered) { return; } if (!view.supportsRenderLifecycle) { triggerMethodOn(view, 'before:render', view); } view.render(); if (!view.supportsRenderLifecycle) { view._isRendered = true; triggerMethodOn(view, 'render', view); } }, _attachView: function _attachView(view) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var shouldTriggerAttach = !view._isAttached && isNodeAttached(this.el); var shouldReplaceEl = typeof options.replaceElement === 'undefined' ? !!_.result(this, 'replaceElement') : !!options.replaceElement; if (shouldTriggerAttach) { triggerMethodOn(view, 'before:attach', view); } if (shouldReplaceEl) { this._replaceEl(view); } else { this.attachHtml(view); } if (shouldTriggerAttach) { view._isAttached = true; triggerMethodOn(view, 'attach', view); } }, _ensureElement: function _ensureElement() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!_.isObject(this.el)) { this.$el = this.getEl(this.el); this.el = this.$el[0]; } if (!this.$el || this.$el.length === 0) { var allowMissingEl = typeof options.allowMissingEl === 'undefined' ? !!_.result(this, 'allowMissingEl') : !!options.allowMissingEl; if (allowMissingEl) { return false; } else { throw new MarionetteError('An "el" must exist in DOM for this region ' + this.cid); } } return true; }, _getView: function _getView(view) { if (!view) { throw new MarionetteError({ name: 'ViewNotValid', message: 'The view passed is undefined and therefore invalid. You must pass a view instance to show.' }); } if (view._isDestroyed) { throw new MarionetteError({ name: 'ViewDestroyedError', message: 'View (cid: "' + view.cid + '") has already been destroyed and cannot be used.' }); } 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) { return this.findEls(el, _.result(this, 'parentEl')); }, _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.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.appendChildren(this.el, view.el); }, // Destroy the current view, if there is one. If there is no current view, it does // nothing and returns immediately. empty: function empty() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { allowMissingEl: true }; var view = this.currentView; // If there is no view in the region we should only detach current html if (!view) { if (this._ensureElement(options)) { this.detachHtml(); } return this; } var shouldDestroy = !options.preventDestroy; if (!shouldDestroy) { deprecate('The preventDestroy option is deprecated. Use Region#detachView'); } this._empty(view, shouldDestroy); return this; }, _empty: function _empty(view, shouldDestroy) { view.off('destroy', this._empty, this); this.triggerMethod('before:empty', this, view); this._restoreEl(); delete this.currentView; if (!view._isDestroyed) { if (shouldDestroy) { this.removeView(view); } else { this._detachView(view); } this._stopChildViewEvents(view); } this.triggerMethod('empty', this, view); }, _stopChildViewEvents: function _stopChildViewEvents(view) { var parentView = this._parentView; if (!parentView) { return; } this._parentView.stopListening(view); }, destroyView: function destroyView(view) { if (view._isDestroyed) { return view; } if (view.destroy) { view.destroy(); } else { destroyBackboneView(view); } return view; }, 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; var shouldRestoreEl = this._isReplaced; if (shouldTriggerDetach) { triggerMethodOn(view, 'before:detach', view); } if (shouldRestoreEl) { this.replaceEl(this.el, view.el); } else { this.detachHtml(); } if (shouldTriggerDetach) { view._isAttached = false; triggerMethodOn(view, 'detach', view); } }, // Override this method to change how the region detaches current content detachHtml: function detachHtml() { this.detachContents(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); if (this.$el) { this.el = this._initEl; } delete this.$el; return this; }, destroy: function destroy(options) { if (this._isDestroyed) { return this; } this.reset(options); if (this._name) { this._parentView._removeReferences(this._name); } delete this._parentView; delete this._name; return MarionetteObject.prototype.destroy.apply(this, arguments); } }); _.extend(Region.prototype, DomMixin); // return the region instance from the definition var buildRegion = function (definition, defaults) { if (definition instanceof Region) { return definition; } return buildRegionFromDefinition(definition, defaults); }; function buildRegionFromDefinition(definition, defaults) { var opts = _.extend({}, defaults); if (_.isString(definition)) { _.extend(opts, { el: definition }); return buildRegionFromObject(opts); } if (_.isFunction(definition)) { _.extend(opts, { regionClass: definition }); return buildRegionFromObject(opts); } if (_.isObject(definition)) { if (definition.selector) { deprecate('The selector option on a Region definition object is deprecated. Use el to pass a selector string'); } _.extend(opts, { el: definition.selector }, definition); return buildRegionFromObject(opts); } throw new MarionetteError({ message: 'Improper region configuration type.', url: 'marionette.region.html#region-configuration-types' }); } function buildRegionFromObject(definition) { var RegionClass = definition.regionClass; var options = _.omit(definition, 'regionClass'); return new RegionClass(options); } // MixinOptions // - regions // - regionClass var RegionsMixin = { regionClass: Region, // Internal method to initialize the regions that have been defined in a // `regions` attribute on this View. _initRegions: function _initRegions() { // init regions hash this.regions = this.regions || {}; this._regions = {}; this.addRegions(_.result(this, 'regions')); }, // Internal method to re-initialize all of the regions by updating // the `el` that they point to _reInitRegions: function _reInitRegions() { _invoke(this._regions, 'reset'); }, // Add a single region, by name, to the View addRegion: function addRegion(name, definition) { var regions = {}; regions[name] = definition; return this.addRegions(regions)[name]; }, // Add multiple regions as a {name: definition, name2: def2} object literal addRegions: function addRegions(regions) { // If there's nothing to add, stop here. if (_.isEmpty(regions)) { return; } // Normalize region selectors hash to allow // a user to use the @ui. syntax. regions = this.normalizeUIValues(regions, ['selector', 'el']); // Add the regions definitions to the regions property this.regions = _.extend({}, this.regions, regions); return this._addRegions(regions); }, // internal method to build and add regions _addRegions: function _addRegions(regionDefinitions) { var _this = this; var defaults = { regionClass: this.regionClass, parentEl: _.partial(_.result, this, 'el') }; return _.reduce(regionDefinitions, function (regions, definition, name) { regions[name] = buildRegion(definition, defaults); _this._addRegion(regions[name], name); return regions; }, {}); }, _addRegion: function _addRegion(region, name) { this.triggerMethod('before:add:region', this, name, region); region._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, _.bind(this._removeRegion, 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]; }, // Get all regions _getRegions: function _getRegions() { return _.clone(this._regions); }, getRegions: function getRegions() { if (!this._isRendered) { this.render(); } return this._getRegions(); }, showChildView: function showChildView(name, view) { var region = this.getRegion(name); for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } return region.show.apply(region, [view].concat(args)); }, detachChildView: function detachChildView(name) { return this.getRegion(name).detachView(); }, getChildView: function getChildView(name) { return this.getRegion(name).currentView; } }; // Renderer // -------- // Render a template with data by passing in the template // selector and the data to render. var Renderer = { // Render a template with data. The `template` parameter is // passed to the `TemplateCache` object to retrieve the // template function. Override this method to provide your own // custom rendering and template handling for all of Marionette. render: function render(template, data) { if (!template) { throw new MarionetteError({ name: 'TemplateNotFoundError', message: 'Cannot render the template since its false, null or undefined.' }); } var templateFunc = _.isFunction(template) ? template : TemplateCache.get(template); return templateFunc(data); } }; // View // --------- var ClassOptions$1 = ['behaviors', 'childViewEventPrefix', 'childViewEvents', 'childViewTriggers', 'collectionEvents', 'events', 'modelEvents', 'regionClass', 'regions', 'template', 'templateContext', 'triggers', 'ui']; // The standard view. Includes view events, automatic rendering // of Underscore templates, nested views, and more. var View = Backbone.View.extend({ constructor: function constructor(options) { this.render = _.bind(this.render, this); this._setOptions(options); this.mergeOptions(options, ClassOptions$1); monitorViewEvents(this); this._initBehaviors(); this._initRegions(); var args = Array.prototype.slice.call(arguments); args[0] = this.options; Backbone.View.prototype.constructor.apply(this, args); this.delegateEntityEvents(); this._triggerEventOnBehaviors('initialize', this); }, // Serialize the view's model *or* collection, if // it exists, for the template serializeData: function serializeData() { if (!this.model && !this.collection) { return {}; } // If we have a model, we serialize that if (this.model) { return this.serializeModel(); } // Otherwise, we serialize the collection, // making it available under the `items` property return { items: this.serializeCollection() }; }, // Prepares the special `model` property of a view // for being displayed in the template. By default // we simply clone the attributes. Override this if // you need a custom transformation for your view's model serializeModel: function serializeModel() { if (!this.model) { return {}; } return _.clone(this.model.attributes); }, // Serialize a collection by cloning each of // its model's attributes serializeCollection: function serializeCollection() { if (!this.collection) { return {}; } return this.collection.map(function (model) { return _.clone(model.attributes); }); }, // Overriding Backbone.View's `setElement` to handle // if an el was previously defined. If so, the view might be // rendered or attached on setElement. setElement: function setElement() { var hasEl = !!this.el; Backbone.View.prototype.setElement.apply(this, arguments); if (hasEl) { this._isRendered = !!this.$el.length; this._isAttached = isNodeAttached(this.el); } if (this._isRendered) { this.bindUIElements(); } return this; }, // Render the view, defaulting to underscore.js templates. // You can override this in your view definition to provide // a very specific rendering for your view. In general, though, // you should override the `Marionette.Renderer` object to // change how Marionette renders views. // Subsequent renders after the first will re-render all nested // views. render: function render() { if (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(); this.bindUIElements(); this._isRendered = true; this.triggerMethod('render', this); return this; }, // Internal method to render the template with the serialized data // and template context via the `Marionette.Renderer` object. _renderTemplate: function _renderTemplate() { var template = this.getTemplate(); // Allow template-less views if (template === false) { deprecate('template:false is deprecated. Use _.noop.'); return; } // Add in entity data and template context var data = this.mixinTemplateContext(this.serializeData()); // Render and add to el var html = this._renderHtml(template, data); this.attachElContent(html); }, // Renders the data into the template _renderHtml: function _renderHtml(template, data) { return Renderer.render(template, data, this); }, // Get the template for this view // instance. You can set a `template` attribute in the view // definition or pass a `template: "whatever"` parameter in // to the constructor options. getTemplate: function getTemplate() { return this.template; }, // Mix in template context methods. Looks for a // `templateContext` attribute, which can either be an // object literal, or a function that returns an object // literal. All methods and attributes from this object // are copies to the object passed in. mixinTemplateContext: function mixinTemplateContext() { var target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var templateContext = _.result(this, 'templateContext'); return _.extend(target, templateContext); }, // Attaches the content of a given view. // This method can be overridden to optimize rendering, // or to render in a non standard way. // // For example, using `innerHTML` instead of `$el.html` // // ```js // attachElContent(html) { // this.el.innerHTML = html; // return this; // } // ``` attachElContent: function attachElContent(html) { this.setInnerContent(this.el, html); return this; }, // called by ViewMixin destroy _removeChildren: function _removeChildren() { this.removeRegions(); }, _getImmediateChildren: function _getImmediateChildren() { return _.chain(this._getRegions()).map('currentView').compact().value(); } }, { // Sets the renderer for the Marionette.View class setRenderer: function setRenderer(renderer) { this.prototype._renderHtml = renderer; } }); _.extend(View.prototype, ViewMixin, RegionsMixin); // 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']; var emulateCollection = function emulateCollection(object, listProperty) { _.each(methods, function (method) { object[method] = function () { var list = _.values(_.result(this, listProperty)); var args = [list].concat(_.toArray(arguments)); return _[method].apply(_, args); }; }); }; // Provide a container to store, retrieve and // shut down child views. var Container = function Container(views) { this._views = {}; this._indexByModel = {}; this._indexByCustom = {}; this._updateLength(); _.each(views, _.bind(this.add, this)); }; emulateCollection(Container.prototype, '_views'); // Container Methods // ----------------- _.extend(Container.prototype, { // Add a view to this container. Stores the view // by `cid` and makes it searchable by the model // cid (and model itself). Optionally specify // a custom key to store an retrieve the view. add: function add(view, customIndex) { return this._add(view, customIndex)._updateLength(); }, // To be used when avoiding call _updateLength // When you are done adding all your new views // call _updateLength _add: function _add(view, customIndex) { var viewCid = view.cid; // store the view this._views[viewCid] = view; // index it by model if (view.model) { this._indexByModel[view.model.cid] = viewCid; } // index by custom if (customIndex) { this._indexByCustom[customIndex] = viewCid; } return this; }, // Find a view by the model that was attached to // it. Uses the model's `cid` to find it. findByModel: function findByModel(model) { return this.findByModelCid(model.cid); }, // Find a view by the `cid` of the model that was attached to // it. Uses the model's `cid` to find the view `cid` and // retrieve the view using it. findByModelCid: function findByModelCid(modelCid) { var viewCid = this._indexByModel[modelCid]; return this.findByCid(viewCid); }, // Find a view by a custom indexer. findByCustom: function findByCustom(index) { var viewCid = this._indexByCustom[index]; return this.findByCid(viewCid); }, // Find by index. This is not guaranteed to be a // stable index. findByIndex: function findByIndex(index) { return _.values(this._views)[index]; }, // retrieve a view by its `cid` directly findByCid: function findByCid(cid) { return this._views[cid]; }, // Remove a view remove: function remove(view) { return this._remove(view)._updateLength(); }, // To be used when avoiding call _updateLength // When you are done adding all your new views // call _updateLength _remove: function _remove(view) { var viewCid = view.cid; // delete model index if (view.model) { delete this._indexByModel[view.model.cid]; } // delete custom index _.some(this._indexByCustom, _.bind(function (cid, key) { if (cid === viewCid) { delete this._indexByCustom[key]; return true; } }, this)); // remove the view from the container delete this._views[viewCid]; return this; }, // Update the `.length` attribute on this container _updateLength: function _updateLength() { this.length = _.size(this._views); return this; } }); // Collection View // --------------- var ClassOptions$3 = ['behaviors', 'childView', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'events', 'filter', 'emptyView', 'emptyViewOptions', 'modelEvents', 'reorderOnSort', 'sort', 'triggers', 'ui', 'viewComparator']; // A view that iterates over a Backbone.Collection // and renders an individual child view for each model. var CollectionView = Backbone.View.extend({ // flag for maintaining the sorted order of the collection sort: true, // constructor // option to pass `{sort: false}` to prevent the `CollectionView` from // maintaining the sorted order of the collection. // This will fallback onto appending childView's to the end. // // option to pass `{viewComparator: compFunction()}` to allow the `CollectionView` // to use a custom sort order for the collection. constructor: function constructor(options) { this.render = _.bind(this.render, this); this._setOptions(options); this.mergeOptions(options, ClassOptions$3); monitorViewEvents(this); this._initBehaviors(); this.once('render', this._initialEvents); this._initChildViewStorage(); this._bufferedChildren = []; var args = Array.prototype.slice.call(arguments); args[0] = this.options; Backbone.View.prototype.constructor.apply(this, args); this.delegateEntityEvents(); this._triggerEventOnBehaviors('initialize', this); }, // Instead of inserting elements one by one into the page, it's much more performant to insert // elements into a document fragment and then insert that document fragment into the page _startBuffering: function _startBuffering() { this._isBuffering = true; }, _endBuffering: function _endBuffering() { var shouldTriggerAttach = !!this._isAttached; var triggerOnChildren = shouldTriggerAttach ? this._getImmediateChildren() : []; this._isBuffering = false; _.each(triggerOnChildren, function (child) { triggerMethodOn(child, 'before:attach', child); }); this.attachBuffer(this, this._createBuffer()); _.each(triggerOnChildren, function (child) { child._isAttached = true; triggerMethodOn(child, 'attach', child); }); this._bufferedChildren = []; }, _getImmediateChildren: function _getImmediateChildren() { return _.values(this.children._views); }, // Configured the initial events that the collection view binds to. _initialEvents: function _initialEvents() { if (this.collection) { this.listenTo(this.collection, 'add', this._onCollectionAdd); this.listenTo(this.collection, 'update', this._onCollectionUpdate); this.listenTo(this.collection, 'reset', this.render); if (this.sort) { this.listenTo(this.collection, 'sort', this._sortViews); } } }, // Handle a child added to the collection _onCollectionAdd: function _onCollectionAdd(child, collection, opts) { // `index` is present when adding with `at` since BB 1.2; indexOf fallback for < 1.2 var index = opts.at !== undefined && (opts.index || collection.indexOf(child)); // When filtered or when there is no initial index, calculate index. if (this.filter || index === false) { index = _.indexOf(this._filteredSortedModels(index), child); } if (this._shouldAddChild(child, index)) { this._destroyEmptyView(); this._addChild(child, index); } }, // Handle collection update model removals _onCollectionUpdate: function _onCollectionUpdate(collection, options) { var changes = options.changes; this._removeChildModels(changes.removed); }, // Remove the child views and destroy them. // This function also updates the indices of later views // in the collection in order to keep the children in sync with the collection. // "models" is an array of models and the corresponding views // will be removed and destroyed from the CollectionView _removeChildModels: function _removeChildModels(models) { // Used to determine where to update the remaining // sibling view indices after these views are removed. var removedViews = this._getRemovedViews(models); if (!removedViews.length) { return; } this.children._updateLength(); // decrement the index of views after this one this._updateIndices(removedViews, false); if (this.isEmpty()) { this._showEmptyView(); } }, // Returns the views that will be used for re-indexing // through CollectionView#_updateIndices. _getRemovedViews: function _getRemovedViews(models) { var _this = this; // Returning a view means something was removed. return _.reduce(models, function (removingViews, model) { var view = model && _this.children.findByModel(model); if (!view || view._isDestroyed) { return removingViews; } _this._removeChildView(view); removingViews.push(view); return removingViews; }, []); }, _removeChildView: function _removeChildView(view) { this.triggerMethod('before:remove:child', this, view); this.children._remove(view); if (view.destroy) { view.destroy(); } else { destroyBackboneView(view); } this.stopListening(view); this.triggerMethod('remove:child', this, view); }, // Overriding Backbone.View's `setElement` to handle // if an el was previously defined. If so, the view might be // attached on setElement. setElement: function setElement() { var hasEl = !!this.el; Backbone.View.prototype.setElement.apply(this, arguments); if (hasEl) { this._isAttached = isNodeAttached(this.el); } return this; }, // Render children views. Override this method to provide your own implementation of a // render function for the collection view. render: function render() { if (this._isDestroyed) { return this; } this.triggerMethod('before:render', this); this._renderChildren(); this._isRendered = true; this.triggerMethod('render', this); return this; }, // An efficient rendering used for filtering. Instead of modifying the whole DOM for the // collection view, we are only adding or removing the related childrenViews. setFilter: function setFilter(filter) { var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, preventRender = _ref.preventRender; var canBeRendered = this._isRendered && !this._isDestroyed; var filterChanged = this.filter !== filter; var shouldRender = canBeRendered && filterChanged && !preventRender; if (shouldRender) { var previousModels = this._filteredSortedModels(); this.filter = filter; var models = this._filteredSortedModels(); this._applyModelDeltas(models, previousModels); } else { this.filter = filter; } return this; }, // `removeFilter` is actually an alias for removing filters. removeFilter: function removeFilter(options) { return this.setFilter(null, options); }, // Calculate and apply difference by cid between `models` and `previousModels`. _applyModelDeltas: function _applyModelDeltas(models, previousModels) { var _this2 = this; var currentIds = {}; _.each(models, function (model, index) { var addedChildNotExists = !_this2.children.findByModel(model); if (addedChildNotExists) { _this2._onCollectionAdd(model, _this2.collection, { at: index }); } currentIds[model.cid] = true; }); var removeModels = _.filter(previousModels, function (prevModel) { return !currentIds[prevModel.cid] && _this2.children.findByModel(prevModel); }); this._removeChildModels(removeModels); }, // Reorder DOM after sorting. When your element's rendering do not use their index, // you can pass reorderOnSort: true to only reorder the DOM after a sort instead of // rendering all the collectionView. reorder: function reorder() { var children = this.children; var models = this._filteredSortedModels(); if (!models.length && this._showingEmptyView) { return this; } var anyModelsAdded = _.some(models, function (model) { return !children.findByModel(model); }); // If there are any new models added due to filtering we need to add child views, // so render as normal. if (anyModelsAdded) { this.render(); } else { var filteredOutModels = []; // Get the DOM nodes in the same order as the models and // find the model that were children before but aren't in this new order. var elsToReorder = children.reduce(function (viewEls, view) { var index = _.indexOf(models, view.model); if (index === -1) { filteredOutModels.push(view.model); return viewEls; } view._index = index; viewEls[index] = view.el; return viewEls; }, new Array(models.length)); this.triggerMethod('before:reorder', this); // Since append moves elements that are already in the DOM, appending the elements // will effectively reorder them. this._appendReorderedChildren(elsToReorder); // remove any views that have been filtered out this._removeChildModels(filteredOutModels); this.triggerMethod('reorder', this); } return this; }, // Render view after sorting. Override this method to change how the view renders // after a `sort` on the collection. resortView: function resortView() { if (this.reorderOnSort) { this.reorder(); } else { this._renderChildren(); } return this; }, // Internal method. This checks for any changes in the order of the collection. // If the index of any view doesn't match, it will render. _sortViews: function _sortViews() { var _this3 = this; var models = this._filteredSortedModels(); // check for any changes in sort order of views var orderChanged = _.find(models, function (item, index) { var view = _this3.children.findByModel(item); return !view || view._index !== index; }); if (orderChanged) { this.resortView(); } }, // Internal reference to what index a `emptyView` is. _emptyViewIndex: -1, // Internal method. Separated so that CompositeView can append to the childViewContainer // if necessary _appendReorderedChildren: function _appendReorderedChildren(children) { this.appendChildren(this.el, children); }, // Internal method. Separated so that CompositeView can have more control over events // being triggered, around the rendering process _renderChildren: function _renderChildren() { if (this._isRendered) { this._destroyEmptyView(); this._destroyChildren(); } var models = this._filteredSortedModels(); if (this.isEmpty({ processedModels: models })) { this._showEmptyView(); } else { this.triggerMethod('before:render:children', this); this._startBuffering(); this._showCollection(models); this._endBuffering(); this.triggerMethod('render:children', this); } }, _createView: function _createView(model, index) { var ChildView = this._getChildView(model); var childViewOptions = this._getChildViewOptions(model, index); var view = this.buildChildView(model, ChildView, childViewOptions); return view; }, _setupChildView: function _setupChildView(view, index) { monitorViewEvents(view); // set up the child view event forwarding this._proxyChildViewEvents(view); if (this.sort) { view._index = index; } }, // Internal method to loop through collection and show each child view. _showCollection: function _showCollection(models) { _.each(models, _.bind(this._addChild, this)); this.children._updateLength(); }, // Allow the collection to be sorted by a custom view comparator _filteredSortedModels: function _filteredSortedModels(addedAt) { if (!this.collection || !this.collection.length) { return []; } var viewComparator = this.getViewComparator(); var models = this.collection.models; addedAt = Math.min(Math.max(addedAt, 0), models.length - 1); if (viewComparator) { var addedModel = void 0; // Preserve `at` location, even for a sorted view if (addedAt) { addedModel = models[addedAt]; models = models.slice(0, addedAt).concat(models.slice(addedAt + 1)); } models = this._sortModelsBy(models, viewComparator); if (addedModel) { models.splice(addedAt, 0, addedModel); } } // Filter after sorting in case the filter uses the index models = this._filterModels(models); return models; }, getViewComparator: function getViewComparator() { return this.viewComparator; }, // Filter an array of models, if a filter exists _filterModels: function _filterModels(models) { var _this4 = this; if (this.filter) { models = _.filter(models, function (model, index) { return _this4._shouldAddChild(model, index); }); } return models; }, _sortModelsBy: function _sortModelsBy(models, comparator) { if (typeof comparator === 'string') { return _.sortBy(models, function (model) { return model.get(comparator); }); } else if (comparator.length === 1) { return _.sortBy(models, _.bind(comparator, this)); } else { return _.clone(models).sort(_.bind(comparator, this)); } }, // Internal method to show an empty view in place of a collection of child views, // when the collection is empty _showEmptyView: function _showEmptyView() { var EmptyView = this._getEmptyView(); if (EmptyView && !this._showingEmptyView) { this._showingEmptyView = true; var model = new Backbone.Model(); var emptyViewOptions = this.emptyViewOptions || this.childViewOptions; if (_.isFunction(emptyViewOptions)) { emptyViewOptions = emptyViewOptions.call(this, model, this._emptyViewIndex); } var view = this.buildChildView(model, EmptyView, emptyViewOptions); this.triggerMethod('before:render:empty', this, view); this.addChildView(view, 0); this.triggerMethod('render:empty', this, view); } }, // Internal method to destroy an existing emptyView instance if one exists. Called when // a collection view has been rendered empty, and then a child is added to the collection. _destroyEmptyView: function _destroyEmptyView() { if (this._showingEmptyView) { this.triggerMethod('before:remove:empty', this); this._destroyChildren(); delete this._showingEmptyView; this.triggerMethod('remove:empty', this); } }, // Retrieve the empty view class _getEmptyView: function _getEmptyView() { var emptyView = this.emptyView; if (!emptyView) { return; } return this._getView(emptyView); }, // Retrieve the `childView` class // The `childView` property can be either a view class or a function that // returns a view class. If it is a function, it will receive the model that // will be passed to the view instance (created from the returned view class) _getChildView: function _getChildView(child) { var childView = this.childView; if (!childView) { throw new MarionetteError({ name: 'NoChildViewError', message: 'A "childView" must be specified' }); } childView = this._getView(childView, child); if (!childView) { throw new MarionetteError({ name: 'InvalidChildViewError', message: '"childView" must be a view class or a function that returns a view class' }); } return childView; }, // First check if the `view` is a view class (the common case) // Then check if it's a function (which we assume that returns a view class) _getView: function _getView(view, child) { if (view.prototype instanceof Backbone.View || view === Backbone.View) { return view; } else if (_.isFunction(view)) { return view.call(this, child); } }, // Internal method for building and adding a child view _addChild: function _addChild(child, index) { var view = this._createView(child, index); this.addChildView(view, index); return view; }, _getChildViewOptions: function _getChildViewOptions(child, index) { if (_.isFunction(this.childViewOptions)) { return this.childViewOptions(child, index); } return this.childViewOptions; }, // Render the child's view and add it to the HTML for the collection view at a given index. // This will also update the indices of later views in the collection in order to keep the // children in sync with the collection. addChildView: function addChildView(view, index) { this.triggerMethod('before:add:child', this, view); this._setupChildView(view, index); // Store the child view itself so we can properly remove and/or destroy it later if (this._isBuffering) { // Add to children, but don't update children's length. this.children._add(view); } else { // increment indices of views after this one this._updateIndices(view, true); this.children.add(view); } this._renderView(view); this._attachView(view, index); this.triggerMethod('add:child', this, view); return view; }, // Internal method. This decrements or increments the indices of views after the added/removed // view to keep in sync with the collection. _updateIndices: function _updateIndices(views, increment) { if (!this.sort) { return; } if (!increment) { _.each(_.sortBy(this.children._views, '_index'), function (view, index) { view._index = index; }); return; } var view = _.isArray(views) ? _.max(views, '_index') : views; if (_.isObject(view)) { // update the indexes of views after this one this.children.each(function (laterView) { if (laterView._index >= view._index) { laterView._index += 1; } }); } }, _renderView: function _renderView(view) { if (view._isRendered) { return; } if (!view.supportsRenderLifecycle) { triggerMethodOn(view, 'before:render', view); } view.render(); if (!view.supportsRenderLifecycle) { view._isRendered = true; triggerMethodOn(view, 'render', view); } }, _attachView: function _attachView(view, index) { // Only trigger attach if already attached and not buffering, // otherwise _endBuffering() or Region#show() handles this. var shouldTriggerAttach = !view._isAttached && !this._isBuffering && this._isAttached; if (shouldTriggerAttach) { triggerMethodOn(view, 'before:attach', view); } this.attachHtml(this, view, index); if (shouldTriggerAttach) { view._isAttached = true; triggerMethodOn(view, 'attach', view); } }, // Build a `childView` for a model in the collection. buildChildView: function buildChildView(child, ChildViewClass, childViewOptions) { var options = _.extend({ model: child }, childViewOptions); return new ChildViewClass(options); }, // Remove the child view and destroy it. This function also updates the indices of later views // in the collection in order to keep the children in sync with the collection. removeChildView: function removeChildView(view) { if (!view || view._isDestroyed) { return view; } this._removeChildView(view); this.children._updateLength(); // decrement the index of views after this one this._updateIndices(view, false); return view; }, // check if the collection is empty or optionally whether an array of pre-processed models is empty isEmpty: function isEmpty(options) { var models = void 0; if (_.result(options, 'processedModels')) { models = options.processedModels; } else { models = this.collection ? this.collection.models : []; models = this._filterModels(models); } return models.length === 0; }, // You might need to override this if you've overridden attachHtml attachBuffer: function attachBuffer(collectionView, buffer) { this.appendChildren(collectionView.el, buffer); }, // Create a fragment buffer from the currently buffered children _createBuffer: function _createBuffer() { var _this5 = this; var elBuffer = this.createBuffer(); _.each(this._bufferedChildren, function (b) { _this5.appendChildren(elBuffer, b.el); }); return elBuffer; }, // Append the HTML to the collection's `el`. Override this method to do something other // than `.append`. attachHtml: function attachHtml(collectionView, childView, index) { if (collectionView._isBuffering) { // buffering happens on reset events and initial renders // in order to reduce the number of inserts into the // document, which are expensive. collectionView._bufferedChildren.splice(index, 0, childView); } else { // If we've already rendered the main collection, append // the new child into the correct order if we need to. Otherwise // append to the end. if (!collectionView._insertBefore(childView, index)) { collectionView._insertAfter(childView); } } }, // Internal method. Check whether we need to insert the view into the correct position. _insertBefore: function _insertBefore(childView, index) { var currentView = void 0; var findPosition = this.sort && index < this.children.length - 1; if (findPosition) { // Find the view after this one currentView = this.children.find(function (view) { return view._index === index + 1; }); } if (currentView) { this.beforeEl(currentView.el, childView.el); return true; } return false; }, // Internal method. Append a view to the end of the $el _insertAfter: function _insertAfter(childView) { this.appendChildren(this.el, childView.el); }, // Internal method to set up the `children` object for storing all of the child views _initChildViewStorage: function _initChildViewStorage() { this.children = new Container(); }, // called by ViewMixin destroy _removeChildren: function _removeChildren() { this._destroyChildren(); }, // Destroy the child views that this collection view is holding on to, if any _destroyChildren: function _destroyChildren(options) { if (!this.children.length) { return; } this.triggerMethod('before:destroy:children', this); this.children.each(_.bind(this._removeChildView, this)); this.children._updateLength(); this.triggerMethod('destroy:children', this); }, // Return true if the given child should be shown. Return false otherwise. // The filter will be passed (child, index, collection), where // 'child' is the given model // 'index' is the index of that model in the collection // 'collection' is the collection referenced by this CollectionView _shouldAddChild: function _shouldAddChild(child, index) { var filter = this.filter; return !_.isFunction(filter) || filter.call(this, child, index, this.collection); } }); _.extend(CollectionView.prototype, ViewMixin); // Provide a container to store, retrieve and // shut down child views. var Container$1 = function Container$1() { this._init(); }; emulateCollection(Container$1.prototype, '_views'); function stringComparator(comparator, view) { return view.model && view.model.get(comparator); } // Container Methods // ----------------- _.extend(Container$1.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; var viewCid = view.cid; // store the view this._viewsByCid[viewCid] = view; // index it by model if (view.model) { this._indexByModel[view.model.cid] = viewCid; } // add to end by default this._views.splice(index, 0, view); this._updateLength(); }, // Sort (mutate) and return the array of the child views. _sort: function _sort(comparator) { if (typeof comparator === 'string') { comparator = _.partial(stringComparator, comparator); return this._sortBy(comparator); } if (comparator.length === 1) { return this._sortBy(comparator); } return this._views.sort(comparator); }, // 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. _set: function _set(views) { this._views.length = 0; this._views.push.apply(this._views, views.slice(0)); this._updateLength(); }, // Find a view by the model that was attached to it. // Uses the model's `cid` to find it. findByModel: function findByModel(model) { return this.findByModelCid(model.cid); }, // Find a view by the `cid` of the model that was attached to it. // Uses the model's `cid` to find the view `cid` and // retrieve the view using it. findByModelCid: function findByModelCid(modelCid) { var viewCid = this._indexByModel[modelCid]; return this.findByCid(viewCid); }, // Find a view by 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]; }, // 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; } }); // Next Collection View // --------------- var ClassOptions$4 = ['behaviors', 'childView', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'emptyView', 'emptyViewOptions', 'events', 'modelEvents', 'sortWithCollection', 'triggers', 'ui', 'viewComparator', 'viewFilter']; // A view that iterates over a Backbone.Collection // and renders an individual child view for each model. var CollectionView$2 = Backbone.View.extend({ // flag for maintaining the sorted order of the collection sortWithCollection: true, // constructor constructor: function constructor(options) { this._setOptions(options); this.mergeOptions(options, ClassOptions$4); monitorViewEvents(this); this.once('render', this._initialEvents); // This children container isn't really used by a render, but it provides // the ability to check `this.children.length` prior to rendering // It also allows for cases where only addChildView is used this._initChildViewStorage(); this._initBehaviors(); var args = Array.prototype.slice.call(arguments); args[0] = this.options; Backbone.View.prototype.constructor.apply(this, args); this._initEmptyRegion(); this.delegateEntityEvents(); this._triggerEventOnBehaviors('initialize', this); }, // Internal method to set up the `children` object for storing all of the child views _initChildViewStorage: function _initChildViewStorage() { this.children = new Container$1(); }, // Create an region to show the emptyView _initEmptyRegion: function _initEmptyRegion() { this.emptyRegion = new Region({ el: this.el }); this.emptyRegion._parentView = this; }, // Configured the initial events that the collection view binds to. _initialEvents: function _initialEvents() { 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() { var _this = this; if (!this.sortWithCollection) { return; } // If the data is changing we will handle the sort later if (this.collection.length !== this.children.length) { return; } // Additional check if the data is changing var hasAddedModel = this.collection.some(function (model) { return !_this.children.findByModel(model); }); if (hasAddedModel) { return; } // If the only thing happening here is sorting, sort. this.sort(); }, _onCollectionReset: function _onCollectionReset() { this.render(); }, // 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 = this._removeChildModels(changes.removed); this._addChildModels(changes.added); this._detachChildren(removedViews); this._showChildren(); // Destroy removed child views after all of the render is complete this._removeChildViews(removedViews); }, _removeChildModels: function _removeChildModels(models) { return _.map(models, _.bind(this._removeChildModel, this)); }, _removeChildModel: function _removeChildModel(model) { var view = this.children.findByModel(model); this._removeChild(view); return view; }, _removeChild: function _removeChild(view) { this.triggerMethod('before:remove:child', this, 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, _.bind(this._addChildModel, 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.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: 'NoChildViewError', message: 'A "childView" must be specified' }); } childView = this._getView(childView, child); if (!childView) { throw new MarionetteError({ name: 'InvalidChildViewError', message: '"childView" must be a view class or a function that returns a view class' }); } return childView; }, // First check if the `view` is a view class (the common case) // Then check if it's a function (which we assume that returns a view class) _getView: function _getView(view, child) { if (view.prototype instanceof Backbone.View || view === Backbone.View) { return view; } else if (_.isFunction(view)) { return view.call(this, child); } }, _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() { var hasEl = !!this.el; Backbone.View.prototype.setElement.apply(this, arguments); if (hasEl) { this._isAttached = isNodeAttached(this.el); } return this; }, // Render children views. render: function render() { if (this._isDestroyed) { return this; } this.triggerMethod('before:render', this); this._destroyChildren(); // After all children have been destroyed re-init the container this.children._init(); if (this.collection) { this._addChildModels(this.collection.models); } this._showChildren(); this._isRendered = true; this.triggerMethod('render', this); return this; }, // Sorts the children then filters and renders the results. sort: function sort() { if (this._isDestroyed) { return this; } if (!this.children.length) { return this; } this._showChildren(); return this; }, _showChildren: function _showChildren() { if (this.isEmpty()) { this._showEmptyView(); return; } this._sortChildren(); this.filter(); }, // Returns true if the collectionView is considered empty. // This is called twice during a render. Once to check the data, // and again when views are filtered. Override this function to // customize what empty means. isEmpty: function isEmpty(allViewsFiltered) { return allViewsFiltered || !this.children.length; }, _showEmptyView: function _showEmptyView() { var EmptyView = this._getEmptyView(); if (!EmptyView) { return; } var options = this._getEmptyViewOptions(); this.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() { // Only empty if a view is show so the region // doesn't detach any other unrelated HTML if (this.emptyRegion.hasView()) { this.emptyRegion.empty(); } }, // _getEmptyViewOptions: function _getEmptyViewOptions() { var emptyViewOptions = this.emptyViewOptions || this.childViewOptions; if (_.isFunction(emptyViewOptions)) { return emptyViewOptions.call(this); } return emptyViewOptions; }, // Sorts views by viewComparator and sets the children to the new order _sortChildren: function _sortChildren() { this.triggerMethod('before:sort', this); var viewComparator = this.getComparator(); if (_.isFunction(viewComparator)) { // Must use native bind to preserve length viewComparator = viewComparator.bind(this); } this.children._sort(viewComparator); 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 _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, preventRender = _ref.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() { return this.viewComparator || this._viewComparator; }, // Default internal view comparator that order the views by // the order of the collection _viewComparator: function _viewComparator(view) { if (!this.collection) { return; } return this.collection.indexOf(view.model); }, // This method re-filters the children views and re-renders the results filter: function filter() { if (this._isDestroyed) { return this; } if (!this.children.length) { return this; } var filteredViews = this._filterChildren(); this._renderChildren(filteredViews); return this; }, _filterChildren: function _filterChildren() { var viewFilter = this._getFilter(); if (!viewFilter) { return this.children._views; } this.triggerMethod('before:filter', this); var filteredViews = this.children.partition(_.bind(viewFilter, this)); this._detachChildren(filteredViews[1]); this.triggerMethod('filter', this); return filteredViews[0]; }, // 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: 'InvalidViewFilterError', message: '"viewFilter" must be a function, predicate object literal, a string indicating a model attribute, or falsy' }); }, // 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 _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, preventRender = _ref2.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, _.bind(this._detachChildView, this)); }, _detachChildView: function _detachChildView(view) { var shouldTriggerDetach = !!view._isAttached; if (shouldTriggerDetach) { triggerMethodOn(view, 'before:detach', view); } this.detachHtml(view); if (shouldTriggerDetach) { view._isAttached = false; triggerMethodOn(view, 'detach', view); } }, // Override this method to change how the collectionView detaches a child view detachHtml: function detachHtml(view) { this.detachEl(view.el); }, _renderChildren: function _renderChildren(views) { if (this.isEmpty(!views.length)) { this._showEmptyView(); return; } this._destroyEmptyView(); this.triggerMethod('before:render:children', this, views); var els = this._getBuffer(views); this._attachChildren(els, views); this.triggerMethod('render:children', this, views); }, _attachChildren: function _attachChildren(els, views) { var shouldTriggerAttach = !!this._isAttached; views = shouldTriggerAttach ? views : []; _.each(views, function (view) { if (view._isAttached) { return; } triggerMethodOn(view, 'before:attach', view); }); this.attachHtml(this, els); _.each(views, function (view) { if (view._isAttached) { return; } view._isAttached = true; triggerMethodOn(view, 'attach', view); }); }, // Renders each view in children and creates a fragment buffer from them _getBuffer: function _getBuffer(views) { var _this2 = this; var elBuffer = this.createBuffer(); _.each(views, function (view) { _this2._renderChildView(view); _this2.appendChildren(elBuffer, view.el); }); return elBuffer; }, _renderChildView: function _renderChildView(view) { if (view._isRendered) { return; } if (!view.supportsRenderLifecycle) { triggerMethodOn(view, 'before:render', view); } view.render(); if (!view.supportsRenderLifecycle) { view._isRendered = true; triggerMethodOn(view, 'render', view); } }, // Override this method to do something other than `.append`. // You can attach any HTML at this point including the els. attachHtml: function attachHtml(collectionView, els) { this.appendChildren(collectionView.el, els); }, // 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) { if (!view || view._isDestroyed) { return view; } this._addChild(view, index); this._showChildren(); 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, _.bind(this._removeChildView, this)); }, _removeChildView: function _removeChildView(view) { var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, shouldDetach = _ref3.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; } if (view.destroy) { view.destroy(); } else { destroyBackboneView(view); } }, // called by ViewMixin destroy _removeChildren: function _removeChildren() { this._destroyChildren(); this.emptyRegion.destroy(); }, // Destroy the child views that this collection view is holding on to, if any _destroyChildren: function _destroyChildren() { if (!this.children || !this.children.length) { return; } this.triggerMethod('before:destroy:children', this); this.children.each(_.bind(this._removeChildView, this)); this.triggerMethod('destroy:children', this); } }); _.extend(CollectionView$2.prototype, ViewMixin); // Composite View // -------------- var ClassOptions$5 = ['childViewContainer', 'template', 'templateContext']; // Used for rendering a branch-leaf, hierarchical structure. // Extends directly from CollectionView // @deprecated var CompositeView = CollectionView.extend({ // Setting up the inheritance chain which allows changes to // Marionette.CollectionView.prototype.constructor which allows overriding // option to pass '{sort: false}' to prevent the CompositeView from // maintaining the sorted order of the collection. // This will fallback onto appending childView's to the end. constructor: function constructor(options) { deprecate('CompositeView is deprecated. Convert to View at your earliest convenience'); this.mergeOptions(options, ClassOptions$5); CollectionView.prototype.constructor.apply(this, arguments); }, // Configured the initial events that the composite view // binds to. Override this method to prevent the initial // events, or to add your own initial events. _initialEvents: function _initialEvents() { // Bind only after composite view is rendered to avoid adding child views // to nonexistent childViewContainer if (this.collection) { this.listenTo(this.collection, 'add', this._onCollectionAdd); this.listenTo(this.collection, 'update', this._onCollectionUpdate); this.listenTo(this.collection, 'reset', this.renderChildren); if (this.sort) { this.listenTo(this.collection, 'sort', this._sortViews); } } }, // Retrieve the `childView` to be used when rendering each of // the items in the collection. The default is to return // `this.childView` or Marionette.CompositeView if no `childView` // has been defined. As happens in CollectionView, `childView` can // be a function (which should return a view class). _getChildView: function _getChildView(child) { var childView = this.childView; // for CompositeView, if `childView` is not specified, we'll get the same // composite view class rendered for each child in the collection // then check if the `childView` is a view class (the common case) // finally check if it's a function (which we assume that returns a view class) if (!childView) { return this.constructor; } childView = this._getView(childView, child); if (!childView) { throw new MarionetteError({ name: 'InvalidChildViewError', message: '"childView" must be a view class or a function that returns a view class' }); } return childView; }, // Return the serialized model serializeData: function serializeData() { return this.serializeModel(); }, // Renders the model and the collection. render: function render() { if (this._isDestroyed) { return this; } this._isRendering = true; this.resetChildViewContainer(); this.triggerMethod('before:render', this); this._renderTemplate(); this.bindUIElements(); this.renderChildren(); this._isRendering = false; this._isRendered = true; this.triggerMethod('render', this); return this; }, renderChildren: function renderChildren() { if (this._isRendered || this._isRendering) { CollectionView.prototype._renderChildren.call(this); } }, // You might need to override this if you've overridden attachHtml attachBuffer: function attachBuffer(compositeView, buffer) { var $container = this.getChildViewContainer(compositeView); this.appendChildren($container, buffer); }, // Internal method. Append a view to the end of the $el. // Overidden from CollectionView to ensure view is appended to // childViewContainer _insertAfter: function _insertAfter(childView) { var $container = this.getChildViewContainer(this, childView); this.appendChildren($container, childView.el); }, // Internal method. Append reordered childView'. // Overidden from CollectionView to ensure reordered views // are appended to childViewContainer _appendReorderedChildren: function _appendReorderedChildren(children) { var $container = this.getChildViewContainer(this); this.appendChildren($container, children); }, // Internal method to ensure an `$childViewContainer` exists, for the // `attachHtml` method to use. getChildViewContainer: function getChildViewContainer(containerView, childView) { if (!!containerView.$childViewContainer) { return containerView.$childViewContainer; } var container = void 0; var childViewContainer = containerView.childViewContainer; if (childViewContainer) { var selector = _.result(containerView, 'childViewContainer'); if (selector.charAt(0) === '@' && containerView.ui) { container = containerView.ui[selector.substr(4)]; } else { container = this.findEls(selector, containerView.$el); } if (container.length <= 0) { throw new MarionetteError({ name: 'ChildViewContainerMissingError', message: 'The specified "childViewContainer" was not found: ' + containerView.childViewContainer }); } } else { container = containerView.$el; } containerView.$childViewContainer = container; return container; }, // Internal method to reset the `$childViewContainer` on render resetChildViewContainer: function resetChildViewContainer() { if (this.$childViewContainer) { this.$childViewContainer = undefined; } } }); // To prevent duplication but allow the best View organization // Certain View methods are mixed directly into the deprecated CompositeView var MixinFromView = _.pick(View.prototype, 'serializeModel', 'getTemplate', '_renderTemplate', '_renderHtml', 'mixinTemplateContext', 'attachElContent'); _.extend(CompositeView.prototype, MixinFromView); // Behavior // -------- // A Behavior is an isolated set of DOM / // user interactions that can be mixed into any View. // Behaviors allow you to blackbox View specific interactions // into portable logical chunks, keeping your views simple and your code DRY. var ClassOptions$6 = ['collectionEvents', 'events', 'modelEvents', 'triggers', 'ui']; var Behavior = MarionetteObject.extend({ cidPrefix: 'mnb', constructor: function constructor(options, view) { // Setup reference to the view. // this comes in handle when a behavior // wants to directly talk up the chain // to the view. this.view = view; if (this.defaults) { deprecate('Behavior defaults are deprecated. For similar functionality set options on the Behavior class.'); } this.defaults = _.clone(_.result(this, 'defaults', {})); this._setOptions(this.defaults, options); this.mergeOptions(this.options, ClassOptions$6); // Construct an internal UI hash using // the behaviors UI hash and then the view UI hash. // This allows the user to use UI hash elements // defined in the parent view as well as those // defined in the given behavior. // This order will help the reuse and share of a behavior // between multiple views, while letting a view override a // selector under an UI key. this.ui = _.extend({}, _.result(this, 'ui'), _.result(view, 'ui')); MarionetteObject.apply(this, arguments); }, // proxy behavior $ method to the view // this is useful for doing jquery DOM lookups // scoped to behaviors view. $: function $() { return this.view.$.apply(this.view, arguments); }, // Stops the behavior from listening to events. // Overrides Object#destroy to prevent additional events from being triggered. destroy: function destroy() { this.stopListening(); this.view._removeBehavior(this); 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; // Normalize behavior events hash to allow // a user to use the @ui. syntax. var behaviorEvents = this.normalizeUIKeys(_.result(this, 'events')); // binds the handler to the behavior and builds a unique eventName return _.reduce(behaviorEvents, function (events, behaviorHandler, key) { if (!_.isFunction(behaviorHandler)) { behaviorHandler = _this[behaviorHandler]; } if (!behaviorHandler) { return; } key = getUniqueEventName(key); events[key] = _.bind(behaviorHandler, _this); return events; }, {}); }, // Internal method to build all trigger handlers for a given behavior getTriggers: function getTriggers() { if (!this.triggers) { return; } // Normalize behavior triggers hash to allow // a user to use the @ui. syntax. var behaviorTriggers = this.normalizeUIKeys(_.result(this, 'triggers')); return this._getViewTriggers(this.view, behaviorTriggers); } }); _.extend(Behavior.prototype, DelegateEntityEventsMixin, TriggersMixin, UIMixin); // Application // ----------- var ClassOptions$7 = ['region', 'regionClass']; // A container for a Marionette application. var Application = MarionetteObject.extend({ cidPrefix: 'mna', constructor: function constructor(options) { this._setOptions(options); this.mergeOptions(options, ClassOptions$7); this._initRegion(); MarionetteObject.prototype.constructor.apply(this, arguments); }, regionClass: Region, _initRegion: function _initRegion() { var region = this.region; if (!region) { return; } var defaults = { regionClass: this.regionClass }; this._region = buildRegion(region, defaults); }, getRegion: function getRegion() { return this._region; }, showView: function showView(view) { var region = this.getRegion(); for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } return region.show.apply(region, [view].concat(args)); }, getView: function getView() { return this.getRegion().currentView; }, // kick off all of the application's processes. start: function start(options) { this.triggerMethod('before:start', this, options); this.triggerMethod('start', this, options); return this; } }); // App Router // ---------- // Reduce the boilerplate code of handling route events // and then calling a single method on another object, // called a controller. // Have your routers configured to call the method on // your controller, directly. // // Configure an AppRouter with `appRoutes`. // // App routers can only take one `controller` object. // It is recommended that you divide your controller // objects in to smaller pieces of related functionality // and have multiple routers / controllers, instead of // just one giant router and controller. // // You can also add standard routes to an AppRouter. var ClassOptions$8 = ['appRoutes', 'controller']; var AppRouter = Backbone.Router.extend({ constructor: function constructor(options) { this._setOptions(options); this.mergeOptions(options, ClassOptions$8); Backbone.Router.apply(this, arguments); var appRoutes = this.appRoutes; var controller = this._getController(); this.processAppRoutes(controller, appRoutes); this.on('route', this._processOnRoute, this); }, // Similar to route method on a Backbone Router but // method is called on the controller appRoute: function appRoute(route, methodName) { var controller = this._getController(); this._addAppRoute(controller, route, methodName); return this; }, // process the route event and trigger the onRoute // method call, if it exists _processOnRoute: function _processOnRoute(routeName, routeArgs) { // make sure an onRoute before trying to call it if (_.isFunction(this.onRoute)) { // find the path that matches the current route var routePath = _.invert(this.appRoutes)[routeName]; this.onRoute(routeName, routePath, routeArgs); } }, // Internal method to process the `appRoutes` for the // router, and turn them in to routes that trigger the // specified method on the specified `controller`. processAppRoutes: function processAppRoutes(controller, appRoutes) { var _this = this; if (!appRoutes) { return this; } var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes _.each(routeNames, function (route) { _this._addAppRoute(controller, route, appRoutes[route]); }); return this; }, _getController: function _getController() { return this.controller; }, _addAppRoute: function _addAppRoute(controller, route, methodName) { var method = controller[methodName]; if (!method) { throw new MarionetteError('Method "' + methodName + '" was not found on the controller'); } this.route(route, methodName, _.bind(method, controller)); }, triggerMethod: triggerMethod$1 }); _.extend(AppRouter.prototype, CommonMixin); // Placeholder method to be extended by the user. // The method should define the object that stores the behaviors. // i.e. // // ```js // Marionette.Behaviors.behaviorsLookup: function() { // return App.Behaviors // } // ``` function behaviorsLookup() { throw new MarionetteError({ message: 'You must define where your behaviors are stored.', url: 'marionette.behaviors.md#behaviorslookup' }); } var previousMarionette = Backbone.Marionette; var Marionette = Backbone.Marionette = {}; // This allows you to run multiple instances of Marionette on the same // webapp. After loading the new version, call `noConflict()` to // get a reference to it. At the same time the old version will be // returned to Backbone.Marionette. Marionette.noConflict = function () { Backbone.Marionette = previousMarionette; return this; }; // Utilities Marionette.bindEvents = proxy(bindEvents); Marionette.unbindEvents = proxy(unbindEvents); Marionette.bindRequests = proxy(bindRequests); Marionette.unbindRequests = proxy(unbindRequests); Marionette.mergeOptions = proxy(mergeOptions); Marionette.getOption = proxy(getOption); Marionette.normalizeMethods = proxy(normalizeMethods); Marionette.extend = extend; Marionette.isNodeAttached = isNodeAttached; Marionette.deprecate = deprecate; Marionette.triggerMethod = proxy(triggerMethod$1); Marionette.triggerMethodOn = triggerMethodOn; Marionette.isEnabled = isEnabled; Marionette.setEnabled = setEnabled; Marionette.monitorViewEvents = monitorViewEvents; Marionette.Behaviors = {}; Marionette.Behaviors.behaviorsLookup = behaviorsLookup; // Classes Marionette.Application = Application; Marionette.AppRouter = AppRouter; Marionette.Renderer = Renderer; Marionette.TemplateCache = TemplateCache; Marionette.View = View; Marionette.CollectionView = CollectionView; Marionette.NextCollectionView = CollectionView$2; Marionette.CompositeView = CompositeView; Marionette.Behavior = Behavior; Marionette.Region = Region; Marionette.Error = MarionetteError; Marionette.Object = MarionetteObject; // Configuration Marionette.DEV_MODE = false; Marionette.FEATURES = FEATURES; Marionette.VERSION = version; return Marionette; }))); //# sourceMappingURL=backbone.marionette.js.map