backbone.marionette.js

From disqus.com, 1 Month ago, written in JavaScript, viewed 3 times. This paste is a reply to Re: backbone.babysitter.js from Cream Elephant - view diff
URL https://pastebin.freepbx.org/view/80751319 Embed
Download Paste or View Raw
  1. // MarionetteJS (Backbone.Marionette)
  2. // ----------------------------------
  3. // v1.7.4
  4. //
  5. // Copyright (c)2014 Derick Bailey, Muted Solutions, LLC.
  6. // Distributed under MIT license
  7. //
  8. // http://marionettejs.com
  9.  
  10.  
  11.  
  12. /*!
  13.  * Includes BabySitter
  14.  * https://github.com/marionettejs/backbone.babysitter/
  15.  *
  16.  * Includes Wreqr
  17.  * https://github.com/marionettejs/backbone.wreqr/
  18.  */
  19.  
  20. (function (root, factory) {
  21.   if (typeof exports === 'object') {
  22.  
  23.     var underscore = require('underscore');
  24.     var backbone = require('backbone');
  25.     var wreqr = require('backbone.wreqr');
  26.     var babysitter = require('backbone.babysitter');
  27.  
  28.     module.exports = factory(underscore, backbone, wreqr, babysitter);
  29.  
  30.   } else if (typeof define === 'function' && define.amd) {
  31.  
  32.     define('backbone-marionette',['underscore', 'backbone', 'backbone.wreqr', 'backbone.babysitter'], factory);
  33.  
  34.   }
  35. }(this, function (_, Backbone) {
  36.  
  37.   var Marionette = (function(global, Backbone, _){
  38.   "use strict";
  39.  
  40.   // Define and export the Marionette namespace
  41.   var Marionette = {};
  42.   Backbone.Marionette = Marionette;
  43.  
  44.   // Get the DOM manipulator for later use
  45.   Marionette.$ = Backbone.$;
  46.  
  47. // Helpers
  48. // -------
  49.  
  50. // For slicing `arguments` in functions
  51. var slice = Array.prototype.slice;
  52.  
  53. function throwError(message, name) {
  54.   var error = new Error(message);
  55.   error.name = name || 'Error';
  56.   throw error;
  57. }
  58.  
  59. // Marionette.extend
  60. // -----------------
  61.  
  62. // Borrow the Backbone `extend` method so we can use it as needed
  63. Marionette.extend = Backbone.Model.extend;
  64.  
  65. // Marionette.getOption
  66. // --------------------
  67.  
  68. // Retrieve an object, function or other value from a target
  69. // object or its `options`, with `options` taking precedence.
  70. Marionette.getOption = function(target, optionName){
  71.   if (!target || !optionName){ return; }
  72.   var value;
  73.  
  74.   if (target.options && (optionName in target.options) && (target.options[optionName] !== undefined)){
  75.     value = target.options[optionName];
  76.   } else {
  77.     value = target[optionName];
  78.   }
  79.  
  80.   return value;
  81. };
  82.  
  83. // Marionette.normalizeMethods
  84. // ----------------------
  85.  
  86. // Pass in a mapping of events => functions or function names
  87. // and return a mapping of events => functions
  88. Marionette.normalizeMethods = function(hash) {
  89.   var normalizedHash = {}, method;
  90.   _.each(hash, function(fn, name) {
  91.     method = fn;
  92.     if (!_.isFunction(method)) {
  93.       method = this[method];
  94.     }
  95.     if (!method) {
  96.       return;
  97.     }
  98.     normalizedHash[name] = method;
  99.   }, this);
  100.   return normalizedHash;
  101. };
  102.  
  103.  
  104. // allows for the use of the @ui. syntax within
  105. // a given key for triggers and events
  106. // swaps the @ui with the associated selector
  107. Marionette.normalizeUIKeys = function(hash, ui) {
  108.   if (typeof(hash) === "undefined") {
  109.     return;
  110.   }
  111.  
  112.   _.each(_.keys(hash), function(v) {
  113.     var pattern = /@ui.[a-zA-Z_$0-9]*/g;
  114.     if (v.match(pattern)) {
  115.       hash[v.replace(pattern, function(r) {
  116.         return ui[r.slice(4)];
  117.       })] = hash[v];
  118.       delete hash[v];
  119.     }
  120.   });
  121.  
  122.   return hash;
  123. };
  124.  
  125. // Trigger an event and/or a corresponding method name. Examples:
  126. //
  127. // `this.triggerMethod("foo")` will trigger the "foo" event and
  128. // call the "onFoo" method.
  129. //
  130. // `this.triggerMethod("foo:bar")` will trigger the "foo:bar" event and
  131. // call the "onFooBar" method.
  132. Marionette.triggerMethod = (function(){
  133.  
  134.   // split the event name on the ":"
  135.   var splitter = /(^|:)(\w)/gi;
  136.  
  137.   // take the event section ("section1:section2:section3")
  138.   // and turn it in to uppercase name
  139.   function getEventName(match, prefix, eventName) {
  140.     return eventName.toUpperCase();
  141.   }
  142.  
  143.   // actual triggerMethod implementation
  144.   var triggerMethod = function(event) {
  145.     // get the method name from the event name
  146.     var methodName = 'on' + event.replace(splitter, getEventName);
  147.     var method = this[methodName];
  148.  
  149.     // trigger the event, if a trigger method exists
  150.     if(_.isFunction(this.trigger)) {
  151.       this.trigger.apply(this, arguments);
  152.     }
  153.  
  154.     // call the onMethodName if it exists
  155.     if (_.isFunction(method)) {
  156.       // pass all arguments, except the event name
  157.       return method.apply(this, _.tail(arguments));
  158.     }
  159.   };
  160.  
  161.   return triggerMethod;
  162. })();
  163.  
  164. // DOMRefresh
  165. // ----------
  166. //
  167. // Monitor a view's state, and after it has been rendered and shown
  168. // in the DOM, trigger a "dom:refresh" event every time it is
  169. // re-rendered.
  170.  
  171. Marionette.MonitorDOMRefresh = (function(documentElement){
  172.   // track when the view has been shown in the DOM,
  173.   // using a Marionette.Region (or by other means of triggering "show")
  174.   function handleShow(view){
  175.     view._isShown = true;
  176.     triggerDOMRefresh(view);
  177.   }
  178.  
  179.   // track when the view has been rendered
  180.   function handleRender(view){
  181.     view._isRendered = true;
  182.     triggerDOMRefresh(view);
  183.   }
  184.  
  185.   // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method
  186.   function triggerDOMRefresh(view){
  187.     if (view._isShown && view._isRendered && isInDOM(view)){
  188.       if (_.isFunction(view.triggerMethod)){
  189.         view.triggerMethod("dom:refresh");
  190.       }
  191.     }
  192.   }
  193.  
  194.   function isInDOM(view) {
  195.     return documentElement.contains(view.el);
  196.   }
  197.  
  198.   // Export public API
  199.   return function(view){
  200.     view.listenTo(view, "show", function(){
  201.       handleShow(view);
  202.     });
  203.  
  204.     view.listenTo(view, "render", function(){
  205.       handleRender(view);
  206.     });
  207.   };
  208. })(document.documentElement);
  209.  
  210.  
  211. // Marionette.bindEntityEvents & unbindEntityEvents
  212. // ---------------------------
  213. //
  214. // These methods are used to bind/unbind a backbone "entity" (collection/model)
  215. // to methods on a target object.
  216. //
  217. // The first parameter, `target`, must have a `listenTo` method from the
  218. // EventBinder object.
  219. //
  220. // The second parameter is the entity (Backbone.Model or Backbone.Collection)
  221. // to bind the events from.
  222. //
  223. // The third parameter is a hash of { "event:name": "eventHandler" }
  224. // configuration. Multiple handlers can be separated by a space. A
  225. // function can be supplied instead of a string handler name.
  226.  
  227. (function(Marionette){
  228.   "use strict";
  229.  
  230.   // Bind the event to handlers specified as a string of
  231.   // handler names on the target object
  232.   function bindFromStrings(target, entity, evt, methods){
  233.     var methodNames = methods.split(/\s+/);
  234.  
  235.     _.each(methodNames,function(methodName) {
  236.  
  237.       var method = target[methodName];
  238.       if(!method) {
  239.         throwError("Method '"+ methodName +"' was configured as an event handler, but does not exist.");
  240.       }
  241.  
  242.       target.listenTo(entity, evt, method);
  243.     });
  244.   }
  245.  
  246.   // Bind the event to a supplied callback function
  247.   function bindToFunction(target, entity, evt, method){
  248.       target.listenTo(entity, evt, method);
  249.   }
  250.  
  251.   // Bind the event to handlers specified as a string of
  252.   // handler names on the target object
  253.   function unbindFromStrings(target, entity, evt, methods){
  254.     var methodNames = methods.split(/\s+/);
  255.  
  256.     _.each(methodNames,function(methodName) {
  257.       var method = target[methodName];
  258.       target.stopListening(entity, evt, method);
  259.     });
  260.   }
  261.  
  262.   // Bind the event to a supplied callback function
  263.   function unbindToFunction(target, entity, evt, method){
  264.       target.stopListening(entity, evt, method);
  265.   }
  266.  
  267.  
  268.   // generic looping function
  269.   function iterateEvents(target, entity, bindings, functionCallback, stringCallback){
  270.     if (!entity || !bindings) { return; }
  271.  
  272.     // allow the bindings to be a function
  273.     if (_.isFunction(bindings)){
  274.       bindings = bindings.call(target);
  275.     }
  276.  
  277.     // iterate the bindings and bind them
  278.     _.each(bindings, function(methods, evt){
  279.  
  280.       // allow for a function as the handler,
  281.       // or a list of event names as a string
  282.       if (_.isFunction(methods)){
  283.         functionCallback(target, entity, evt, methods);
  284.       } else {
  285.         stringCallback(target, entity, evt, methods);
  286.       }
  287.  
  288.     });
  289.   }
  290.  
  291.   // Export Public API
  292.   Marionette.bindEntityEvents = function(target, entity, bindings){
  293.     iterateEvents(target, entity, bindings, bindToFunction, bindFromStrings);
  294.   };
  295.  
  296.   Marionette.unbindEntityEvents = function(target, entity, bindings){
  297.     iterateEvents(target, entity, bindings, unbindToFunction, unbindFromStrings);
  298.   };
  299.  
  300. })(Marionette);
  301.  
  302.  
  303. // Callbacks
  304. // ---------
  305.  
  306. // A simple way of managing a collection of callbacks
  307. // and executing them at a later point in time, using jQuery's
  308. // `Deferred` object.
  309. Marionette.Callbacks = function(){
  310.   this._deferred = Marionette.$.Deferred();
  311.   this._callbacks = [];
  312. };
  313.  
  314. _.extend(Marionette.Callbacks.prototype, {
  315.  
  316.   // Add a callback to be executed. Callbacks added here are
  317.   // guaranteed to execute, even if they are added after the
  318.   // `run` method is called.
  319.   add: function(callback, contextOverride){
  320.     this._callbacks.push({cb: callback, ctx: contextOverride});
  321.  
  322.     this._deferred.done(function(context, options){
  323.       if (contextOverride){ context = contextOverride; }
  324.       callback.call(context, options);
  325.     });
  326.   },
  327.  
  328.   // Run all registered callbacks with the context specified.
  329.   // Additional callbacks can be added after this has been run
  330.   // and they will still be executed.
  331.   run: function(options, context){
  332.     this._deferred.resolve(context, options);
  333.   },
  334.  
  335.   // Resets the list of callbacks to be run, allowing the same list
  336.   // to be run multiple times - whenever the `run` method is called.
  337.   reset: function(){
  338.     var callbacks = this._callbacks;
  339.     this._deferred = Marionette.$.Deferred();
  340.     this._callbacks = [];
  341.  
  342.     _.each(callbacks, function(cb){
  343.       this.add(cb.cb, cb.ctx);
  344.     }, this);
  345.   }
  346. });
  347.  
  348.  
  349. // Marionette Controller
  350. // ---------------------
  351. //
  352. // A multi-purpose object to use as a controller for
  353. // modules and routers, and as a mediator for workflow
  354. // and coordination of other objects, views, and more.
  355. Marionette.Controller = function(options){
  356.   this.triggerMethod = Marionette.triggerMethod;
  357.   this.options = options || {};
  358.  
  359.   if (_.isFunction(this.initialize)){
  360.     this.initialize(this.options);
  361.   }
  362. };
  363.  
  364. Marionette.Controller.extend = Marionette.extend;
  365.  
  366. // Controller Methods
  367. // --------------
  368.  
  369. // Ensure it can trigger events with Backbone.Events
  370. _.extend(Marionette.Controller.prototype, Backbone.Events, {
  371.   close: function(){
  372.     this.stopListening();
  373.     var args = Array.prototype.slice.call(arguments);
  374.     this.triggerMethod.apply(this, ["close"].concat(args));
  375.     this.unbind();
  376.   }
  377. });
  378.  
  379. // Region
  380. // ------
  381. //
  382. // Manage the visual regions of your composite application. See
  383. // http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/
  384.  
  385. Marionette.Region = function(options){
  386.   this.options = options || {};
  387.   this.el = Marionette.getOption(this, "el");
  388.  
  389.   if (!this.el){
  390.     throwError("An 'el' must be specified for a region.", "NoElError");
  391.   }
  392.  
  393.   if (this.initialize){
  394.     var args = Array.prototype.slice.apply(arguments);
  395.     this.initialize.apply(this, args);
  396.   }
  397. };
  398.  
  399.  
  400. // Region Type methods
  401. // -------------------
  402.  
  403. _.extend(Marionette.Region, {
  404.  
  405.   // Build an instance of a region by passing in a configuration object
  406.   // and a default region type to use if none is specified in the config.
  407.   //
  408.   // The config object should either be a string as a jQuery DOM selector,
  409.   // a Region type directly, or an object literal that specifies both
  410.   // a selector and regionType:
  411.   //
  412.   // ```js
  413.   // {
  414.   //   selector: "#foo",
  415.   //   regionType: MyCustomRegion
  416.   // }
  417.   // ```
  418.   //
  419.   buildRegion: function(regionConfig, defaultRegionType){
  420.     var regionIsString = _.isString(regionConfig);
  421.     var regionSelectorIsString = _.isString(regionConfig.selector);
  422.     var regionTypeIsUndefined = _.isUndefined(regionConfig.regionType);
  423.     var regionIsType = _.isFunction(regionConfig);
  424.  
  425.     if (!regionIsType && !regionIsString && !regionSelectorIsString) {
  426.       throwError("Region must be specified as a Region type, a selector string or an object with selector property");
  427.     }
  428.  
  429.     var selector, RegionType;
  430.  
  431.     // get the selector for the region
  432.  
  433.     if (regionIsString) {
  434.       selector = regionConfig;
  435.     }
  436.  
  437.     if (regionConfig.selector) {
  438.       selector = regionConfig.selector;
  439.       delete regionConfig.selector;
  440.     }
  441.  
  442.     // get the type for the region
  443.  
  444.     if (regionIsType){
  445.       RegionType = regionConfig;
  446.     }
  447.  
  448.     if (!regionIsType && regionTypeIsUndefined) {
  449.       RegionType = defaultRegionType;
  450.     }
  451.  
  452.     if (regionConfig.regionType) {
  453.       RegionType = regionConfig.regionType;
  454.       delete regionConfig.regionType;
  455.     }
  456.  
  457.     if (regionIsString || regionIsType) {
  458.       regionConfig = {};
  459.     }
  460.  
  461.     regionConfig.el = selector;
  462.  
  463.     // build the region instance
  464.     var region = new RegionType(regionConfig);
  465.  
  466.     // override the `getEl` function if we have a parentEl
  467.     // this must be overridden to ensure the selector is found
  468.     // on the first use of the region. if we try to assign the
  469.     // region's `el` to `parentEl.find(selector)` in the object
  470.     // literal to build the region, the element will not be
  471.     // guaranteed to be in the DOM already, and will cause problems
  472.     if (regionConfig.parentEl){
  473.       region.getEl = function(selector) {
  474.         var parentEl = regionConfig.parentEl;
  475.         if (_.isFunction(parentEl)){
  476.           parentEl = parentEl();
  477.         }
  478.         return parentEl.find(selector);
  479.       };
  480.     }
  481.  
  482.     return region;
  483.   }
  484.  
  485. });
  486.  
  487. // Region Instance Methods
  488. // -----------------------
  489.  
  490. _.extend(Marionette.Region.prototype, Backbone.Events, {
  491.  
  492.   // Displays a backbone view instance inside of the region.
  493.   // Handles calling the `render` method for you. Reads content
  494.   // directly from the `el` attribute. Also calls an optional
  495.   // `onShow` and `close` method on your view, just after showing
  496.   // or just before closing the view, respectively.
  497.   show: function(view){
  498.     this.ensureEl();
  499.  
  500.     var isViewClosed = view.isClosed || _.isUndefined(view.$el);
  501.     var isDifferentView = view !== this.currentView;
  502.  
  503.     if (isDifferentView) {
  504.       this.close();
  505.     }
  506.  
  507.     view.render();
  508.     Marionette.triggerMethod.call(this, "before:show", view);
  509.     Marionette.triggerMethod.call(view, "before:show");
  510.  
  511.     if (isDifferentView || isViewClosed) {
  512.       this.open(view);
  513.     }
  514.  
  515.     this.currentView = view;
  516.  
  517.     Marionette.triggerMethod.call(this, "show", view);
  518.     Marionette.triggerMethod.call(view, "show");
  519.   },
  520.  
  521.   ensureEl: function(){
  522.     if (!this.$el || this.$el.length === 0){
  523.       this.$el = this.getEl(this.el);
  524.     }
  525.   },
  526.  
  527.   // Override this method to change how the region finds the
  528.   // DOM element that it manages. Return a jQuery selector object.
  529.   getEl: function(selector){
  530.     return Marionette.$(selector);
  531.   },
  532.  
  533.   // Override this method to change how the new view is
  534.   // appended to the `$el` that the region is managing
  535.   open: function(view){
  536.     this.$el.empty().append(view.el);
  537.   },
  538.  
  539.   // Close the current view, if there is one. If there is no
  540.   // current view, it does nothing and returns immediately.
  541.   close: function(){
  542.     var view = this.currentView;
  543.     if (!view || view.isClosed){ return; }
  544.  
  545.     // call 'close' or 'remove', depending on which is found
  546.     if (view.close) { view.close(); }
  547.     else if (view.remove) { view.remove(); }
  548.  
  549.     Marionette.triggerMethod.call(this, "close", view);
  550.  
  551.     delete this.currentView;
  552.   },
  553.  
  554.   // Attach an existing view to the region. This
  555.   // will not call `render` or `onShow` for the new view,
  556.   // and will not replace the current HTML for the `el`
  557.   // of the region.
  558.   attachView: function(view){
  559.     this.currentView = view;
  560.   },
  561.  
  562.   // Reset the region by closing any existing view and
  563.   // clearing out the cached `$el`. The next time a view
  564.   // is shown via this region, the region will re-query the
  565.   // DOM for the region's `el`.
  566.   reset: function(){
  567.     this.close();
  568.     delete this.$el;
  569.   }
  570. });
  571.  
  572. // Copy the `extend` function used by Backbone's classes
  573. Marionette.Region.extend = Marionette.extend;
  574.  
  575. // Marionette.RegionManager
  576. // ------------------------
  577. //
  578. // Manage one or more related `Marionette.Region` objects.
  579. Marionette.RegionManager = (function(Marionette){
  580.  
  581.   var RegionManager = Marionette.Controller.extend({
  582.     constructor: function(options){
  583.       this._regions = {};
  584.       Marionette.Controller.prototype.constructor.call(this, options);
  585.     },
  586.  
  587.     // Add multiple regions using an object literal, where
  588.     // each key becomes the region name, and each value is
  589.     // the region definition.
  590.     addRegions: function(regionDefinitions, defaults){
  591.       var regions = {};
  592.  
  593.       _.each(regionDefinitions, function(definition, name){
  594.         if (_.isString(definition)){
  595.           definition = { selector: definition };
  596.         }
  597.  
  598.         if (definition.selector){
  599.           definition = _.defaults({}, definition, defaults);
  600.         }
  601.  
  602.         var region = this.addRegion(name, definition);
  603.         regions[name] = region;
  604.       }, this);
  605.  
  606.       return regions;
  607.     },
  608.  
  609.     // Add an individual region to the region manager,
  610.     // and return the region instance
  611.     addRegion: function(name, definition){
  612.       var region;
  613.  
  614.       var isObject = _.isObject(definition);
  615.       var isString = _.isString(definition);
  616.       var hasSelector = !!definition.selector;
  617.  
  618.       if (isString || (isObject && hasSelector)){
  619.         region = Marionette.Region.buildRegion(definition, Marionette.Region);
  620.       } else if (_.isFunction(definition)){
  621.         region = Marionette.Region.buildRegion(definition, Marionette.Region);
  622.       } else {
  623.         region = definition;
  624.       }
  625.  
  626.       this._store(name, region);
  627.       this.triggerMethod("region:add", name, region);
  628.       return region;
  629.     },
  630.  
  631.     // Get a region by name
  632.     get: function(name){
  633.       return this._regions[name];
  634.     },
  635.  
  636.     // Remove a region by name
  637.     removeRegion: function(name){
  638.       var region = this._regions[name];
  639.       this._remove(name, region);
  640.     },
  641.  
  642.     // Close all regions in the region manager, and
  643.     // remove them
  644.     removeRegions: function(){
  645.       _.each(this._regions, function(region, name){
  646.         this._remove(name, region);
  647.       }, this);
  648.     },
  649.  
  650.     // Close all regions in the region manager, but
  651.     // leave them attached
  652.     closeRegions: function(){
  653.       _.each(this._regions, function(region, name){
  654.         region.close();
  655.       }, this);
  656.     },
  657.  
  658.     // Close all regions and shut down the region
  659.     // manager entirely
  660.     close: function(){
  661.       this.removeRegions();
  662.       Marionette.Controller.prototype.close.apply(this, arguments);
  663.     },
  664.  
  665.     // internal method to store regions
  666.     _store: function(name, region){
  667.       this._regions[name] = region;
  668.       this._setLength();
  669.     },
  670.  
  671.     // internal method to remove a region
  672.     _remove: function(name, region){
  673.       region.close();
  674.       delete this._regions[name];
  675.       this._setLength();
  676.       this.triggerMethod("region:remove", name, region);
  677.     },
  678.  
  679.     // set the number of regions current held
  680.     _setLength: function(){
  681.       this.length = _.size(this._regions);
  682.     }
  683.  
  684.   });
  685.  
  686.   // Borrowing this code from Backbone.Collection:
  687.   // http://backbonejs.org/docs/backbone.html#section-106
  688.   //
  689.   // Mix in methods from Underscore, for iteration, and other
  690.   // collection related features.
  691.   var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter',
  692.     'select', 'reject', 'every', 'all', 'some', 'any', 'include',
  693.     'contains', 'invoke', 'toArray', 'first', 'initial', 'rest',
  694.     'last', 'without', 'isEmpty', 'pluck'];
  695.  
  696.   _.each(methods, function(method) {
  697.     RegionManager.prototype[method] = function() {
  698.       var regions = _.values(this._regions);
  699.       var args = [regions].concat(_.toArray(arguments));
  700.       return _[method].apply(_, args);
  701.     };
  702.   });
  703.  
  704.   return RegionManager;
  705. })(Marionette);
  706.  
  707.  
  708. // Template Cache
  709. // --------------
  710.  
  711. // Manage templates stored in `<script>` blocks,
  712. // caching them for faster access.
  713. Marionette.TemplateCache = function(templateId){
  714.   this.templateId = templateId;
  715. };
  716.  
  717. // TemplateCache object-level methods. Manage the template
  718. // caches from these method calls instead of creating
  719. // your own TemplateCache instances
  720. _.extend(Marionette.TemplateCache, {
  721.   templateCaches: {},
  722.  
  723.   // Get the specified template by id. Either
  724.   // retrieves the cached version, or loads it
  725.   // from the DOM.
  726.   get: function(templateId){
  727.     var cachedTemplate = this.templateCaches[templateId];
  728.  
  729.     if (!cachedTemplate){
  730.       cachedTemplate = new Marionette.TemplateCache(templateId);
  731.       this.templateCaches[templateId] = cachedTemplate;
  732.     }
  733.  
  734.     return cachedTemplate.load();
  735.   },
  736.  
  737.   // Clear templates from the cache. If no arguments
  738.   // are specified, clears all templates:
  739.   // `clear()`
  740.   //
  741.   // If arguments are specified, clears each of the
  742.   // specified templates from the cache:
  743.   // `clear("#t1", "#t2", "...")`
  744.   clear: function(){
  745.     var i;
  746.     var args = slice.call(arguments);
  747.     var length = args.length;
  748.  
  749.     if (length > 0){
  750.       for(i=0; i<length; i++){
  751.         delete this.templateCaches[args[i]];
  752.       }
  753.     } else {
  754.       this.templateCaches = {};
  755.     }
  756.   }
  757. });
  758.  
  759. // TemplateCache instance methods, allowing each
  760. // template cache object to manage its own state
  761. // and know whether or not it has been loaded
  762. _.extend(Marionette.TemplateCache.prototype, {
  763.  
  764.   // Internal method to load the template
  765.   load: function(){
  766.     // Guard clause to prevent loading this template more than once
  767.     if (this.compiledTemplate){
  768.       return this.compiledTemplate;
  769.     }
  770.  
  771.     // Load the template and compile it
  772.     var template = this.loadTemplate(this.templateId);
  773.     this.compiledTemplate = this.compileTemplate(template);
  774.  
  775.     return this.compiledTemplate;
  776.   },
  777.  
  778.   // Load a template from the DOM, by default. Override
  779.   // this method to provide your own template retrieval
  780.   // For asynchronous loading with AMD/RequireJS, consider
  781.   // using a template-loader plugin as described here:
  782.   // https://github.com/marionettejs/backbone.marionette/wiki/Using-marionette-with-requirejs
  783.   loadTemplate: function(templateId){
  784.     var template = Marionette.$(templateId).html();
  785.  
  786.     if (!template || template.length === 0){
  787.       throwError("Could not find template: '" + templateId + "'", "NoTemplateError");
  788.     }
  789.  
  790.     return template;
  791.   },
  792.  
  793.   // Pre-compile the template before caching it. Override
  794.   // this method if you do not need to pre-compile a template
  795.   // (JST / RequireJS for example) or if you want to change
  796.   // the template engine used (Handebars, etc).
  797.   compileTemplate: function(rawTemplate){
  798.     return _.template(rawTemplate);
  799.   }
  800. });
  801.  
  802.  
  803. // Renderer
  804. // --------
  805.  
  806. // Render a template with data by passing in the template
  807. // selector and the data to render.
  808. Marionette.Renderer = {
  809.  
  810.   // Render a template with data. The `template` parameter is
  811.   // passed to the `TemplateCache` object to retrieve the
  812.   // template function. Override this method to provide your own
  813.   // custom rendering and template handling for all of Marionette.
  814.   render: function(template, data){
  815.  
  816.     if (!template) {
  817.       throwError("Cannot render the template since it's false, null or undefined.", "TemplateNotFoundError");
  818.     }
  819.  
  820.     var templateFunc;
  821.     if (typeof template === "function"){
  822.       templateFunc = template;
  823.     } else {
  824.       templateFunc = Marionette.TemplateCache.get(template);
  825.     }
  826.  
  827.     return templateFunc(data);
  828.   }
  829. };
  830.  
  831.  
  832.  
  833. // Marionette.View
  834. // ---------------
  835.  
  836. // The core view type that other Marionette views extend from.
  837. Marionette.View = Backbone.View.extend({
  838.  
  839.   constructor: function(options){
  840.     _.bindAll(this, "render");
  841.  
  842.     if (_.isObject(this.behaviors)) {
  843.       new Marionette.Behaviors(this);
  844.     }
  845.  
  846.     // this exposes view options to the view initializer
  847.     // this is a backfill since backbone removed the assignment
  848.     // of this.options
  849.     // at some point however this may be removed
  850.     this.options = _.extend({}, _.result(this, 'options'), _.isFunction(options) ? options.call(this) : options);
  851.  
  852.     // parses out the @ui DSL for events
  853.     this.events = this.normalizeUIKeys(_.result(this, 'events'));
  854.     Backbone.View.prototype.constructor.apply(this, arguments);
  855.  
  856.     Marionette.MonitorDOMRefresh(this);
  857.     this.listenTo(this, "show", this.onShowCalled);
  858.   },
  859.  
  860.   // import the "triggerMethod" to trigger events with corresponding
  861.   // methods if the method exists
  862.   triggerMethod: Marionette.triggerMethod,
  863.  
  864.   // Imports the "normalizeMethods" to transform hashes of
  865.   // events=>function references/names to a hash of events=>function references
  866.   normalizeMethods: Marionette.normalizeMethods,
  867.  
  868.   // Get the template for this view
  869.   // instance. You can set a `template` attribute in the view
  870.   // definition or pass a `template: "whatever"` parameter in
  871.   // to the constructor options.
  872.   getTemplate: function(){
  873.     return Marionette.getOption(this, "template");
  874.   },
  875.  
  876.   // Mix in template helper methods. Looks for a
  877.   // `templateHelpers` attribute, which can either be an
  878.   // object literal, or a function that returns an object
  879.   // literal. All methods and attributes from this object
  880.   // are copies to the object passed in.
  881.   mixinTemplateHelpers: function(target){
  882.     target = target || {};
  883.     var templateHelpers = Marionette.getOption(this, "templateHelpers");
  884.     if (_.isFunction(templateHelpers)){
  885.       templateHelpers = templateHelpers.call(this);
  886.     }
  887.     return _.extend(target, templateHelpers);
  888.   },
  889.  
  890.  
  891.   normalizeUIKeys: function(hash) {
  892.     var ui = _.result(this, 'ui');
  893.     return Marionette.normalizeUIKeys(hash, ui);
  894.   },
  895.  
  896.   // Configure `triggers` to forward DOM events to view
  897.   // events. `triggers: {"click .foo": "do:foo"}`
  898.   configureTriggers: function(){
  899.     if (!this.triggers) { return; }
  900.  
  901.     var triggerEvents = {};
  902.  
  903.     // Allow `triggers` to be configured as a function
  904.     var triggers = this.normalizeUIKeys(_.result(this, "triggers"));
  905.  
  906.     // Configure the triggers, prevent default
  907.     // action and stop propagation of DOM events
  908.     _.each(triggers, function(value, key){
  909.  
  910.       var hasOptions = _.isObject(value);
  911.       var eventName = hasOptions ? value.event : value;
  912.  
  913.       // build the event handler function for the DOM event
  914.       triggerEvents[key] = function(e){
  915.  
  916.         // stop the event in its tracks
  917.         if (e) {
  918.           var prevent = e.preventDefault;
  919.           var stop = e.stopPropagation;
  920.  
  921.           var shouldPrevent = hasOptions ? value.preventDefault : prevent;
  922.           var shouldStop = hasOptions ? value.stopPropagation : stop;
  923.  
  924.           if (shouldPrevent && prevent) { prevent.apply(e); }
  925.           if (shouldStop && stop) { stop.apply(e); }
  926.         }
  927.  
  928.         // build the args for the event
  929.         var args = {
  930.           view: this,
  931.           model: this.model,
  932.           collection: this.collection
  933.         };
  934.  
  935.         // trigger the event
  936.         this.triggerMethod(eventName, args);
  937.       };
  938.  
  939.     }, this);
  940.  
  941.     return triggerEvents;
  942.   },
  943.  
  944.   // Overriding Backbone.View's delegateEvents to handle
  945.   // the `triggers`, `modelEvents`, and `collectionEvents` configuration
  946.   delegateEvents: function(events){
  947.     this._delegateDOMEvents(events);
  948.     Marionette.bindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
  949.     Marionette.bindEntityEvents(this, this.collection, Marionette.getOption(this, "collectionEvents"));
  950.   },
  951.  
  952.   // internal method to delegate DOM events and triggers
  953.   _delegateDOMEvents: function(events){
  954.     events = events || this.events;
  955.     if (_.isFunction(events)){ events = events.call(this); }
  956.  
  957.     var combinedEvents = {};
  958.  
  959.     // look up if this view has behavior events
  960.     var behaviorEvents = _.result(this, 'behaviorEvents') || {};
  961.     var triggers = this.configureTriggers();
  962.  
  963.     // behavior events will be overriden by view events and or triggers
  964.     _.extend(combinedEvents, behaviorEvents, events, triggers);
  965.  
  966.     Backbone.View.prototype.delegateEvents.call(this, combinedEvents);
  967.   },
  968.  
  969.   // Overriding Backbone.View's undelegateEvents to handle unbinding
  970.   // the `triggers`, `modelEvents`, and `collectionEvents` config
  971.   undelegateEvents: function(){
  972.     var args = Array.prototype.slice.call(arguments);
  973.     Backbone.View.prototype.undelegateEvents.apply(this, args);
  974.  
  975.     Marionette.unbindEntityEvents(this, this.model, Marionette.getOption(this, "modelEvents"));
  976.     Marionette.unbindEntityEvents(this, this.collection, Marionette.getOption(this, "collectionEvents"));
  977.   },
  978.  
  979.   // Internal method, handles the `show` event.
  980.   onShowCalled: function(){},
  981.  
  982.   // Default `close` implementation, for removing a view from the
  983.   // DOM and unbinding it. Regions will call this method
  984.   // for you. You can specify an `onClose` method in your view to
  985.   // add custom code that is called after the view is closed.
  986.   close: function(){
  987.     if (this.isClosed) { return; }
  988.  
  989.     var args = Array.prototype.slice.call(arguments);
  990.  
  991.     // allow the close to be stopped by returning `false`
  992.     // from the `onBeforeClose` method
  993.     var shouldClose = this.triggerMethod.apply(this, ["before:close"].concat(args));
  994.     if (shouldClose === false){
  995.       return;
  996.     }
  997.  
  998.     // mark as closed before doing the actual close, to
  999.     // prevent infinite loops within "close" event handlers
  1000.     // that are trying to close other views
  1001.     this.isClosed = true;
  1002.     this.triggerMethod.apply(this, ["close"].concat(args));
  1003.  
  1004.     // unbind UI elements
  1005.     this.unbindUIElements();
  1006.  
  1007.     // remove the view from the DOM
  1008.     this.remove();
  1009.   },
  1010.  
  1011.   // This method binds the elements specified in the "ui" hash inside the view's code with
  1012.   // the associated jQuery selectors.
  1013.   bindUIElements: function(){
  1014.     if (!this.ui) { return; }
  1015.  
  1016.     // store the ui hash in _uiBindings so they can be reset later
  1017.     // and so re-rendering the view will be able to find the bindings
  1018.     if (!this._uiBindings){
  1019.       this._uiBindings = this.ui;
  1020.     }
  1021.  
  1022.     // get the bindings result, as a function or otherwise
  1023.     var bindings = _.result(this, "_uiBindings");
  1024.  
  1025.     // empty the ui so we don't have anything to start with
  1026.     this.ui = {};
  1027.  
  1028.     // bind each of the selectors
  1029.     _.each(_.keys(bindings), function(key) {
  1030.       var selector = bindings[key];
  1031.       this.ui[key] = this.$(selector);
  1032.     }, this);
  1033.   },
  1034.  
  1035.   // This method unbinds the elements specified in the "ui" hash
  1036.   unbindUIElements: function(){
  1037.     if (!this.ui || !this._uiBindings){ return; }
  1038.  
  1039.     // delete all of the existing ui bindings
  1040.     _.each(this.ui, function($el, name){
  1041.       delete this.ui[name];
  1042.     }, this);
  1043.  
  1044.     // reset the ui element to the original bindings configuration
  1045.     this.ui = this._uiBindings;
  1046.     delete this._uiBindings;
  1047.   }
  1048. });
  1049.  
  1050. // Item View
  1051. // ---------
  1052.  
  1053. // A single item view implementation that contains code for rendering
  1054. // with underscore.js templates, serializing the view's model or collection,
  1055. // and calling several methods on extended views, such as `onRender`.
  1056. Marionette.ItemView = Marionette.View.extend({
  1057.  
  1058.   // Setting up the inheritance chain which allows changes to
  1059.   // Marionette.View.prototype.constructor which allows overriding
  1060.   constructor: function(){
  1061.     Marionette.View.prototype.constructor.apply(this, arguments);
  1062.   },
  1063.  
  1064.   // Serialize the model or collection for the view. If a model is
  1065.   // found, `.toJSON()` is called. If a collection is found, `.toJSON()`
  1066.   // is also called, but is used to populate an `items` array in the
  1067.   // resulting data. If both are found, defaults to the model.
  1068.   // You can override the `serializeData` method in your own view
  1069.   // definition, to provide custom serialization for your view's data.
  1070.   serializeData: function(){
  1071.     var data = {};
  1072.  
  1073.     if (this.model) {
  1074.       data = this.model.toJSON();
  1075.     }
  1076.     else if (this.collection) {
  1077.       data = { items: this.collection.toJSON() };
  1078.     }
  1079.  
  1080.     return data;
  1081.   },
  1082.  
  1083.   // Render the view, defaulting to underscore.js templates.
  1084.   // You can override this in your view definition to provide
  1085.   // a very specific rendering for your view. In general, though,
  1086.   // you should override the `Marionette.Renderer` object to
  1087.   // change how Marionette renders views.
  1088.   render: function(){
  1089.     this.isClosed = false;
  1090.  
  1091.     this.triggerMethod("before:render", this);
  1092.     this.triggerMethod("item:before:render", this);
  1093.  
  1094.     var data = this.serializeData();
  1095.     data = this.mixinTemplateHelpers(data);
  1096.  
  1097.     var template = this.getTemplate();
  1098.     var html = Marionette.Renderer.render(template, data);
  1099.  
  1100.     this.$el.html(html);
  1101.     this.bindUIElements();
  1102.  
  1103.     this.triggerMethod("render", this);
  1104.     this.triggerMethod("item:rendered", this);
  1105.  
  1106.     return this;
  1107.   },
  1108.  
  1109.   // Override the default close event to add a few
  1110.   // more events that are triggered.
  1111.   close: function(){
  1112.     if (this.isClosed){ return; }
  1113.  
  1114.     this.triggerMethod('item:before:close');
  1115.  
  1116.     Marionette.View.prototype.close.apply(this, arguments);
  1117.  
  1118.     this.triggerMethod('item:closed');
  1119.   }
  1120. });
  1121.  
  1122. // Collection View
  1123. // ---------------
  1124.  
  1125. // A view that iterates over a Backbone.Collection
  1126. // and renders an individual ItemView for each model.
  1127. Marionette.CollectionView = Marionette.View.extend({
  1128.   // used as the prefix for item view events
  1129.   // that are forwarded through the collectionview
  1130.   itemViewEventPrefix: "itemview",
  1131.  
  1132.   // constructor
  1133.   constructor: function(options){
  1134.     this._initChildViewStorage();
  1135.  
  1136.     Marionette.View.prototype.constructor.apply(this, arguments);
  1137.  
  1138.     this._initialEvents();
  1139.     this.initRenderBuffer();
  1140.   },
  1141.  
  1142.   // Instead of inserting elements one by one into the page,
  1143.   // it's much more performant to insert elements into a document
  1144.   // fragment and then insert that document fragment into the page
  1145.   initRenderBuffer: function() {
  1146.     this.elBuffer = document.createDocumentFragment();
  1147.     this._bufferedChildren = [];
  1148.   },
  1149.  
  1150.   startBuffering: function() {
  1151.     this.initRenderBuffer();
  1152.     this.isBuffering = true;
  1153.   },
  1154.  
  1155.   endBuffering: function() {
  1156.     this.isBuffering = false;
  1157.     this.appendBuffer(this, this.elBuffer);
  1158.     this._triggerShowBufferedChildren();
  1159.     this.initRenderBuffer();
  1160.   },
  1161.  
  1162.   _triggerShowBufferedChildren: function () {
  1163.     if (this._isShown) {
  1164.       _.each(this._bufferedChildren, function (child) {
  1165.         Marionette.triggerMethod.call(child, "show");
  1166.       });
  1167.       this._bufferedChildren = [];
  1168.     }
  1169.   },
  1170.  
  1171.   // Configured the initial events that the collection view
  1172.   // binds to.
  1173.   _initialEvents: function(){
  1174.     if (this.collection){
  1175.       this.listenTo(this.collection, "add", this.addChildView);
  1176.       this.listenTo(this.collection, "remove", this.removeItemView);
  1177.       this.listenTo(this.collection, "reset", this.render);
  1178.     }
  1179.   },
  1180.  
  1181.   // Handle a child item added to the collection
  1182.   addChildView: function(item, collection, options){
  1183.     this.closeEmptyView();
  1184.     var ItemView = this.getItemView(item);
  1185.     var index = this.collection.indexOf(item);
  1186.     this.addItemView(item, ItemView, index);
  1187.   },
  1188.  
  1189.   // Override from `Marionette.View` to guarantee the `onShow` method
  1190.   // of child views is called.
  1191.   onShowCalled: function(){
  1192.     this.children.each(function(child){
  1193.       Marionette.triggerMethod.call(child, "show");
  1194.     });
  1195.   },
  1196.  
  1197.   // Internal method to trigger the before render callbacks
  1198.   // and events
  1199.   triggerBeforeRender: function(){
  1200.     this.triggerMethod("before:render", this);
  1201.     this.triggerMethod("collection:before:render", this);
  1202.   },
  1203.  
  1204.   // Internal method to trigger the rendered callbacks and
  1205.   // events
  1206.   triggerRendered: function(){
  1207.     this.triggerMethod("render", this);
  1208.     this.triggerMethod("collection:rendered", this);
  1209.   },
  1210.  
  1211.   // Render the collection of items. Override this method to
  1212.   // provide your own implementation of a render function for
  1213.   // the collection view.
  1214.   render: function(){
  1215.     this.isClosed = false;
  1216.     this.triggerBeforeRender();
  1217.     this._renderChildren();
  1218.     this.triggerRendered();
  1219.     return this;
  1220.   },
  1221.  
  1222.   // Internal method. Separated so that CompositeView can have
  1223.   // more control over events being triggered, around the rendering
  1224.   // process
  1225.   _renderChildren: function(){
  1226.     this.startBuffering();
  1227.  
  1228.     this.closeEmptyView();
  1229.     this.closeChildren();
  1230.  
  1231.     if (!this.isEmpty(this.collection)) {
  1232.       this.showCollection();
  1233.     } else {
  1234.       this.showEmptyView();
  1235.     }
  1236.  
  1237.     this.endBuffering();
  1238.   },
  1239.  
  1240.   // Internal method to loop through each item in the
  1241.   // collection view and show it
  1242.   showCollection: function(){
  1243.     var ItemView;
  1244.     this.collection.each(function(item, index){
  1245.       ItemView = this.getItemView(item);
  1246.       this.addItemView(item, ItemView, index);
  1247.     }, this);
  1248.   },
  1249.  
  1250.   // Internal method to show an empty view in place of
  1251.   // a collection of item views, when the collection is
  1252.   // empty
  1253.   showEmptyView: function(){
  1254.     var EmptyView = this.getEmptyView();
  1255.  
  1256.     if (EmptyView && !this._showingEmptyView){
  1257.       this._showingEmptyView = true;
  1258.       var model = new Backbone.Model();
  1259.       this.addItemView(model, EmptyView, 0);
  1260.     }
  1261.   },
  1262.  
  1263.   // Internal method to close an existing emptyView instance
  1264.   // if one exists. Called when a collection view has been
  1265.   // rendered empty, and then an item is added to the collection.
  1266.   closeEmptyView: function(){
  1267.     if (this._showingEmptyView){
  1268.       this.closeChildren();
  1269.       delete this._showingEmptyView;
  1270.     }
  1271.   },
  1272.  
  1273.   // Retrieve the empty view type
  1274.   getEmptyView: function(){
  1275.     return Marionette.getOption(this, "emptyView");
  1276.   },
  1277.  
  1278.   // Retrieve the itemView type, either from `this.options.itemView`
  1279.   // or from the `itemView` in the object definition. The "options"
  1280.   // takes precedence.
  1281.   getItemView: function(item){
  1282.     var itemView = Marionette.getOption(this, "itemView");
  1283.  
  1284.     if (!itemView){
  1285.       throwError("An `itemView` must be specified", "NoItemViewError");
  1286.     }
  1287.  
  1288.     return itemView;
  1289.   },
  1290.  
  1291.   // Render the child item's view and add it to the
  1292.   // HTML for the collection view.
  1293.   addItemView: function(item, ItemView, index){
  1294.     // get the itemViewOptions if any were specified
  1295.     var itemViewOptions = Marionette.getOption(this, "itemViewOptions");
  1296.     if (_.isFunction(itemViewOptions)){
  1297.       itemViewOptions = itemViewOptions.call(this, item, index);
  1298.     }
  1299.  
  1300.     // build the view
  1301.     var view = this.buildItemView(item, ItemView, itemViewOptions);
  1302.  
  1303.     // set up the child view event forwarding
  1304.     this.addChildViewEventForwarding(view);
  1305.  
  1306.     // this view is about to be added
  1307.     this.triggerMethod("before:item:added", view);
  1308.  
  1309.     // Store the child view itself so we can properly
  1310.     // remove and/or close it later
  1311.     this.children.add(view);
  1312.  
  1313.     // Render it and show it
  1314.     this.renderItemView(view, index);
  1315.  
  1316.     // call the "show" method if the collection view
  1317.     // has already been shown
  1318.     if (this._isShown && !this.isBuffering){
  1319.       Marionette.triggerMethod.call(view, "show");
  1320.     }
  1321.  
  1322.     // this view was added
  1323.     this.triggerMethod("after:item:added", view);
  1324.  
  1325.     return view;
  1326.   },
  1327.  
  1328.   // Set up the child view event forwarding. Uses an "itemview:"
  1329.   // prefix in front of all forwarded events.
  1330.   addChildViewEventForwarding: function(view){
  1331.     var prefix = Marionette.getOption(this, "itemViewEventPrefix");
  1332.  
  1333.     // Forward all child item view events through the parent,
  1334.     // prepending "itemview:" to the event name
  1335.     this.listenTo(view, "all", function(){
  1336.       var args = slice.call(arguments);
  1337.       var rootEvent = args[0];
  1338.       var itemEvents = this.normalizeMethods(this.getItemEvents());
  1339.  
  1340.       args[0] = prefix + ":" + rootEvent;
  1341.       args.splice(1, 0, view);
  1342.  
  1343.       // call collectionView itemEvent if defined
  1344.       if (typeof itemEvents !== "undefined" && _.isFunction(itemEvents[rootEvent])) {
  1345.         itemEvents[rootEvent].apply(this, args);
  1346.       }
  1347.  
  1348.       Marionette.triggerMethod.apply(this, args);
  1349.     }, this);
  1350.   },
  1351.  
  1352.   // returns the value of itemEvents depending on if a function
  1353.   getItemEvents: function() {
  1354.     if (_.isFunction(this.itemEvents)) {
  1355.       return this.itemEvents.call(this);
  1356.     }
  1357.  
  1358.     return this.itemEvents;
  1359.   },
  1360.  
  1361.   // render the item view
  1362.   renderItemView: function(view, index) {
  1363.     view.render();
  1364.     this.appendHtml(this, view, index);
  1365.   },
  1366.  
  1367.   // Build an `itemView` for every model in the collection.
  1368.   buildItemView: function(item, ItemViewType, itemViewOptions){
  1369.     var options = _.extend({model: item}, itemViewOptions);
  1370.     return new ItemViewType(options);
  1371.   },
  1372.  
  1373.   // get the child view by item it holds, and remove it
  1374.   removeItemView: function(item){
  1375.     var view = this.children.findByModel(item);
  1376.     this.removeChildView(view);
  1377.     this.checkEmpty();
  1378.   },
  1379.  
  1380.   // Remove the child view and close it
  1381.   removeChildView: function(view){
  1382.  
  1383.     // shut down the child view properly,
  1384.     // including events that the collection has from it
  1385.     if (view){
  1386.       this.stopListening(view);
  1387.  
  1388.       // call 'close' or 'remove', depending on which is found
  1389.       if (view.close) { view.close(); }
  1390.       else if (view.remove) { view.remove(); }
  1391.  
  1392.       this.children.remove(view);
  1393.     }
  1394.  
  1395.     this.triggerMethod("item:removed", view);
  1396.   },
  1397.  
  1398.   // helper to check if the collection is empty
  1399.   isEmpty: function(collection){
  1400.     // check if we're empty now
  1401.     return !this.collection || this.collection.length === 0;
  1402.   },
  1403.  
  1404.   // If empty, show the empty view
  1405.   checkEmpty: function (){
  1406.     if (this.isEmpty(this.collection)){
  1407.       this.showEmptyView();
  1408.     }
  1409.   },
  1410.  
  1411.   // You might need to override this if you've overridden appendHtml
  1412.   appendBuffer: function(collectionView, buffer) {
  1413.     collectionView.$el.append(buffer);
  1414.   },
  1415.  
  1416.   // Append the HTML to the collection's `el`.
  1417.   // Override this method to do something other
  1418.   // than `.append`.
  1419.   appendHtml: function(collectionView, itemView, index){
  1420.     if (collectionView.isBuffering) {
  1421.       // buffering happens on reset events and initial renders
  1422.       // in order to reduce the number of inserts into the
  1423.       // document, which are expensive.
  1424.       collectionView.elBuffer.appendChild(itemView.el);
  1425.       collectionView._bufferedChildren.push(itemView);
  1426.     }
  1427.     else {
  1428.       // If we've already rendered the main collection, just
  1429.       // append the new items directly into the element.
  1430.       collectionView.$el.append(itemView.el);
  1431.     }
  1432.   },
  1433.  
  1434.   // Internal method to set up the `children` object for
  1435.   // storing all of the child views
  1436.   _initChildViewStorage: function(){
  1437.     this.children = new Backbone.ChildViewContainer();
  1438.   },
  1439.  
  1440.   // Handle cleanup and other closing needs for
  1441.   // the collection of views.
  1442.   close: function(){
  1443.     if (this.isClosed){ return; }
  1444.  
  1445.     this.triggerMethod("collection:before:close");
  1446.     this.closeChildren();
  1447.     this.triggerMethod("collection:closed");
  1448.  
  1449.     Marionette.View.prototype.close.apply(this, arguments);
  1450.   },
  1451.  
  1452.   // Close the child views that this collection view
  1453.   // is holding on to, if any
  1454.   closeChildren: function(){
  1455.     this.children.each(function(child){
  1456.       this.removeChildView(child);
  1457.     }, this);
  1458.     this.checkEmpty();
  1459.   }
  1460. });
  1461.  
  1462.  
  1463. // Composite View
  1464. // --------------
  1465.  
  1466. // Used for rendering a branch-leaf, hierarchical structure.
  1467. // Extends directly from CollectionView and also renders an
  1468. // an item view as `modelView`, for the top leaf
  1469. Marionette.CompositeView = Marionette.CollectionView.extend({
  1470.  
  1471.   // Setting up the inheritance chain which allows changes to
  1472.   // Marionette.CollectionView.prototype.constructor which allows overriding
  1473.   constructor: function(){
  1474.     Marionette.CollectionView.prototype.constructor.apply(this, arguments);
  1475.   },
  1476.  
  1477.   // Configured the initial events that the composite view
  1478.   // binds to. Override this method to prevent the initial
  1479.   // events, or to add your own initial events.
  1480.   _initialEvents: function(){
  1481.  
  1482.     // Bind only after composite view is rendered to avoid adding child views
  1483.     // to nonexistent itemViewContainer
  1484.     this.once('render', function () {
  1485.       if (this.collection){
  1486.         this.listenTo(this.collection, "add", this.addChildView);
  1487.         this.listenTo(this.collection, "remove", this.removeItemView);
  1488.         this.listenTo(this.collection, "reset", this._renderChildren);
  1489.       }
  1490.     });
  1491.  
  1492.   },
  1493.  
  1494.   // Retrieve the `itemView` to be used when rendering each of
  1495.   // the items in the collection. The default is to return
  1496.   // `this.itemView` or Marionette.CompositeView if no `itemView`
  1497.   // has been defined
  1498.   getItemView: function(item){
  1499.     var itemView = Marionette.getOption(this, "itemView") || this.constructor;
  1500.  
  1501.     if (!itemView){
  1502.       throwError("An `itemView` must be specified", "NoItemViewError");
  1503.     }
  1504.  
  1505.     return itemView;
  1506.   },
  1507.  
  1508.   // Serialize the collection for the view.
  1509.   // You can override the `serializeData` method in your own view
  1510.   // definition, to provide custom serialization for your view's data.
  1511.   serializeData: function(){
  1512.     var data = {};
  1513.  
  1514.     if (this.model){
  1515.       data = this.model.toJSON();
  1516.     }
  1517.  
  1518.     return data;
  1519.   },
  1520.  
  1521.   // Renders the model once, and the collection once. Calling
  1522.   // this again will tell the model's view to re-render itself
  1523.   // but the collection will not re-render.
  1524.   render: function(){
  1525.     this.isRendered = true;
  1526.     this.isClosed = false;
  1527.     this.resetItemViewContainer();
  1528.  
  1529.     this.triggerBeforeRender();
  1530.     var html = this.renderModel();
  1531.     this.$el.html(html);
  1532.     // the ui bindings is done here and not at the end of render since they
  1533.     // will not be available until after the model is rendered, but should be
  1534.     // available before the collection is rendered.
  1535.     this.bindUIElements();
  1536.     this.triggerMethod("composite:model:rendered");
  1537.  
  1538.     this._renderChildren();
  1539.  
  1540.     this.triggerMethod("composite:rendered");
  1541.     this.triggerRendered();
  1542.     return this;
  1543.   },
  1544.  
  1545.   _renderChildren: function(){
  1546.     if (this.isRendered){
  1547.       this.triggerMethod("composite:collection:before:render");
  1548.       Marionette.CollectionView.prototype._renderChildren.call(this);
  1549.       this.triggerMethod("composite:collection:rendered");
  1550.     }
  1551.   },
  1552.  
  1553.   // Render an individual model, if we have one, as
  1554.   // part of a composite view (branch / leaf). For example:
  1555.   // a treeview.
  1556.   renderModel: function(){
  1557.     var data = {};
  1558.     data = this.serializeData();
  1559.     data = this.mixinTemplateHelpers(data);
  1560.  
  1561.     var template = this.getTemplate();
  1562.     return Marionette.Renderer.render(template, data);
  1563.   },
  1564.  
  1565.  
  1566.   // You might need to override this if you've overridden appendHtml
  1567.   appendBuffer: function(compositeView, buffer) {
  1568.     var $container = this.getItemViewContainer(compositeView);
  1569.     $container.append(buffer);
  1570.   },
  1571.  
  1572.   // Appends the `el` of itemView instances to the specified
  1573.   // `itemViewContainer` (a jQuery selector). Override this method to
  1574.   // provide custom logic of how the child item view instances have their
  1575.   // HTML appended to the composite view instance.
  1576.   appendHtml: function(compositeView, itemView, index){
  1577.     if (compositeView.isBuffering) {
  1578.       compositeView.elBuffer.appendChild(itemView.el);
  1579.       compositeView._bufferedChildren.push(itemView);
  1580.     }
  1581.     else {
  1582.       // If we've already rendered the main collection, just
  1583.       // append the new items directly into the element.
  1584.       var $container = this.getItemViewContainer(compositeView);
  1585.       $container.append(itemView.el);
  1586.     }
  1587.   },
  1588.  
  1589.  
  1590.   // Internal method to ensure an `$itemViewContainer` exists, for the
  1591.   // `appendHtml` method to use.
  1592.   getItemViewContainer: function(containerView){
  1593.     if ("$itemViewContainer" in containerView){
  1594.       return containerView.$itemViewContainer;
  1595.     }
  1596.  
  1597.     var container;
  1598.     var itemViewContainer = Marionette.getOption(containerView, "itemViewContainer");
  1599.     if (itemViewContainer){
  1600.  
  1601.       var selector = _.isFunction(itemViewContainer) ? itemViewContainer.call(this) : itemViewContainer;
  1602.       container = containerView.$(selector);
  1603.       if (container.length <= 0) {
  1604.         throwError("The specified `itemViewContainer` was not found: " + containerView.itemViewContainer, "ItemViewContainerMissingError");
  1605.       }
  1606.  
  1607.     } else {
  1608.       container = containerView.$el;
  1609.     }
  1610.  
  1611.     containerView.$itemViewContainer = container;
  1612.     return container;
  1613.   },
  1614.  
  1615.   // Internal method to reset the `$itemViewContainer` on render
  1616.   resetItemViewContainer: function(){
  1617.     if (this.$itemViewContainer){
  1618.       delete this.$itemViewContainer;
  1619.     }
  1620.   }
  1621. });
  1622.  
  1623.  
  1624. // Layout
  1625. // ------
  1626.  
  1627. // Used for managing application layouts, nested layouts and
  1628. // multiple regions within an application or sub-application.
  1629. //
  1630. // A specialized view type that renders an area of HTML and then
  1631. // attaches `Region` instances to the specified `regions`.
  1632. // Used for composite view management and sub-application areas.
  1633. Marionette.Layout = Marionette.ItemView.extend({
  1634.   regionType: Marionette.Region,
  1635.  
  1636.   // Ensure the regions are available when the `initialize` method
  1637.   // is called.
  1638.   constructor: function (options) {
  1639.     options = options || {};
  1640.  
  1641.     this._firstRender = true;
  1642.     this._initializeRegions(options);
  1643.  
  1644.     Marionette.ItemView.prototype.constructor.call(this, options);
  1645.   },
  1646.  
  1647.   // Layout's render will use the existing region objects the
  1648.   // first time it is called. Subsequent calls will close the
  1649.   // views that the regions are showing and then reset the `el`
  1650.   // for the regions to the newly rendered DOM elements.
  1651.   render: function(){
  1652.  
  1653.     if (this.isClosed){
  1654.       // a previously closed layout means we need to
  1655.       // completely re-initialize the regions
  1656.       this._initializeRegions();
  1657.     }
  1658.     if (this._firstRender) {
  1659.       // if this is the first render, don't do anything to
  1660.       // reset the regions
  1661.       this._firstRender = false;
  1662.     } else if (!this.isClosed){
  1663.       // If this is not the first render call, then we need to
  1664.       // re-initializing the `el` for each region
  1665.       this._reInitializeRegions();
  1666.     }
  1667.  
  1668.     return Marionette.ItemView.prototype.render.apply(this, arguments);
  1669.   },
  1670.  
  1671.   // Handle closing regions, and then close the view itself.
  1672.   close: function () {
  1673.     if (this.isClosed){ return; }
  1674.     this.regionManager.close();
  1675.     Marionette.ItemView.prototype.close.apply(this, arguments);
  1676.   },
  1677.  
  1678.   // Add a single region, by name, to the layout
  1679.   addRegion: function(name, definition){
  1680.     var regions = {};
  1681.     regions[name] = definition;
  1682.     return this._buildRegions(regions)[name];
  1683.   },
  1684.  
  1685.   // Add multiple regions as a {name: definition, name2: def2} object literal
  1686.   addRegions: function(regions){
  1687.     this.regions = _.extend({}, this.regions, regions);
  1688.     return this._buildRegions(regions);
  1689.   },
  1690.  
  1691.   // Remove a single region from the Layout, by name
  1692.   removeRegion: function(name){
  1693.     delete this.regions[name];
  1694.     return this.regionManager.removeRegion(name);
  1695.   },
  1696.  
  1697.   // internal method to build regions
  1698.   _buildRegions: function(regions){
  1699.     var that = this;
  1700.  
  1701.     var defaults = {
  1702.       regionType: Marionette.getOption(this, "regionType"),
  1703.       parentEl: function(){ return that.$el; }
  1704.     };
  1705.  
  1706.     return this.regionManager.addRegions(regions, defaults);
  1707.   },
  1708.  
  1709.   // Internal method to initialize the regions that have been defined in a
  1710.   // `regions` attribute on this layout.
  1711.   _initializeRegions: function (options) {
  1712.     var regions;
  1713.     this._initRegionManager();
  1714.  
  1715.     if (_.isFunction(this.regions)) {
  1716.       regions = this.regions(options);
  1717.     } else {
  1718.       regions = this.regions || {};
  1719.     }
  1720.  
  1721.     this.addRegions(regions);
  1722.   },
  1723.  
  1724.   // Internal method to re-initialize all of the regions by updating the `el` that
  1725.   // they point to
  1726.   _reInitializeRegions: function(){
  1727.     this.regionManager.closeRegions();
  1728.     this.regionManager.each(function(region){
  1729.       region.reset();
  1730.     });
  1731.   },
  1732.  
  1733.   // Internal method to initialize the region manager
  1734.   // and all regions in it
  1735.   _initRegionManager: function(){
  1736.     this.regionManager = new Marionette.RegionManager();
  1737.  
  1738.     this.listenTo(this.regionManager, "region:add", function(name, region){
  1739.       this[name] = region;
  1740.       this.trigger("region:add", name, region);
  1741.     });
  1742.  
  1743.     this.listenTo(this.regionManager, "region:remove", function(name, region){
  1744.       delete this[name];
  1745.       this.trigger("region:remove", name, region);
  1746.     });
  1747.   }
  1748. });
  1749.  
  1750.  
  1751. Marionette.Behavior = (function(_, Backbone){
  1752.   function Behavior(options, view){
  1753.     this.view = view;
  1754.     this.defaults = _.result(this, "defaults") || {};
  1755.     this.options  = _.extend({}, this.defaults, options);
  1756.  
  1757.     // proxy behavior $ method to the view
  1758.     this.$ = function() {
  1759.       return this.view.$.apply(this.view, arguments);
  1760.     };
  1761.  
  1762.     this.initialize.apply(this, arguments);
  1763.   }
  1764.  
  1765.   _.extend(Behavior.prototype, {
  1766.     initialize: function(){},
  1767.  
  1768.     triggerMethod: Marionette.triggerMethod
  1769.   });
  1770.  
  1771.   // Borrow Backbones extend implementation
  1772.   _.extend(Behavior, {
  1773.     extend: Backbone.View.extend
  1774.   });
  1775.  
  1776.   return Behavior;
  1777. })(_, Backbone);
  1778.  
  1779. Marionette.Behaviors = (function(Marionette, _) {
  1780.  
  1781.   function Behaviors(view) {
  1782.     this.behaviors = Behaviors.parseBehaviors(view, view.behaviors);
  1783.  
  1784.     Behaviors.wrap(view, this.behaviors, [
  1785.       'bindUIElements', 'unbindUIElements',
  1786.       'delegateEvents', 'undelegateEvents',
  1787.       'onShow', 'onClose',
  1788.       'behaviorEvents', 'triggerMethod',
  1789.       'setElement'
  1790.     ]);
  1791.   }
  1792.  
  1793.   var methods = {
  1794.     setElement: function(setElement, behaviors) {
  1795.       setElement.apply(this, _.tail(arguments, 2));
  1796.  
  1797.       // proxy behavior $el to the view's $el
  1798.       _.each(behaviors, function(b) {
  1799.         b.$el = this.$el;
  1800.       }, this);
  1801.     },
  1802.  
  1803.     onShow: function(onShow, behaviors) {
  1804.       var args = _.tail(arguments, 2);
  1805.  
  1806.       _.each(behaviors, function(b) {
  1807.         Marionette.triggerMethod.apply(b, ["show"].concat(args));
  1808.       });
  1809.  
  1810.       if (_.isFunction(onShow)) {
  1811.         onShow.apply(this, args);
  1812.       }
  1813.     },
  1814.  
  1815.     onClose: function(onClose, behaviors){
  1816.       var args = _.tail(arguments, 2);
  1817.  
  1818.       _.each(behaviors, function(b) {
  1819.         Marionette.triggerMethod.apply(b, ["close"].concat(args));
  1820.       });
  1821.  
  1822.       if (_.isFunction(onClose)) {
  1823.         onClose.apply(this, args);
  1824.       }
  1825.     },
  1826.  
  1827.     bindUIElements: function(bindUIElements, behaviors) {
  1828.       bindUIElements.apply(this);
  1829.       _.invoke(behaviors, bindUIElements);
  1830.     },
  1831.  
  1832.     unbindUIElements: function(unbindUIElements, behaviors) {
  1833.       unbindUIElements.apply(this);
  1834.       _.invoke(behaviors, unbindUIElements);
  1835.     },
  1836.  
  1837.     triggerMethod: function(triggerMethod, behaviors) {
  1838.       var args = _.tail(arguments, 2);
  1839.       triggerMethod.apply(this, args);
  1840.  
  1841.       _.each(behaviors, function(b) {
  1842.         triggerMethod.apply(b, args);
  1843.       });
  1844.     },
  1845.  
  1846.     delegateEvents: function(delegateEvents, behaviors) {
  1847.       var args = _.tail(arguments, 2);
  1848.       delegateEvents.apply(this, args);
  1849.  
  1850.       _.each(behaviors, function(b){
  1851.         Marionette.bindEntityEvents(this, this.model, Marionette.getOption(b, "modelEvents"));
  1852.         Marionette.bindEntityEvents(this, this.collection, Marionette.getOption(b, "collectionEvents"));
  1853.       }, this);
  1854.     },
  1855.  
  1856.     undelegateEvents: function(undelegateEvents, behaviors) {
  1857.       var args = _.tail(arguments, 2);
  1858.       undelegateEvents.apply(this, args);
  1859.  
  1860.       _.each(behaviors, function(b) {
  1861.         Marionette.unbindEntityEvents(this, this.model, Marionette.getOption(b, "modelEvents"));
  1862.         Marionette.unbindEntityEvents(this, this.collection, Marionette.getOption(b, "collectionEvents"));
  1863.       }, this);
  1864.     },
  1865.  
  1866.     behaviorEvents: function(behaviorEvents, behaviors) {
  1867.       var _behaviorsEvents = {};
  1868.       var viewUI = _.result(this, 'ui');
  1869.  
  1870.       _.each(behaviors, function(b, i) {
  1871.         var _events = {};
  1872.         var behaviorEvents = _.result(b, 'events') || {};
  1873.         var behaviorUI = _.result(b, 'ui');
  1874.         var ui = _.extend({}, viewUI, behaviorUI);
  1875.  
  1876.         behaviorEvents = Marionette.normalizeUIKeys(behaviorEvents, ui);
  1877.  
  1878.         _.each(_.keys(behaviorEvents), function(key) {
  1879.           // append white-space at the end of each key to prevent behavior key collisions
  1880.           // this is relying on the fact backbone events considers "click .foo" the same  "click .foo "
  1881.           // starts with an array of two so the first behavior has one space
  1882.           var whitespace = (new Array(i+2)).join(" ");
  1883.           var eventKey   = key + whitespace;
  1884.           var handler    = _.isFunction(behaviorEvents[key]) ? behaviorEvents[key] : b[behaviorEvents[key]];
  1885.  
  1886.           _events[eventKey] = _.bind(handler, b);
  1887.         });
  1888.  
  1889.         _behaviorsEvents = _.extend(_behaviorsEvents, _events);
  1890.       });
  1891.  
  1892.       return _behaviorsEvents;
  1893.     }
  1894.  };
  1895.  
  1896.   _.extend(Behaviors, {
  1897.  
  1898.     // placeholder method to be extended by the user
  1899.     // should define the object that stores the behaviors
  1900.     // i.e.
  1901.     //
  1902.     // Marionette.Behaviors.behaviorsLookup: function() {
  1903.     //   return App.Behaviors
  1904.     // }
  1905.     behaviorsLookup: function() {
  1906.       throw new Error("You must define where your behaviors are stored. See https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.behaviors.md#behaviorslookup");
  1907.     },
  1908.  
  1909.     getBehaviorClass: function(options, key) {
  1910.       if (options.behaviorClass) {
  1911.         return options.behaviorClass;
  1912.       }
  1913.  
  1914.       // Get behavior class can be either a flat object or a method
  1915.       return _.isFunction(Behaviors.behaviorsLookup) ? Behaviors.behaviorsLookup.apply(this, arguments)[key] : Behaviors.behaviorsLookup[key];
  1916.     },
  1917.  
  1918.     parseBehaviors: function(view, behaviors){
  1919.       return _.map(behaviors, function(options, key){
  1920.         var BehaviorClass = Behaviors.getBehaviorClass(options, key);
  1921.         return new BehaviorClass(options, view);
  1922.       });
  1923.     },
  1924.  
  1925.     // wrap view internal methods so that they delegate to behaviors.
  1926.     // For example, onClose should trigger close on all of the behaviors and then close itself.
  1927.     // i.e.
  1928.     //
  1929.     // view.delegateEvents = _.partial(methods.delegateEvents, view.delegateEvents, behaviors);
  1930.     wrap: function(view, behaviors, methodNames) {
  1931.       _.each(methodNames, function(methodName) {
  1932.         view[methodName] = _.partial(methods[methodName], view[methodName], behaviors);
  1933.       });
  1934.     }
  1935.   });
  1936.  
  1937.   return Behaviors;
  1938.  
  1939. })(Marionette, _);
  1940.  
  1941.  
  1942. // AppRouter
  1943. // ---------
  1944.  
  1945. // Reduce the boilerplate code of handling route events
  1946. // and then calling a single method on another object.
  1947. // Have your routers configured to call the method on
  1948. // your object, directly.
  1949. //
  1950. // Configure an AppRouter with `appRoutes`.
  1951. //
  1952. // App routers can only take one `controller` object.
  1953. // It is recommended that you divide your controller
  1954. // objects in to smaller pieces of related functionality
  1955. // and have multiple routers / controllers, instead of
  1956. // just one giant router and controller.
  1957. //
  1958. // You can also add standard routes to an AppRouter.
  1959.  
  1960. Marionette.AppRouter = Backbone.Router.extend({
  1961.  
  1962.   constructor: function(options){
  1963.     Backbone.Router.prototype.constructor.apply(this, arguments);
  1964.        
  1965.     this.options = options || {};
  1966.  
  1967.     var appRoutes = Marionette.getOption(this, "appRoutes");
  1968.     var controller = this._getController();
  1969.     this.processAppRoutes(controller, appRoutes);
  1970.   },
  1971.  
  1972.   // Similar to route method on a Backbone Router but
  1973.   // method is called on the controller
  1974.   appRoute: function(route, methodName) {
  1975.     var controller = this._getController();
  1976.     this._addAppRoute(controller, route, methodName);
  1977.   },
  1978.  
  1979.   // Internal method to process the `appRoutes` for the
  1980.   // router, and turn them in to routes that trigger the
  1981.   // specified method on the specified `controller`.
  1982.   processAppRoutes: function(controller, appRoutes) {
  1983.     if (!appRoutes){ return; }
  1984.  
  1985.     var routeNames = _.keys(appRoutes).reverse(); // Backbone requires reverted order of routes
  1986.  
  1987.     _.each(routeNames, function(route) {
  1988.       this._addAppRoute(controller, route, appRoutes[route]);
  1989.     }, this);
  1990.   },
  1991.  
  1992.   _getController: function(){
  1993.     return Marionette.getOption(this, "controller");
  1994.   },
  1995.  
  1996.   _addAppRoute: function(controller, route, methodName){
  1997.     var method = controller[methodName];
  1998.  
  1999.     if (!method) {
  2000.       throwError("Method '" + methodName + "' was not found on the controller");
  2001.     }
  2002.  
  2003.     this.route(route, methodName, _.bind(method, controller));
  2004.   }
  2005. });
  2006.  
  2007.  
  2008. // Application
  2009. // -----------
  2010.  
  2011. // Contain and manage the composite application as a whole.
  2012. // Stores and starts up `Region` objects, includes an
  2013. // event aggregator as `app.vent`
  2014. Marionette.Application = function(options){
  2015.   this._initRegionManager();
  2016.   this._initCallbacks = new Marionette.Callbacks();
  2017.   this.vent = new Backbone.Wreqr.EventAggregator();
  2018.   this.commands = new Backbone.Wreqr.Commands();
  2019.   this.reqres = new Backbone.Wreqr.RequestResponse();
  2020.   this.submodules = {};
  2021.  
  2022.   _.extend(this, options);
  2023.  
  2024.   this.triggerMethod = Marionette.triggerMethod;
  2025. };
  2026.  
  2027. _.extend(Marionette.Application.prototype, Backbone.Events, {
  2028.   // Command execution, facilitated by Backbone.Wreqr.Commands
  2029.   execute: function(){
  2030.     this.commands.execute.apply(this.commands, arguments);
  2031.   },
  2032.  
  2033.   // Request/response, facilitated by Backbone.Wreqr.RequestResponse
  2034.   request: function(){
  2035.     return this.reqres.request.apply(this.reqres, arguments);
  2036.   },
  2037.  
  2038.   // Add an initializer that is either run at when the `start`
  2039.   // method is called, or run immediately if added after `start`
  2040.   // has already been called.
  2041.   addInitializer: function(initializer){
  2042.     this._initCallbacks.add(initializer);
  2043.   },
  2044.  
  2045.   // kick off all of the application's processes.
  2046.   // initializes all of the regions that have been added
  2047.   // to the app, and runs all of the initializer functions
  2048.   start: function(options){
  2049.     this.triggerMethod("initialize:before", options);
  2050.     this._initCallbacks.run(options, this);
  2051.     this.triggerMethod("initialize:after", options);
  2052.  
  2053.     this.triggerMethod("start", options);
  2054.   },
  2055.  
  2056.   // Add regions to your app.
  2057.   // Accepts a hash of named strings or Region objects
  2058.   // addRegions({something: "#someRegion"})
  2059.   // addRegions({something: Region.extend({el: "#someRegion"}) });
  2060.   addRegions: function(regions){
  2061.     return this._regionManager.addRegions(regions);
  2062.   },
  2063.  
  2064.   // Close all regions in the app, without removing them
  2065.   closeRegions: function(){
  2066.     this._regionManager.closeRegions();
  2067.   },
  2068.  
  2069.   // Removes a region from your app, by name
  2070.   // Accepts the regions name
  2071.   // removeRegion('myRegion')
  2072.   removeRegion: function(region) {
  2073.     this._regionManager.removeRegion(region);
  2074.   },
  2075.  
  2076.   // Provides alternative access to regions
  2077.   // Accepts the region name
  2078.   // getRegion('main')
  2079.   getRegion: function(region) {
  2080.     return this._regionManager.get(region);
  2081.   },
  2082.  
  2083.   // Create a module, attached to the application
  2084.   module: function(moduleNames, moduleDefinition){
  2085.  
  2086.     // Overwrite the module class if the user specifies one
  2087.     var ModuleClass = Marionette.Module.getClass(moduleDefinition);
  2088.  
  2089.     // slice the args, and add this application object as the
  2090.     // first argument of the array
  2091.     var args = slice.call(arguments);
  2092.     args.unshift(this);
  2093.  
  2094.     // see the Marionette.Module object for more information
  2095.     return ModuleClass.create.apply(ModuleClass, args);
  2096.   },
  2097.  
  2098.   // Internal method to set up the region manager
  2099.   _initRegionManager: function(){
  2100.     this._regionManager = new Marionette.RegionManager();
  2101.  
  2102.     this.listenTo(this._regionManager, "region:add", function(name, region){
  2103.       this[name] = region;
  2104.     });
  2105.  
  2106.     this.listenTo(this._regionManager, "region:remove", function(name, region){
  2107.       delete this[name];
  2108.     });
  2109.   }
  2110. });
  2111.  
  2112. // Copy the `extend` function used by Backbone's classes
  2113. Marionette.Application.extend = Marionette.extend;
  2114.  
  2115. // Module
  2116. // ------
  2117.  
  2118. // A simple module system, used to create privacy and encapsulation in
  2119. // Marionette applications
  2120. Marionette.Module = function(moduleName, app, options){
  2121.   this.moduleName = moduleName;
  2122.   this.options = _.extend({}, this.options, options);
  2123.   this.initialize = options.initialize || this.initialize;
  2124.  
  2125.   // store sub-modules
  2126.   this.submodules = {};
  2127.  
  2128.   this._setupInitializersAndFinalizers();
  2129.  
  2130.   // store the configuration for this module
  2131.   this.app = app;
  2132.   this.startWithParent = true;
  2133.  
  2134.   this.triggerMethod = Marionette.triggerMethod;
  2135.  
  2136.   if (_.isFunction(this.initialize)){
  2137.     this.initialize(this.options, moduleName, app);
  2138.   }
  2139. };
  2140.  
  2141. Marionette.Module.extend = Marionette.extend;
  2142.  
  2143. // Extend the Module prototype with events / listenTo, so that the module
  2144. // can be used as an event aggregator or pub/sub.
  2145. _.extend(Marionette.Module.prototype, Backbone.Events, {
  2146.  
  2147.   // Initialize is an empty function by default. Override it with your own
  2148.   // initialization logic when extending Marionette.Module.
  2149.   initialize: function(){},
  2150.  
  2151.   // Initializer for a specific module. Initializers are run when the
  2152.   // module's `start` method is called.
  2153.   addInitializer: function(callback){
  2154.     this._initializerCallbacks.add(callback);
  2155.   },
  2156.  
  2157.   // Finalizers are run when a module is stopped. They are used to teardown
  2158.   // and finalize any variables, references, events and other code that the
  2159.   // module had set up.
  2160.   addFinalizer: function(callback){
  2161.     this._finalizerCallbacks.add(callback);
  2162.   },
  2163.  
  2164.   // Start the module, and run all of its initializers
  2165.   start: function(options){
  2166.     // Prevent re-starting a module that is already started
  2167.     if (this._isInitialized){ return; }
  2168.  
  2169.     // start the sub-modules (depth-first hierarchy)
  2170.     _.each(this.submodules, function(mod){
  2171.       // check to see if we should start the sub-module with this parent
  2172.       if (mod.startWithParent){
  2173.         mod.start(options);
  2174.       }
  2175.     });
  2176.  
  2177.     // run the callbacks to "start" the current module
  2178.     this.triggerMethod("before:start", options);
  2179.  
  2180.     this._initializerCallbacks.run(options, this);
  2181.     this._isInitialized = true;
  2182.  
  2183.     this.triggerMethod("start", options);
  2184.   },
  2185.  
  2186.   // Stop this module by running its finalizers and then stop all of
  2187.   // the sub-modules for this module
  2188.   stop: function(){
  2189.     // if we are not initialized, don't bother finalizing
  2190.     if (!this._isInitialized){ return; }
  2191.     this._isInitialized = false;
  2192.  
  2193.     Marionette.triggerMethod.call(this, "before:stop");
  2194.  
  2195.     // stop the sub-modules; depth-first, to make sure the
  2196.     // sub-modules are stopped / finalized before parents
  2197.     _.each(this.submodules, function(mod){ mod.stop(); });
  2198.  
  2199.     // run the finalizers
  2200.     this._finalizerCallbacks.run(undefined,this);
  2201.  
  2202.     // reset the initializers and finalizers
  2203.     this._initializerCallbacks.reset();
  2204.     this._finalizerCallbacks.reset();
  2205.  
  2206.     Marionette.triggerMethod.call(this, "stop");
  2207.   },
  2208.  
  2209.   // Configure the module with a definition function and any custom args
  2210.   // that are to be passed in to the definition function
  2211.   addDefinition: function(moduleDefinition, customArgs){
  2212.     this._runModuleDefinition(moduleDefinition, customArgs);
  2213.   },
  2214.  
  2215.   // Internal method: run the module definition function with the correct
  2216.   // arguments
  2217.   _runModuleDefinition: function(definition, customArgs){
  2218.     if (!definition){ return; }
  2219.  
  2220.     // build the correct list of arguments for the module definition
  2221.     var args = _.flatten([
  2222.       this,
  2223.       this.app,
  2224.       Backbone,
  2225.       Marionette,
  2226.       Marionette.$, _,
  2227.       customArgs
  2228.     ]);
  2229.  
  2230.     definition.apply(this, args);
  2231.   },
  2232.  
  2233.   // Internal method: set up new copies of initializers and finalizers.
  2234.   // Calling this method will wipe out all existing initializers and
  2235.   // finalizers.
  2236.   _setupInitializersAndFinalizers: function(){
  2237.     this._initializerCallbacks = new Marionette.Callbacks();
  2238.     this._finalizerCallbacks = new Marionette.Callbacks();
  2239.   }
  2240. });
  2241.  
  2242. // Type methods to create modules
  2243. _.extend(Marionette.Module, {
  2244.  
  2245.   // Create a module, hanging off the app parameter as the parent object.
  2246.   create: function(app, moduleNames, moduleDefinition){
  2247.     var module = app;
  2248.  
  2249.     // get the custom args passed in after the module definition and
  2250.     // get rid of the module name and definition function
  2251.     var customArgs = slice.call(arguments);
  2252.     customArgs.splice(0, 3);
  2253.  
  2254.     // split the module names and get the length
  2255.     moduleNames = moduleNames.split(".");
  2256.     var length = moduleNames.length;
  2257.  
  2258.     // store the module definition for the last module in the chain
  2259.     var moduleDefinitions = [];
  2260.     moduleDefinitions[length-1] = moduleDefinition;
  2261.  
  2262.     // Loop through all the parts of the module definition
  2263.     _.each(moduleNames, function(moduleName, i){
  2264.       var parentModule = module;
  2265.       module = this._getModule(parentModule, moduleName, app, moduleDefinition);
  2266.       this._addModuleDefinition(parentModule, module, moduleDefinitions[i], customArgs);
  2267.     }, this);
  2268.  
  2269.     // Return the last module in the definition chain
  2270.     return module;
  2271.   },
  2272.  
  2273.   _getModule: function(parentModule, moduleName, app, def, args){
  2274.     var options = _.extend({}, def);
  2275.     var ModuleClass = this.getClass(def);
  2276.  
  2277.     // Get an existing module of this name if we have one
  2278.     var module = parentModule[moduleName];
  2279.  
  2280.     if (!module){
  2281.       // Create a new module if we don't have one
  2282.       module = new ModuleClass(moduleName, app, options);
  2283.       parentModule[moduleName] = module;
  2284.       // store the module on the parent
  2285.       parentModule.submodules[moduleName] = module;
  2286.     }
  2287.  
  2288.     return module;
  2289.   },
  2290.  
  2291.   getClass: function(moduleDefinition) {
  2292.     var ModuleClass = Marionette.Module;
  2293.  
  2294.     if (!moduleDefinition) {
  2295.       return ModuleClass;
  2296.     }
  2297.  
  2298.     if (moduleDefinition.prototype instanceof ModuleClass) {
  2299.       return moduleDefinition;
  2300.     }
  2301.  
  2302.     return moduleDefinition.moduleClass || ModuleClass;
  2303.   },
  2304.  
  2305.   // Add the module definition and add a startWithParent initializer function.
  2306.   // This is complicated because module definitions are heavily overloaded
  2307.   // and support an anonymous function, module class, or options object
  2308.   _addModuleDefinition: function(parentModule, module, def, args){
  2309.     var fn = this._getDefine(def);
  2310.     var startWithParent = this._getStartWithParent(def, module);
  2311.  
  2312.     if (fn){
  2313.       module.addDefinition(fn, args);
  2314.     }
  2315.  
  2316.     this._addStartWithParent(parentModule, module, startWithParent);
  2317.   },
  2318.  
  2319.   _getStartWithParent: function(def, module) {
  2320.     var swp;
  2321.  
  2322.     if (_.isFunction(def) && (def.prototype instanceof Marionette.Module)) {
  2323.       swp = module.constructor.prototype.startWithParent;
  2324.       return _.isUndefined(swp) ? true : swp;
  2325.     }
  2326.  
  2327.     if (_.isObject(def)){
  2328.       swp = def.startWithParent;
  2329.       return _.isUndefined(swp) ? true : swp;
  2330.     }
  2331.  
  2332.     return true;
  2333.   },
  2334.  
  2335.   _getDefine: function(def) {
  2336.     if (_.isFunction(def) && !(def.prototype instanceof Marionette.Module)) {
  2337.       return def;
  2338.     }
  2339.  
  2340.     if (_.isObject(def)){
  2341.       return def.define;
  2342.     }
  2343.  
  2344.     return null;
  2345.   },
  2346.  
  2347.   _addStartWithParent: function(parentModule, module, startWithParent) {
  2348.     module.startWithParent = module.startWithParent && startWithParent;
  2349.  
  2350.     if (!module.startWithParent || !!module.startWithParentIsConfigured){
  2351.       return;
  2352.     }
  2353.  
  2354.     module.startWithParentIsConfigured = true;
  2355.  
  2356.     parentModule.addInitializer(function(options){
  2357.       if (module.startWithParent){
  2358.         module.start(options);
  2359.       }
  2360.     });
  2361.   }
  2362. });
  2363.  
  2364.  
  2365.  
  2366.   return Marionette;
  2367. })(this, Backbone, _);
  2368.  
  2369.   return Backbone.Marionette;
  2370.  
  2371. }));
  2372.  
  2373. // https://c.disquscdn.com/next/node_modules/backbone.marionette/lib/core/amd/backbone.marionette.js

Reply to "backbone.marionette.js"

Here you can reply to the paste above