From 62b0f649fd5e9a42f9e4ea35e742ecc8303510da Mon Sep 17 00:00:00 2001 From: Jean-Francois Moy Date: Thu, 6 Mar 2014 11:32:11 +0000 Subject: [PATCH] Bumped Backbone.Marionette version from 1.5.1 to 1.6.4 --- .../1.6.4-bundled/backbone.marionette.js | 2553 +++++++++++++++++ .../1.6.4-bundled/backbone.marionette.map | 1 + .../1.6.4-bundled/backbone.marionette.min.js | 22 + ajax/libs/backbone.marionette/package.json | 2 +- 4 files changed, 2577 insertions(+), 1 deletion(-) create mode 100644 ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.js create mode 100644 ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.map create mode 100644 ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.min.js diff --git a/ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.js b/ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.js new file mode 100644 index 00000000000000..378175f33d8c92 --- /dev/null +++ b/ajax/libs/backbone.marionette/1.6.4-bundled/backbone.marionette.js @@ -0,0 +1,2553 @@ +// MarionetteJS (Backbone.Marionette) +// ---------------------------------- +// v1.6.4 +// +// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. +// Distributed under MIT license +// +// http://marionettejs.com + + + +/*! + * Includes BabySitter + * https://github.com/marionettejs/backbone.babysitter/ + * + * Includes Wreqr + * https://github.com/marionettejs/backbone.wreqr/ + */ + +// Backbone.BabySitter +// ------------------- +// v0.1.0 +// +// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. +// Distributed under MIT license +// +// http://github.com/marionettejs/backbone.babysitter + +// Backbone.ChildViewContainer +// --------------------------- +// +// Provide a container to store, retrieve and +// shut down child views. + +Backbone.ChildViewContainer = (function(Backbone, _){ + + // Container Constructor + // --------------------- + + var Container = function(views){ + this._views = {}; + this._indexByModel = {}; + this._indexByCustom = {}; + this._updateLength(); + + _.each(views, this.add, this); + }; + + // 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(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; + } + + this._updateLength(); + return this; + }, + + // Find a view by the model that was attached to + // it. Uses the model's `cid` to find it. + findByModel: function(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(modelCid){ + var viewCid = this._indexByModel[modelCid]; + return this.findByCid(viewCid); + }, + + // Find a view by a custom indexer. + findByCustom: function(index){ + var viewCid = this._indexByCustom[index]; + return this.findByCid(viewCid); + }, + + // Find by index. This is not guaranteed to be a + // stable index. + findByIndex: function(index){ + return _.values(this._views)[index]; + }, + + // retrieve a view by its `cid` directly + findByCid: function(cid){ + return this._views[cid]; + }, + + // Remove a view + remove: function(view){ + var viewCid = view.cid; + + // delete model index + if (view.model){ + delete this._indexByModel[view.model.cid]; + } + + // delete custom index + _.any(this._indexByCustom, function(cid, key) { + if (cid === viewCid) { + delete this._indexByCustom[key]; + return true; + } + }, this); + + // remove the view from the container + delete this._views[viewCid]; + + // update the length + this._updateLength(); + return this; + }, + + // Call a method on every view in the container, + // passing parameters to the call method one at a + // time, like `function.call`. + call: function(method){ + this.apply(method, _.tail(arguments)); + }, + + // Apply a method on every view in the container, + // passing parameters to the call method one at a + // time, like `function.apply`. + apply: function(method, args){ + _.each(this._views, function(view){ + if (_.isFunction(view[method])){ + view[method].apply(view, args || []); + } + }); + }, + + // Update the `.length` attribute on this container + _updateLength: function(){ + this.length = _.size(this._views); + } + }); + + // Borrowing this code from Backbone.Collection: + // http://backbonejs.org/docs/backbone.html#section-106 + // + // Mix in methods from Underscore, for iteration, and other + // collection related features. + 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']; + + _.each(methods, function(method) { + Container.prototype[method] = function() { + var views = _.values(this._views); + var args = [views].concat(_.toArray(arguments)); + return _[method].apply(_, args); + }; + }); + + // return the public API + return Container; +})(Backbone, _); + +// Backbone.Wreqr (Backbone.Marionette) +// ---------------------------------- +// v1.0.0 +// +// Copyright (c)2014 Derick Bailey, Muted Solutions, LLC. +// Distributed under MIT license +// +// http://github.com/marionettejs/backbone.wreqr + + +Backbone.Wreqr = (function(Backbone, Marionette, _){ + "use strict"; + var Wreqr = {}; + + // Handlers +// -------- +// A registry of functions to call, given a name + +Wreqr.Handlers = (function(Backbone, _){ + "use strict"; + + // Constructor + // ----------- + + var Handlers = function(options){ + this.options = options; + this._wreqrHandlers = {}; + + if (_.isFunction(this.initialize)){ + this.initialize(options); + } + }; + + Handlers.extend = Backbone.Model.extend; + + // Instance Members + // ---------------- + + _.extend(Handlers.prototype, Backbone.Events, { + + // Add multiple handlers using an object literal configuration + setHandlers: function(handlers){ + _.each(handlers, function(handler, name){ + var context = null; + + if (_.isObject(handler) && !_.isFunction(handler)){ + context = handler.context; + handler = handler.callback; + } + + this.setHandler(name, handler, context); + }, this); + }, + + // Add a handler for the given name, with an + // optional context to run the handler within + setHandler: function(name, handler, context){ + var config = { + callback: handler, + context: context + }; + + this._wreqrHandlers[name] = config; + + this.trigger("handler:add", name, handler, context); + }, + + // Determine whether or not a handler is registered + hasHandler: function(name){ + return !! this._wreqrHandlers[name]; + }, + + // Get the currently registered handler for + // the specified name. Throws an exception if + // no handler is found. + getHandler: function(name){ + var config = this._wreqrHandlers[name]; + + if (!config){ + throw new Error("Handler not found for '" + name + "'"); + } + + return function(){ + var args = Array.prototype.slice.apply(arguments); + return config.callback.apply(config.context, args); + }; + }, + + // Remove a handler for the specified name + removeHandler: function(name){ + delete this._wreqrHandlers[name]; + }, + + // Remove all handlers from this registry + removeAllHandlers: function(){ + this._wreqrHandlers = {}; + } + }); + + return Handlers; +})(Backbone, _); + + // Wreqr.CommandStorage +// -------------------- +// +// Store and retrieve commands for execution. +Wreqr.CommandStorage = (function(){ + "use strict"; + + // Constructor function + var CommandStorage = function(options){ + this.options = options; + this._commands = {}; + + if (_.isFunction(this.initialize)){ + this.initialize(options); + } + }; + + // Instance methods + _.extend(CommandStorage.prototype, Backbone.Events, { + + // Get an object literal by command name, that contains + // the `commandName` and the `instances` of all commands + // represented as an array of arguments to process + getCommands: function(commandName){ + var commands = this._commands[commandName]; + + // we don't have it, so add it + if (!commands){ + + // build the configuration + commands = { + command: commandName, + instances: [] + }; + + // store it + this._commands[commandName] = commands; + } + + return commands; + }, + + // Add a command by name, to the storage and store the + // args for the command + addCommand: function(commandName, args){ + var command = this.getCommands(commandName); + command.instances.push(args); + }, + + // Clear all commands for the given `commandName` + clearCommands: function(commandName){ + var command = this.getCommands(commandName); + command.instances = []; + } + }); + + return CommandStorage; +})(); + + // Wreqr.Commands +// -------------- +// +// A simple command pattern implementation. Register a command +// handler and execute it. +Wreqr.Commands = (function(Wreqr){ + "use strict"; + + return Wreqr.Handlers.extend({ + // default storage type + storageType: Wreqr.CommandStorage, + + constructor: function(options){ + this.options = options || {}; + + this._initializeStorage(this.options); + this.on("handler:add", this._executeCommands, this); + + var args = Array.prototype.slice.call(arguments); + Wreqr.Handlers.prototype.constructor.apply(this, args); + }, + + // Execute a named command with the supplied args + execute: function(name, args){ + name = arguments[0]; + args = Array.prototype.slice.call(arguments, 1); + + if (this.hasHandler(name)){ + this.getHandler(name).apply(this, args); + } else { + this.storage.addCommand(name, args); + } + + }, + + // Internal method to handle bulk execution of stored commands + _executeCommands: function(name, handler, context){ + var command = this.storage.getCommands(name); + + // loop through and execute all the stored command instances + _.each(command.instances, function(args){ + handler.apply(context, args); + }); + + this.storage.clearCommands(name); + }, + + // Internal method to initialize storage either from the type's + // `storageType` or the instance `options.storageType`. + _initializeStorage: function(options){ + var storage; + + var StorageType = options.storageType || this.storageType; + if (_.isFunction(StorageType)){ + storage = new StorageType(); + } else { + storage = StorageType; + } + + this.storage = storage; + } + }); + +})(Wreqr); + + // Wreqr.RequestResponse +// --------------------- +// +// A simple request/response implementation. Register a +// request handler, and return a response from it +Wreqr.RequestResponse = (function(Wreqr){ + "use strict"; + + return Wreqr.Handlers.extend({ + request: function(){ + var name = arguments[0]; + var args = Array.prototype.slice.call(arguments, 1); + + return this.getHandler(name).apply(this, args); + } + }); + +})(Wreqr); + + // Event Aggregator +// ---------------- +// A pub-sub object that can be used to decouple various parts +// of an application through event-driven architecture. + +Wreqr.EventAggregator = (function(Backbone, _){ + "use strict"; + var EA = function(){}; + + // Copy the `extend` function used by Backbone's classes + EA.extend = Backbone.Model.extend; + + // Copy the basic Backbone.Events on to the event aggregator + _.extend(EA.prototype, Backbone.Events); + + return EA; +})(Backbone, _); + + + return Wreqr; +})(Backbone, Backbone.Marionette, _); + +var Marionette = (function(global, Backbone, _){ + "use strict"; + + // Define and export the Marionette namespace + var Marionette = {}; + Backbone.Marionette = Marionette; + + // Get the DOM manipulator for later use + Marionette.$ = Backbone.$; + +// Helpers +// ------- + +// For slicing `arguments` in functions +var slice = Array.prototype.slice; + +function throwError(message, name) { + var error = new Error(message); + error.name = name || 'Error'; + throw error; +} + +// Marionette.extend +// ----------------- + +// Borrow the Backbone `extend` method so we can use it as needed +Marionette.extend = Backbone.Model.extend; + +// Marionette.getOption +// -------------------- + +// Retrieve an object, function or other value from a target +// object or its `options`, with `options` taking precedence. +Marionette.getOption = function(target, optionName){ + if (!target || !optionName){ return; } + var value; + + if (target.options && (optionName in target.options) && (target.options[optionName] !== undefined)){ + value = target.options[optionName]; + } else { + value = target[optionName]; + } + + return value; +}; + +// Marionette.normalizeMethods +// ---------------------- + +// Pass in a mapping of events => functions or function names +// and return a mapping of events => functions +Marionette.normalizeMethods = function(hash) { + var normalizedHash = {}, method; + _.each(hash, function(fn, name) { + method = fn; + if (!_.isFunction(method)) { + method = this[method]; + } + if (!method) { + return; + } + normalizedHash[name] = method; + }, this); + return normalizedHash; +}; + +// 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. +Marionette.triggerMethod = (function(){ + + // split the event name on the ":" + var splitter = /(^|:)(\w)/gi; + + // take the event section ("section1:section2:section3") + // and turn it in to uppercase name + function getEventName(match, prefix, eventName) { + return eventName.toUpperCase(); + } + + // actual triggerMethod implementation + var triggerMethod = function(event) { + // get the method name from the event name + var methodName = 'on' + event.replace(splitter, getEventName); + var method = this[methodName]; + + // trigger the event, if a trigger method exists + if(_.isFunction(this.trigger)) { + this.trigger.apply(this, arguments); + } + + // call the onMethodName if it exists + if (_.isFunction(method)) { + // pass all arguments, except the event name + return method.apply(this, _.tail(arguments)); + } + }; + + return triggerMethod; +})(); + +// DOMRefresh +// ---------- +// +// Monitor a view's state, and after it has been rendered and shown +// in the DOM, trigger a "dom:refresh" event every time it is +// re-rendered. + +Marionette.MonitorDOMRefresh = (function(documentElement){ + // track when the view has been shown in the DOM, + // using a Marionette.Region (or by other means of triggering "show") + function handleShow(view){ + view._isShown = true; + triggerDOMRefresh(view); + } + + // track when the view has been rendered + function handleRender(view){ + view._isRendered = true; + triggerDOMRefresh(view); + } + + // Trigger the "dom:refresh" event and corresponding "onDomRefresh" method + function triggerDOMRefresh(view){ + if (view._isShown && view._isRendered && isInDOM(view)){ + if (_.isFunction(view.triggerMethod)){ + view.triggerMethod("dom:refresh"); + } + } + } + + function isInDOM(view) { + return documentElement.contains(view.el); + } + + // Export public API + return function(view){ + view.listenTo(view, "show", function(){ + handleShow(view); + }); + + view.listenTo(view, "render", function(){ + handleRender(view); + }); + }; +})(document.documentElement); + + +// Marionette.bindEntityEvents & unbindEntityEvents +// --------------------------- +// +// These methods are used to bind/unbind a backbone "entity" (collection/model) +// to methods on a target object. +// +// The first parameter, `target`, must have a `listenTo` method from the +// EventBinder object. +// +// The second parameter is the entity (Backbone.Model or Backbone.Collection) +// 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. + +(function(Marionette){ + "use strict"; + + // Bind the event to handlers specified as a string of + // handler names on the target object + function bindFromStrings(target, entity, evt, methods){ + var methodNames = methods.split(/\s+/); + + _.each(methodNames,function(methodName) { + + var method = target[methodName]; + if(!method) { + throwError("Method '"+ methodName +"' was configured as an event handler, but does not exist."); + } + + target.listenTo(entity, evt, method); + }); + } + + // Bind the event to a supplied callback function + function bindToFunction(target, entity, evt, method){ + target.listenTo(entity, evt, method); + } + + // Bind the event to handlers specified as a string of + // handler names on the target object + function unbindFromStrings(target, entity, evt, methods){ + var methodNames = methods.split(/\s+/); + + _.each(methodNames,function(methodName) { + var method = target[methodName]; + target.stopListening(entity, evt, method); + }); + } + + // Bind the event to a supplied callback function + function unbindToFunction(target, entity, evt, method){ + target.stopListening(entity, evt, method); + } + + + // generic looping function + function iterateEvents(target, entity, bindings, functionCallback, stringCallback){ + if (!entity || !bindings) { return; } + + // allow the bindings to be a function + if (_.isFunction(bindings)){ + bindings = bindings.call(target); + } + + // iterate the bindings and bind them + _.each(bindings, function(methods, evt){ + + // allow for a function as the handler, + // or a list of event names as a string + if (_.isFunction(methods)){ + functionCallback(target, entity, evt, methods); + } else { + stringCallback(target, entity, evt, methods); + } + + }); + } + + // Export Public API + Marionette.bindEntityEvents = function(target, entity, bindings){ + iterateEvents(target, entity, bindings, bindToFunction, bindFromStrings); + }; + + Marionette.unbindEntityEvents = function(target, entity, bindings){ + iterateEvents(target, entity, bindings, unbindToFunction, unbindFromStrings); + }; + +})(Marionette); + + +// Callbacks +// --------- + +// A simple way of managing a collection of callbacks +// and executing them at a later point in time, using jQuery's +// `Deferred` object. +Marionette.Callbacks = function(){ + this._deferred = Marionette.$.Deferred(); + this._callbacks = []; +}; + +_.extend(Marionette.Callbacks.prototype, { + + // Add a callback to be executed. Callbacks added here are + // guaranteed to execute, even if they are added after the + // `run` method is called. + add: function(callback, contextOverride){ + this._callbacks.push({cb: callback, ctx: contextOverride}); + + this._deferred.done(function(context, options){ + if (contextOverride){ context = contextOverride; } + callback.call(context, options); + }); + }, + + // Run all registered callbacks with the context specified. + // Additional callbacks can be added after this has been run + // and they will still be executed. + run: function(options, context){ + this._deferred.resolve(context, options); + }, + + // Resets the list of callbacks to be run, allowing the same list + // to be run multiple times - whenever the `run` method is called. + reset: function(){ + var callbacks = this._callbacks; + this._deferred = Marionette.$.Deferred(); + this._callbacks = []; + + _.each(callbacks, function(cb){ + this.add(cb.cb, cb.ctx); + }, this); + } +}); + + +// Marionette Controller +// --------------------- +// +// A multi-purpose object to use as a controller for +// modules and routers, and as a mediator for workflow +// and coordination of other objects, views, and more. +Marionette.Controller = function(options){ + this.triggerMethod = Marionette.triggerMethod; + this.options = options || {}; + + if (_.isFunction(this.initialize)){ + this.initialize(this.options); + } +}; + +Marionette.Controller.extend = Marionette.extend; + +// Controller Methods +// -------------- + +// Ensure it can trigger events with Backbone.Events +_.extend(Marionette.Controller.prototype, Backbone.Events, { + close: function(){ + this.stopListening(); + this.triggerMethod("close"); + this.unbind(); + } +}); + +// Region +// ------ +// +// Manage the visual regions of your composite application. See +// http://lostechies.com/derickbailey/2011/12/12/composite-js-apps-regions-and-region-managers/ + +Marionette.Region = function(options){ + this.options = options || {}; + this.el = Marionette.getOption(this, "el"); + + if (!this.el){ + throwError("An 'el' must be specified for a region.", "NoElError"); + } + + if (this.initialize){ + var args = Array.prototype.slice.apply(arguments); + this.initialize.apply(this, args); + } +}; + + +// Region Type methods +// ------------------- + +_.extend(Marionette.Region, { + + // Build an instance of a region by passing in a configuration object + // and a default region type to use if none is specified in the config. + // + // The config object should either be a string as a jQuery DOM selector, + // a Region type directly, or an object literal that specifies both + // a selector and regionType: + // + // ```js + // { + // selector: "#foo", + // regionType: MyCustomRegion + // } + // ``` + // + buildRegion: function(regionConfig, defaultRegionType){ + var regionIsString = _.isString(regionConfig); + var regionSelectorIsString = _.isString(regionConfig.selector); + var regionTypeIsUndefined = _.isUndefined(regionConfig.regionType); + var regionIsType = _.isFunction(regionConfig); + + if (!regionIsType && !regionIsString && !regionSelectorIsString) { + throwError("Region must be specified as a Region type, a selector string or an object with selector property"); + } + + var selector, RegionType; + + // get the selector for the region + + if (regionIsString) { + selector = regionConfig; + } + + if (regionConfig.selector) { + selector = regionConfig.selector; + delete regionConfig.selector; + } + + // get the type for the region + + if (regionIsType){ + RegionType = regionConfig; + } + + if (!regionIsType && regionTypeIsUndefined) { + RegionType = defaultRegionType; + } + + if (regionConfig.regionType) { + RegionType = regionConfig.regionType; + delete regionConfig.regionType; + } + + if (regionIsString || regionIsType) { + regionConfig = {}; + } + + regionConfig.el = selector; + + // build the region instance + var region = new RegionType(regionConfig); + + // override the `getEl` function if we have a parentEl + // this must be overridden to ensure the selector is found + // on the first use of the region. if we try to assign the + // region's `el` to `parentEl.find(selector)` in the object + // literal to build the region, the element will not be + // guaranteed to be in the DOM already, and will cause problems + if (regionConfig.parentEl){ + region.getEl = function(selector) { + var parentEl = regionConfig.parentEl; + if (_.isFunction(parentEl)){ + parentEl = parentEl(); + } + return parentEl.find(selector); + }; + } + + return region; + } + +}); + +// Region Instance Methods +// ----------------------- + +_.extend(Marionette.Region.prototype, Backbone.Events, { + + // Displays a backbone view instance inside of the region. + // Handles calling the `render` method for you. Reads content + // directly from the `el` attribute. Also calls an optional + // `onShow` and `close` method on your view, just after showing + // or just before closing the view, respectively. + show: function(view){ + this.ensureEl(); + + var isViewClosed = view.isClosed || _.isUndefined(view.$el); + var isDifferentView = view !== this.currentView; + + if (isDifferentView) { + this.close(); + } + + view.render(); + + if (isDifferentView || isViewClosed) { + this.open(view); + } + + this.currentView = view; + + Marionette.triggerMethod.call(this, "show", view); + Marionette.triggerMethod.call(view, "show"); + }, + + ensureEl: function(){ + if (!this.$el || this.$el.length === 0){ + this.$el = this.getEl(this.el); + } + }, + + // Override this method to change how the region finds the + // DOM element that it manages. Return a jQuery selector object. + getEl: function(selector){ + return Marionette.$(selector); + }, + + // Override this method to change how the new view is + // appended to the `$el` that the region is managing + open: function(view){ + this.$el.empty().append(view.el); + }, + + // Close the current view, if there is one. If there is no + // current view, it does nothing and returns immediately. + close: function(){ + var view = this.currentView; + if (!view || view.isClosed){ return; } + + // call 'close' or 'remove', depending on which is found + if (view.close) { view.close(); } + else if (view.remove) { view.remove(); } + + Marionette.triggerMethod.call(this, "close", view); + + delete this.currentView; + }, + + // Attach an existing view to the region. This + // will not call `render` or `onShow` for the new view, + // and will not replace the current HTML for the `el` + // of the region. + attachView: function(view){ + this.currentView = view; + }, + + // Reset the region by closing 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(){ + this.close(); + delete this.$el; + } +}); + +// Copy the `extend` function used by Backbone's classes +Marionette.Region.extend = Marionette.extend; + +// Marionette.RegionManager +// ------------------------ +// +// Manage one or more related `Marionette.Region` objects. +Marionette.RegionManager = (function(Marionette){ + + var RegionManager = Marionette.Controller.extend({ + constructor: function(options){ + this._regions = {}; + Marionette.Controller.prototype.constructor.call(this, options); + }, + + // Add multiple regions using an object literal, where + // each key becomes the region name, and each value is + // the region definition. + addRegions: function(regionDefinitions, defaults){ + var regions = {}; + + _.each(regionDefinitions, function(definition, name){ + if (_.isString(definition)){ + definition = { selector: definition }; + } + + if (definition.selector){ + definition = _.defaults({}, definition, defaults); + } + + var region = this.addRegion(name, definition); + regions[name] = region; + }, this); + + return regions; + }, + + // Add an individual region to the region manager, + // and return the region instance + addRegion: function(name, definition){ + var region; + + var isObject = _.isObject(definition); + var isString = _.isString(definition); + var hasSelector = !!definition.selector; + + if (isString || (isObject && hasSelector)){ + region = Marionette.Region.buildRegion(definition, Marionette.Region); + } else if (_.isFunction(definition)){ + region = Marionette.Region.buildRegion(definition, Marionette.Region); + } else { + region = definition; + } + + this._store(name, region); + this.triggerMethod("region:add", name, region); + return region; + }, + + // Get a region by name + get: function(name){ + return this._regions[name]; + }, + + // Remove a region by name + removeRegion: function(name){ + var region = this._regions[name]; + this._remove(name, region); + }, + + // Close all regions in the region manager, and + // remove them + removeRegions: function(){ + _.each(this._regions, function(region, name){ + this._remove(name, region); + }, this); + }, + + // Close all regions in the region manager, but + // leave them attached + closeRegions: function(){ + _.each(this._regions, function(region, name){ + region.close(); + }, this); + }, + + // Close all regions and shut down the region + // manager entirely + close: function(){ + this.removeRegions(); + Marionette.Controller.prototype.close.apply(this, arguments); + }, + + // internal method to store regions + _store: function(name, region){ + this._regions[name] = region; + this._setLength(); + }, + + // internal method to remove a region + _remove: function(name, region){ + region.close(); + delete this._regions[name]; + this._setLength(); + this.triggerMethod("region:remove", name, region); + }, + + // set the number of regions current held + _setLength: function(){ + this.length = _.size(this._regions); + } + + }); + + // Borrowing this code from Backbone.Collection: + // http://backbonejs.org/docs/backbone.html#section-106 + // + // Mix in methods from Underscore, for iteration, and other + // collection related features. + 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']; + + _.each(methods, function(method) { + RegionManager.prototype[method] = function() { + var regions = _.values(this._regions); + var args = [regions].concat(_.toArray(arguments)); + return _[method].apply(_, args); + }; + }); + + return RegionManager; +})(Marionette); + + +// Template Cache +// -------------- + +// Manage templates stored in `