From 1acc1b29fc178ae01fc075ee2b82a3e8384589f5 Mon Sep 17 00:00:00 2001 From: Andrew Humphreys Date: Thu, 4 Aug 2016 10:35:03 +0100 Subject: [PATCH] Bump and build v3.0.0-pre.5 (#3094) --- bower.json | 2 +- changelog.md | 34 + lib/backbone.marionette.js | 1110 +++----- lib/backbone.marionette.js.map | 2 +- lib/backbone.marionette.min.js | 17 +- lib/backbone.marionette.min.js.map | 2 +- lib/core/backbone.marionette.js | 3089 ----------------------- lib/core/backbone.marionette.js.map | 1 - lib/core/backbone.marionette.min.js | 11 - lib/core/backbone.marionette.min.js.map | 1 - package.json | 4 +- 11 files changed, 388 insertions(+), 3885 deletions(-) delete mode 100644 lib/core/backbone.marionette.js delete mode 100644 lib/core/backbone.marionette.js.map delete mode 100644 lib/core/backbone.marionette.min.js delete mode 100644 lib/core/backbone.marionette.min.js.map diff --git a/bower.json b/bower.json index ca4ea270e2..6da7e8ec34 100644 --- a/bower.json +++ b/bower.json @@ -3,7 +3,7 @@ "description": "The Backbone Framework", "homepage": "http://marionettejs.org", "main": "./lib/backbone.marionette.js", - "version": "3.0.0-pre.4", + "version": "3.0.0-pre.5", "keywords": [ "backbone", "framework", diff --git a/changelog.md b/changelog.md index 4fa0cd8670..b896c21b5d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,37 @@ +### v3.0.0-pre5 + +#### Documentation + +* Improved installation docs. +* Updated `CollectionView` docs to reflect API changes. +* Improved `Behavior` docs. +* Improved functions docs. +* Improved update guide. +* Added "basics" docs. + +#### API Changes + +* `emptyView` now accepts a function as an arg. +* Removed the `apply:filter` event from `CollectionView`. +* `removeChildView` now returns the removed view. +* `bindEntityEvents` renamed `bindEvents`. +* Deprecated Behavior Lookups. +* Added Backbone.Babysitter to Mn and removed the Babysitter dependency. + +#### Bug fixes + +* `CollectionView` now only triggers `destroy:children` if it has been rendered. +* Parent views will now successfully listen for `destroy` in `childViewEvents`. + +#### Misc + +* Replaced `var` and `let` with `const`. +* Added consistent function declarations and added rules to eslint. +* Tweaked peerDependencies to only allow patch versions. +* Directory structure changes and file naming consistency. +* Improved test coverage. +* Removed bundled build. + ### v3.0.0-pre4 #### Documentation diff --git a/lib/backbone.marionette.js b/lib/backbone.marionette.js index 2d562f6df0..eb5b8fafc7 100644 --- a/lib/backbone.marionette.js +++ b/lib/backbone.marionette.js @@ -1,6 +1,6 @@ // MarionetteJS (Backbone.Marionette) // ---------------------------------- -// v3.0.0-pre.4 +// v3.0.0-pre.5 // // Copyright (c)2016 Derick Bailey, Muted Solutions, LLC. // Distributed under MIT license @@ -8,44 +8,17 @@ // http://marionettejs.com -/*! -* Includes BabySitter -* https://github.com/marionettejs/backbone.babysitter/ -* -* Includes Radio -* https://github.com/marionettejs/backbone.radio/ -*/ - - - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone'), require('underscore')) : - typeof define === 'function' && define.amd ? define(['backbone', 'underscore'], factory) : - (global.Marionette = global['Mn'] = factory(global.Backbone,global._)); -}(this, function (Backbone,_) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone'), require('underscore'), require('backbone.radio')) : + typeof define === 'function' && define.amd ? define(['backbone', 'underscore', 'backbone.radio'], factory) : + (global.Marionette = global['Mn'] = factory(global.Backbone,global._,global.Backbone.Radio)); +}(this, function (Backbone,_,Radio) { 'use strict'; Backbone = 'default' in Backbone ? Backbone['default'] : Backbone; _ = 'default' in _ ? _['default'] : _; + Radio = 'default' in Radio ? Radio['default'] : Radio; - var __commonjs_global = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this; - function __commonjs(fn, module) { return module = { exports: {} }, fn(module, module.exports, __commonjs_global), module.exports; } - - - var babelHelpers = {}; - - babelHelpers.toConsumableArray = function (arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - - return arr2; - } else { - return Array.from(arr); - } - }; - - babelHelpers; - - var version = "3.0.0-pre.4"; + var version = "3.0.0-pre.5"; //Internal utility for creating context style global utils var proxy = function proxy(method) { @@ -61,6 +34,28 @@ // Borrow the Backbone `extend` method so we can use it as needed var extend = Backbone.Model.extend; + var deprecate = function deprecate(message, test) { + if (_.isObject(message)) { + message = message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : ''); + } + + if (!Marionette.DEV_MODE) { + return; + } + + if ((test === undefined || !test) && !deprecate._cache[message]) { + deprecate._warn('Deprecation warning: ' + message); + deprecate._cache[message] = true; + } + }; + + deprecate._console = typeof console !== 'undefined' ? console : {}; + deprecate._warn = function () { + var warn = deprecate._console.warn || deprecate._console.log || _.noop; + return warn.apply(deprecate._console, arguments); + }; + deprecate._cache = {}; + // Determine if `el` is a child of the document var isNodeAttached = function isNodeAttached(el) { return Backbone.$.contains(document.documentElement, el); @@ -109,28 +104,6 @@ }, {}); }; - var deprecate = function deprecate(message, test) { - if (_.isObject(message)) { - message = message.prev + ' is going to be removed in the future. ' + 'Please use ' + message.next + ' instead.' + (message.url ? ' See: ' + message.url : ''); - } - - if (!Marionette.DEV_MODE) { - return; - } - - if ((test === undefined || !test) && !deprecate._cache[message]) { - deprecate._warn('Deprecation warning: ' + message); - deprecate._cache[message] = true; - } - }; - - deprecate._console = typeof console !== 'undefined' ? console : {}; - deprecate._warn = function () { - var warn = deprecate._console.warn || deprecate._console.log || function () {}; - return warn.apply(deprecate._console, arguments); - }; - deprecate._cache = {}; - // split the event name on the ":" var splitter = /(^|:)(\w)/gi; @@ -151,7 +124,7 @@ // get the method name from the event name var methodName = 'on' + event.replace(splitter, getEventName); var method = getOption.call(this, methodName); - var result; + var result = void 0; // call the onMethodName if it exists @@ -266,21 +239,12 @@ }); } - // Internal utility for setting options consistently across Mn - var _setOptions = function _setOptions() { - for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { - args[_key] = arguments[_key]; - } - - this.options = _.extend.apply(_, [{}, _.result(this, 'options')].concat(args)); - }; - var errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number']; var MarionetteError = extend.call(Error, { urlRoot: 'http://marionettejs.com/docs/v' + version + '/', - constructor: function MarionetteError(message, options) { + constructor: function constructor(message, options) { if (_.isObject(message)) { options = message; message = options.message; @@ -297,13 +261,11 @@ this.url = this.urlRoot + options.url; } }, - captureStackTrace: function captureStackTrace() { if (Error.captureStackTrace) { Error.captureStackTrace(this, MarionetteError); } }, - toString: function toString() { return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : ''); } @@ -336,7 +298,7 @@ if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', - url: 'marionette.functions.html#marionettebindentityevents' + url: 'marionette.functions.html#marionettebindevents' }); } @@ -353,391 +315,16 @@ }); } - function bindEntityEvents(entity, bindings) { + function bindEvents(entity, bindings) { iterateEvents(this, entity, bindings, 'listenTo'); return this; } - function unbindEntityEvents(entity, bindings) { + function unbindEvents(entity, bindings) { iterateEvents(this, entity, bindings, 'stopListening'); return this; } - var CommonMixin = { - - // Imports the "normalizeMethods" to transform hashes of - // events=>function references/names to a hash of events=>function references - normalizeMethods: normalizeMethods, - - _setOptions: _setOptions, - - // A handy way to merge passed-in options onto the instance - mergeOptions: mergeOptions, - - // Enable getting options from this or this.options by name. - getOption: getOption, - - // Enable binding view's events from another entity. - bindEntityEvents: bindEntityEvents, - - // Enable unbinding view's events from another entity. - unbindEntityEvents: unbindEntityEvents - }; - - var backbone_radio = __commonjs(function (module, exports, global) { - // Backbone.Radio v2.0.0-pre.1 - - (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(_, Backbone) : - typeof define === 'function' && define.amd ? define(['underscore', 'backbone'], factory) : - (global.Backbone = global.Backbone || {}, global.Backbone.Radio = factory(global._,global.Backbone)); - }(__commonjs_global, function (_,Backbone) { 'use strict'; - - _ = 'default' in _ ? _['default'] : _; - Backbone = 'default' in Backbone ? Backbone['default'] : Backbone; - - var babelHelpers = {}; - babelHelpers.typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; - }; - babelHelpers; - - var previousRadio = Backbone.Radio; - - var Radio = Backbone.Radio = {}; - - Radio.VERSION = '2.0.0-pre.1'; - - // This allows you to run multiple instances of Radio on the same - // webapp. After loading the new version, call `noConflict()` to - // get a reference to it. At the same time the old version will be - // returned to Backbone.Radio. - Radio.noConflict = function () { - Backbone.Radio = previousRadio; - return this; - }; - - // Whether or not we're in DEBUG mode or not. DEBUG mode helps you - // get around the issues of lack of warnings when events are mis-typed. - Radio.DEBUG = false; - - // Format debug text. - Radio._debugText = function (warning, eventName, channelName) { - return warning + (channelName ? ' on the ' + channelName + ' channel' : '') + ': "' + eventName + '"'; - }; - - // This is the method that's called when an unregistered event was called. - // By default, it logs warning to the console. By overriding this you could - // make it throw an Error, for instance. This would make firing a nonexistent event - // have the same consequence as firing a nonexistent method on an Object. - Radio.debugLog = function (warning, eventName, channelName) { - if (Radio.DEBUG && console && console.warn) { - console.warn(Radio._debugText(warning, eventName, channelName)); - } - }; - - var eventSplitter = /\s+/; - - // An internal method used to handle Radio's method overloading for Requests. - // It's borrowed from Backbone.Events. It differs from Backbone's overload - // API (which is used in Backbone.Events) in that it doesn't support space-separated - // event names. - Radio._eventsApi = function (obj, action, name, rest) { - if (!name) { - return false; - } - - var results = {}; - - // Handle event maps. - if ((typeof name === 'undefined' ? 'undefined' : babelHelpers.typeof(name)) === 'object') { - for (var key in name) { - var result = obj[action].apply(obj, [key, name[key]].concat(rest)); - eventSplitter.test(key) ? _.extend(results, result) : results[key] = result; - } - return results; - } - - // Handle space separated event names. - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, l = names.length; i < l; i++) { - results[names[i]] = obj[action].apply(obj, [names[i]].concat(rest)); - } - return results; - } - - return false; - }; - - // An optimized way to execute callbacks. - Radio._callHandler = function (callback, context, args) { - var a1 = args[0], - a2 = args[1], - a3 = args[2]; - switch (args.length) { - case 0: - return callback.call(context); - case 1: - return callback.call(context, a1); - case 2: - return callback.call(context, a1, a2); - case 3: - return callback.call(context, a1, a2, a3); - default: - return callback.apply(context, args); - } - }; - - // A helper used by `off` methods to the handler from the store - function removeHandler(store, name, callback, context) { - var event = store[name]; - if ((!callback || callback === event.callback || callback === event.callback._callback) && (!context || context === event.context)) { - delete store[name]; - return true; - } - } - - function removeHandlers(store, name, callback, context) { - store || (store = {}); - var names = name ? [name] : _.keys(store); - var matched = false; - - for (var i = 0, length = names.length; i < length; i++) { - name = names[i]; - - // If there's no event by this name, log it and continue - // with the loop - if (!store[name]) { - continue; - } - - if (removeHandler(store, name, callback, context)) { - matched = true; - } - } - - return matched; - } - - /* - * tune-in - * ------- - * Get console logs of a channel's activity - * - */ - - var _logs = {}; - - // This is to produce an identical function in both tuneIn and tuneOut, - // so that Backbone.Events unregisters it. - function _partial(channelName) { - return _logs[channelName] || (_logs[channelName] = _.partial(Radio.log, channelName)); - } - - _.extend(Radio, { - - // Log information about the channel and event - log: function log(channelName, eventName) { - if (typeof console === 'undefined') { - return; - } - var args = _.drop(arguments, 2); - console.log('[' + channelName + '] "' + eventName + '"', args); - }, - - // Logs all events on this channel to the console. It sets an - // internal value on the channel telling it we're listening, - // then sets a listener on the Backbone.Events - tuneIn: function tuneIn(channelName) { - var channel = Radio.channel(channelName); - channel._tunedIn = true; - channel.on('all', _partial(channelName)); - return this; - }, - - // Stop logging all of the activities on this channel to the console - tuneOut: function tuneOut(channelName) { - var channel = Radio.channel(channelName); - channel._tunedIn = false; - channel.off('all', _partial(channelName)); - delete _logs[channelName]; - return this; - } - }); - - /* - * Backbone.Radio.Requests - * ----------------------- - * A messaging system for requesting data. - * - */ - - function makeCallback(callback) { - return _.isFunction(callback) ? callback : function () { - return callback; - }; - } - - Radio.Requests = { - - // Make a request - request: function request(name) { - var args = _.rest(arguments); - var results = Radio._eventsApi(this, 'request', name, args); - if (results) { - return results; - } - var channelName = this.channelName; - var requests = this._requests; - - // Check if we should log the request, and if so, do it - if (channelName && this._tunedIn) { - Radio.log.apply(this, [channelName, name].concat(args)); - } - - // If the request isn't handled, log it in DEBUG mode and exit - if (requests && (requests[name] || requests['default'])) { - var handler = requests[name] || requests['default']; - args = requests[name] ? args : arguments; - return Radio._callHandler(handler.callback, handler.context, args); - } else { - Radio.debugLog('An unhandled request was fired', name, channelName); - } - }, - - // Set up a handler for a request - reply: function reply(name, callback, context) { - if (Radio._eventsApi(this, 'reply', name, [callback, context])) { - return this; - } - - this._requests || (this._requests = {}); - - if (this._requests[name]) { - Radio.debugLog('A request was overwritten', name, this.channelName); - } - - this._requests[name] = { - callback: makeCallback(callback), - context: context || this - }; - - return this; - }, - - // Set up a handler that can only be requested once - replyOnce: function replyOnce(name, callback, context) { - if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) { - return this; - } - - var self = this; - - var once = _.once(function () { - self.stopReplying(name); - return makeCallback(callback).apply(this, arguments); - }); - - return this.reply(name, once, context); - }, - - // Remove handler(s) - stopReplying: function stopReplying(name, callback, context) { - if (Radio._eventsApi(this, 'stopReplying', name)) { - return this; - } - - // Remove everything if there are no arguments passed - if (!name && !callback && !context) { - delete this._requests; - } else if (!removeHandlers(this._requests, name, callback, context)) { - Radio.debugLog('Attempted to remove the unregistered request', name, this.channelName); - } - - return this; - } - }; - - /* - * Backbone.Radio.channel - * ---------------------- - * Get a reference to a channel by name. - * - */ - - Radio._channels = {}; - - Radio.channel = function (channelName) { - if (!channelName) { - throw new Error('You must provide a name for the channel.'); - } - - if (Radio._channels[channelName]) { - return Radio._channels[channelName]; - } else { - return Radio._channels[channelName] = new Radio.Channel(channelName); - } - }; - - /* - * Backbone.Radio.Channel - * ---------------------- - * A Channel is an object that extends from Backbone.Events, - * and Radio.Requests. - * - */ - - Radio.Channel = function (channelName) { - this.channelName = channelName; - }; - - _.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, { - - // Remove all handlers from the messaging systems of this channel - reset: function reset() { - this.off(); - this.stopListening(); - this.stopReplying(); - return this; - } - }); - - /* - * Top-level API - * ------------- - * Supplies the 'top-level API' for working with Channels directly - * from Backbone.Radio. - * - */ - - var channel; - var args; - var systems = [Backbone.Events, Radio.Requests]; - _.each(systems, function (system) { - _.each(system, function (method, methodName) { - Radio[methodName] = function (channelName) { - args = _.rest(arguments); - channel = this.channel(channelName); - return channel[methodName].apply(channel, args); - }; - }); - }); - - Radio.reset = function (channelName) { - var channels = !channelName ? this._channels : [this._channels[channelName]]; - _.invoke(channels, 'reset'); - }; - - return Radio; - - })); - }); - - var Radio = (backbone_radio && typeof backbone_radio === 'object' && 'default' in backbone_radio ? backbone_radio['default'] : backbone_radio); - function iterateReplies(target, channel, bindings, actionName) { if (!channel || !bindings) { return; @@ -747,7 +334,7 @@ if (!_.isObject(bindings)) { throw new MarionetteError({ message: 'Bindings must be an object.', - url: 'marionette.functions.html#marionettebindradiorequests' + url: 'marionette.functions.html#marionettebindrequests' }); } @@ -756,23 +343,52 @@ channel[actionName](normalizedRadioRequests, target); } - function bindRadioRequests(channel, bindings) { + function bindRequests(channel, bindings) { iterateReplies(this, channel, bindings, 'reply'); return this; } - function unbindRadioRequests(channel, bindings) { + function unbindRequests(channel, bindings) { iterateReplies(this, channel, bindings, 'stopReplying'); return this; } + // Internal utility for setting options consistently across Mn + var setOptions = function setOptions() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + this.options = _.extend.apply(_, [{}, _.result(this, 'options')].concat(args)); + }; + + var CommonMixin = { + + // Imports the "normalizeMethods" to transform hashes of + // events=>function references/names to a hash of events=>function references + normalizeMethods: normalizeMethods, + + _setOptions: setOptions, + + // A handy way to merge passed-in options onto the instance + mergeOptions: mergeOptions, + + // Enable getting options from this or this.options by name. + getOption: getOption, + + // Enable binding view's events from another entity. + bindEvents: bindEvents, + + // Enable unbinding view's events from another entity. + unbindEvents: unbindEvents + }; + // MixinOptions // - channelName // - radioEvents // - radioRequests var RadioMixin = { - _initRadio: function _initRadio() { var channelName = _.result(this, 'channelName'); @@ -780,36 +396,43 @@ return; } + /* istanbul ignore next */ + if (!Radio) { + throw new MarionetteError({ + name: 'BackboneRadioMissing', + message: 'The dependency "backbone.radio" is missing.' + }); + } + var channel = this._channel = Radio.channel(channelName); var radioEvents = _.result(this, 'radioEvents'); - this.bindRadioEvents(channel, radioEvents); + this.bindEvents(channel, radioEvents); var radioRequests = _.result(this, 'radioRequests'); - this.bindRadioRequests(channel, radioRequests); + this.bindRequests(channel, radioRequests); this.on('destroy', this._destroyRadio); }, - _destroyRadio: function _destroyRadio() { this._channel.stopReplying(null, null, this); }, - getChannel: function getChannel() { return this._channel; }, - // Proxy `bindRadioEvents` - bindRadioEvents: bindEntityEvents, - // Proxy `unbindRadioEvents` - unbindRadioEvents: unbindEntityEvents, + // Proxy `bindEvents` + bindEvents: bindEvents, + + // Proxy `unbindEvents` + unbindEvents: unbindEvents, - // Proxy `bindRadioRequests` - bindRadioRequests: bindRadioRequests, + // Proxy `bindRequests` + bindRequests: bindRequests, - // Proxy `unbindRadioRequests` - unbindRadioRequests: unbindRadioRequests + // Proxy `unbindRequests` + unbindRequests: unbindRequests }; @@ -841,9 +464,9 @@ return this._isDestroyed; }, + //this is a noop method intended to be overridden by classes that extend from this base initialize: function initialize() {}, - destroy: function destroy() { if (this._isDestroyed) { return this; @@ -862,6 +485,7 @@ return this; }, + triggerMethod: triggerMethod }); @@ -891,6 +515,7 @@ return cachedTemplate.load(options); }, + // Clear templates from the cache. If no arguments // are specified, clears all templates: // `clear()` @@ -899,7 +524,7 @@ // specified templates from the cache: // `clear("#t1", "#t2", "...")` clear: function clear() { - var i; + var i = void 0; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; @@ -923,6 +548,7 @@ _.extend(TemplateCache.prototype, { // Internal method to load the template + load: function load(options) { // Guard clause to prevent loading this template more than once if (this.compiledTemplate) { @@ -936,6 +562,7 @@ return this.compiledTemplate; }, + // Load a template from the DOM, by default. Override // this method to provide your own template retrieval // For asynchronous loading with AMD/RequireJS, consider @@ -953,6 +580,7 @@ return $template.html(); }, + // Pre-compile the template before caching it. Override // this method if you do not need to pre-compile a template // (JST / RequireJS for example) or if you want to change @@ -962,30 +590,18 @@ } }); - // Render a template with data by passing in the template - // selector and the data to render. - var Renderer = { - - // Render a template with data. The `template` parameter is - // passed to the `TemplateCache` object to retrieve the - // template function. Override this method to provide your own - // custom rendering and template handling for all of Marionette. - render: function render(template, data) { - if (!template) { - throw new MarionetteError({ - name: 'TemplateNotFoundError', - message: 'Cannot render the template since its false, null or undefined.' - }); - } + var _invoke = _.invokeMap || _.invoke; - var templateFunc = _.isFunction(template) ? template : TemplateCache.get(template); + var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - return templateFunc(data); + return arr2; + } else { + return Array.from(arr); } }; - var _invoke = _.invokeMap || _.invoke; - // MixinOptions // - behaviors @@ -1034,48 +650,45 @@ // or it can be a function that returns an object. this._behaviors = _.isObject(behaviors) ? parseBehaviors(this, behaviors) : {}; }, - _getBehaviorTriggers: function _getBehaviorTriggers() { var triggers = _invoke(this._behaviors, 'getTriggers'); - return _.extend.apply(_, [{}].concat(babelHelpers.toConsumableArray(triggers))); + return _.extend.apply(_, [{}].concat(toConsumableArray(triggers))); }, - _getBehaviorEvents: function _getBehaviorEvents() { var events = _invoke(this._behaviors, 'getEvents'); - return _.extend.apply(_, [{}].concat(babelHelpers.toConsumableArray(events))); + return _.extend.apply(_, [{}].concat(toConsumableArray(events))); }, + // proxy behavior $el to the view's $el. _proxyBehaviorViewProperties: function _proxyBehaviorViewProperties() { _invoke(this._behaviors, 'proxyViewProperties'); }, + // delegate modelEvents and collectionEvents _delegateBehaviorEntityEvents: function _delegateBehaviorEntityEvents() { _invoke(this._behaviors, 'delegateEntityEvents'); }, + // undelegate modelEvents and collectionEvents _undelegateBehaviorEntityEvents: function _undelegateBehaviorEntityEvents() { _invoke(this._behaviors, 'undelegateEntityEvents'); }, - _destroyBehaviors: function _destroyBehaviors(args) { // Call destroy on each behavior after // destroying the view. // This unbinds event listeners // that behaviors have registered for. - _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(babelHelpers.toConsumableArray(args))); + _invoke.apply(undefined, [this._behaviors, 'destroy'].concat(toConsumableArray(args))); }, - _bindBehaviorUIElements: function _bindBehaviorUIElements() { _invoke(this._behaviors, 'bindUIElements'); }, - _unbindBehaviorUIElements: function _unbindBehaviorUIElements() { _invoke(this._behaviors, 'unbindUIElements'); }, - _triggerEventOnBehaviors: function _triggerEventOnBehaviors() { var behaviors = this._behaviors; // Use good ol' for as this is a very hot function @@ -1096,22 +709,22 @@ var DelegateEntityEventsMixin = { // Handle `modelEvents`, and `collectionEvents` configuration + _delegateEntityEvents: function _delegateEntityEvents(model, collection) { this._undelegateEntityEvents(model, collection); var modelEvents = _.result(this, 'modelEvents'); - bindEntityEvents.call(this, model, modelEvents); + bindEvents.call(this, model, modelEvents); var collectionEvents = _.result(this, 'collectionEvents'); - bindEntityEvents.call(this, collection, collectionEvents); + bindEvents.call(this, collection, collectionEvents); }, - _undelegateEntityEvents: function _undelegateEntityEvents(model, collection) { var modelEvents = _.result(this, 'modelEvents'); - unbindEntityEvents.call(this, model, modelEvents); + unbindEvents.call(this, model, modelEvents); var collectionEvents = _.result(this, 'collectionEvents'); - unbindEntityEvents.call(this, collection, collectionEvents); + unbindEvents.call(this, collection, collectionEvents); } }; @@ -1158,6 +771,7 @@ // Configure `triggers` to forward DOM events to view // events. `triggers: {"click .foo": "do:foo"}` + _getViewTriggers: function _getViewTriggers(view, triggers) { // Configure the triggers, prevent default // action and stop propagation of DOM events @@ -1167,7 +781,6 @@ return events; }, {}); } - }; // allows for the use of the @ui. syntax within @@ -1185,7 +798,7 @@ // utility method for parsing @ui. syntax strings // into associated selector var normalizeUIString = function normalizeUIString(uiString, ui) { - return uiString.replace(/@ui\.[a-zA-Z_$0-9]*/g, function (r) { + return uiString.replace(/@ui\.[a-zA-Z-_$0-9]*/g, function (r) { return ui[r.slice(4)]; }); }; @@ -1215,24 +828,26 @@ // normalize the keys of passed hash with the views `ui` selectors. // `{"@ui.foo": "bar"}` + normalizeUIKeys: function normalizeUIKeys(hash) { var uiBindings = this._getUIBindings(); return _normalizeUIKeys(hash, uiBindings); }, + // normalize the values of passed hash with the views `ui` selectors. // `{foo: "@ui.bar"}` normalizeUIValues: function normalizeUIValues(hash, properties) { var uiBindings = this._getUIBindings(); return _normalizeUIValues(hash, uiBindings, properties); }, - _getUIBindings: function _getUIBindings() { var uiBindings = _.result(this, '_uiBindings'); var ui = _.result(this, 'ui'); return uiBindings || ui; }, + // This method binds the elements specified in the "ui" hash inside the view's code with // the associated jQuery selectors. _bindUIElements: function _bindUIElements() { @@ -1261,7 +876,6 @@ this.ui = this._ui; }, - _unbindUIElements: function _unbindUIElements() { var _this2 = this; @@ -1279,7 +893,6 @@ delete this._uiBindings; delete this._ui; }, - _getUI: function _getUI(name) { return this._ui[name]; } @@ -1356,8 +969,6 @@ return this; }, - - _getEvents: function _getEvents(eventsArg) { var events = eventsArg || this.events; @@ -1368,6 +979,7 @@ return this.normalizeUIKeys(events); }, + // Configure `triggers` to forward DOM events to view // events. `triggers: {"click .foo": "do:foo"}` getTriggers: function getTriggers() { @@ -1504,7 +1116,6 @@ this._childViewEvents = _.result(this, 'childViewEvents'); this._childViewTriggers = _.result(this, 'childViewTriggers'); }, - _triggerEventOnParentLayout: function _triggerEventOnParentLayout(eventName) { var layoutView = this._parentView(); if (!layoutView) { @@ -1733,11 +1344,17 @@ // Restore the region's element in the DOM. _restoreEl: function _restoreEl() { - if (!this.currentView) { + // There is nothing to replace + if (!this._isReplaced) { return; } var view = this.currentView; + + if (!view) { + return; + } + var parent = view.el.parentNode; if (!parent) { @@ -1747,6 +1364,9 @@ parent.replaceChild(this.el, view.el); this._isReplaced = false; }, + + + // Check to see if the region's el was replaced. isReplaced: function isReplaced() { return !!this._isReplaced; }, @@ -1782,18 +1402,15 @@ view.off('destroy', this.empty, this); this.triggerMethod('before:empty', this, view); - if (this._isReplaced) { - this._restoreEl(); - } + this._restoreEl(); delete this.currentView; if (!view._isDestroyed) { this._removeView(view, options); + delete view._parent; } - delete view._parent; - this.triggerMethod('empty', this, view); return this; }, @@ -1817,7 +1434,6 @@ }, _detachView: function _detachView(view) { var shouldTriggerDetach = !!view._isAttached; - if (shouldTriggerDetach) { triggerMethodOn(view, 'before:detach', view); } @@ -1857,8 +1473,6 @@ delete this.$el; return this; }, - - destroy: function destroy(options) { this.reset(options); return MarionetteObject.prototype.destroy.apply(this, arguments); @@ -1883,12 +1497,14 @@ this.addRegions(_.result(this, 'regions')); }, + // Internal method to re-initialize all of the regions by updating // the `el` that they point to _reInitRegions: function _reInitRegions() { _invoke(this._regions, 'reset'); }, + // Add a single region, by name, to the View addRegion: function addRegion(name, definition) { var regions = {}; @@ -1896,6 +1512,7 @@ return this.addRegions(regions)[name]; }, + // Add multiple regions as a {name: definition, name2: def2} object literal addRegions: function addRegions(regions) { // If there's nothing to add, stop here. @@ -1913,6 +1530,7 @@ return this._addRegions(regions); }, + // internal method to build and add regions _addRegions: function _addRegions(regionDefinitions) { var _this = this; @@ -1924,6 +1542,7 @@ }, {}); }, + // return the region instance from the definition _buildRegion: function _buildRegion(definition) { if (definition instanceof Region) { @@ -1932,7 +1551,6 @@ return this._buildRegionFromDefinition(definition); }, - _buildRegionFromDefinition: function _buildRegionFromDefinition(definition) { if (_.isString(definition)) { return this._buildRegionFromObject({ el: definition }); @@ -1951,7 +1569,6 @@ url: 'marionette.region.html#region-configuration-types' }); }, - _buildRegionFromObject: function _buildRegionFromObject(definition) { var RegionClass = definition.regionClass || this.regionClass; @@ -1965,13 +1582,13 @@ return new RegionClass(options); }, + // Build the region directly from a given `RegionClass` _buildRegionFromRegionClass: function _buildRegionFromRegionClass(RegionClass) { return new RegionClass({ parentEl: _.partial(_.result, this, 'el') }); }, - _addRegion: function _addRegion(region, name) { this.triggerMethod('before:add:region', this, name, region); @@ -1982,6 +1599,7 @@ this.triggerMethod('add:region', this, name, region); }, + // Remove a single region from the View, by name removeRegion: function removeRegion(name) { var region = this._regions[name]; @@ -1991,6 +1609,7 @@ return region; }, + // Remove all regions from the View removeRegions: function removeRegions() { var regions = this.getRegions(); @@ -1999,7 +1618,6 @@ return regions; }, - _removeRegion: function _removeRegion(region, name) { this.triggerMethod('before:remove:region', this, name, region); @@ -2012,6 +1630,7 @@ this.triggerMethod('remove:region', this, name, region); }, + // Empty all regions in the region manager, but // leave them attached emptyRegions: function emptyRegions() { @@ -2020,6 +1639,7 @@ return regions; }, + // Checks to see if view contains region // Accepts the region name // hasRegion('main') @@ -2027,6 +1647,7 @@ return !!this.getRegion(name); }, + // Provides access to regions // Accepts the region name // getRegion('main') @@ -2034,11 +1655,11 @@ return this._regions[name]; }, + // Get all regions getRegions: function getRegions() { return _.clone(this._regions); }, - showChildView: function showChildView(name, view) { var region = this.getRegion(name); @@ -2048,11 +1669,32 @@ return region.show.apply(region, [view].concat(args)); }, - getChildView: function getChildView(name) { return this.getRegion(name).currentView; } + }; + // Render a template with data by passing in the template + // selector and the data to render. + var Renderer = { + + // Render a template with data. The `template` parameter is + // passed to the `TemplateCache` object to retrieve the + // template function. Override this method to provide your own + // custom rendering and template handling for all of Marionette. + + render: function render(template, data) { + if (!template) { + throw new MarionetteError({ + name: 'TemplateNotFoundError', + message: 'Cannot render the template since its false, null or undefined.' + }); + } + + var templateFunc = _.isFunction(template) ? template : TemplateCache.get(template); + + return templateFunc(data); + } }; var ClassOptions$1 = ['behaviors', 'childViewEventPrefix', 'childViewEvents', 'childViewTriggers', 'collectionEvents', 'events', 'modelEvents', 'regionClass', 'regions', 'template', 'templateContext', 'triggers', 'ui']; @@ -2192,6 +1834,7 @@ return _.extend(target, templateContext); }, + // Attaches the content of a given view. // This method can be overridden to optimize rendering, // or to render in a non standard way. @@ -2215,8 +1858,6 @@ _removeChildren: function _removeChildren() { this.removeRegions(); }, - - _getImmediateChildren: function _getImmediateChildren() { return _.chain(this.getRegions()).map('currentView').compact().value(); } @@ -2224,201 +1865,130 @@ _.extend(View.prototype, ViewMixin, RegionsMixin); - var backbone_babysitter = __commonjs(function (module, exports, global) { - // Backbone.BabySitter - // ------------------- - // v1.0.0-pre.1 - // - // Copyright (c)2016 Derick Bailey, Muted Solutions, LLC. - // Distributed under MIT license - // - // http://github.com/marionettejs/backbone.babysitter + var methods = ['forEach', 'each', 'map', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'toArray', 'first', 'initial', 'rest', 'last', 'without', 'isEmpty', 'pluck', 'reduce']; - (function(root, factory) { + var emulateCollection = function emulateCollection(object, listProperty) { + _.each(methods, function (method) { + object[method] = function () { + var list = _.values(_.result(this, listProperty)); + var args = [list].concat(_.toArray(arguments)); + return _[method].apply(_, args); + }; + }); + }; - if (typeof define === 'function' && define.amd) { - define(['backbone', 'underscore'], function(Backbone$$, _$$) { - return factory(Backbone$$, _$$); - }); - } else if (typeof exports !== 'undefined') { - var Backbone$$ = Backbone; - var _$$ = _; - module.exports = factory(Backbone$$, _$$); - } else { - factory(root.Backbone, root._); - } + // Provide a container to store, retrieve and + // shut down child views. + var Container = function Container(views) { + this._views = {}; + this._indexByModel = {}; + this._indexByCustom = {}; + this._updateLength(); - }(__commonjs_global, function(Backbone$$, _$$) { - 'use strict'; + _.each(views, _.bind(this.add, this)); + }; - var previousChildViewContainer = Backbone$$.ChildViewContainer; + emulateCollection(Container.prototype, '_views'); - // BabySitter.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); + // Container Methods + // ----------------- + + _.extend(Container.prototype, { + + // Add a view to this container. Stores the view + // by `cid` and makes it searchable by the model + // cid (and model itself). Optionally specify + // a custom key to store an retrieve the view. + + add: function add(view, customIndex) { + 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 findByModel(model) { + return this.findByModelCid(model.cid); + }, + + + // Find a view by the `cid` of the model that was attached to + // it. Uses the model's `cid` to find the view `cid` and + // retrieve the view using it. + findByModelCid: function findByModelCid(modelCid) { + var viewCid = this._indexByModel[modelCid]; + return this.findByCid(viewCid); + }, + + + // Find a view by a custom indexer. + findByCustom: function findByCustom(index) { + var viewCid = this._indexByCustom[index]; + return this.findByCid(viewCid); + }, + + + // Find by index. This is not guaranteed to be a + // stable index. + findByIndex: function findByIndex(index) { + return _.values(this._views)[index]; + }, + + + // retrieve a view by its `cid` directly + findByCid: function findByCid(cid) { + return this._views[cid]; + }, + + + // Remove a view + remove: function remove(view) { + 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; } - }); - - // 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', 'reduce']; - - _$$.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$$, _$$); - + }, this); - Backbone$$.ChildViewContainer.VERSION = '1.0.0-pre.1'; + // remove the view from the container + delete this._views[viewCid]; - Backbone$$.ChildViewContainer.noConflict = function () { - Backbone$$.ChildViewContainer = previousChildViewContainer; + // update the length + this._updateLength(); return this; - }; + }, - return Backbone$$.ChildViewContainer; - })); + // Update the `.length` attribute on this container + _updateLength: function _updateLength() { + this.length = _.size(this._views); + } }); - var ChildViewContainer = (backbone_babysitter && typeof backbone_babysitter === 'object' && 'default' in backbone_babysitter ? backbone_babysitter['default'] : backbone_babysitter); - var ClassOptions$3 = ['behaviors', 'childView', 'childViewEventPrefix', 'childViewEvents', 'childViewOptions', 'childViewTriggers', 'collectionEvents', 'events', 'filter', 'emptyView', 'emptyViewOptions', 'modelEvents', 'reorderOnSort', 'sort', 'triggers', 'ui', 'viewComparator']; // A view that iterates over a Backbone.Collection @@ -2433,7 +2003,7 @@ // maintaining the sorted order of the collection. // This will fallback onto appending childView's to the end. // - // option to pass `{comparator: compFunction()}` to allow the `CollectionView` + // option to pass `{viewComparator: compFunction()}` to allow the `CollectionView` // to use a custom sort order for the collection. constructor: function constructor(options) { this.render = _.bind(this.render, this); @@ -2550,23 +2120,21 @@ var shouldRender = canBeRendered && filterChanged && !preventRender; if (shouldRender) { - this.triggerMethod('before:apply:filter', this); var previousModels = this._filteredSortedModels(); this.filter = filter; var models = this._filteredSortedModels(); this._applyModelDeltas(models, previousModels); - this.triggerMethod('apply:filter', this); } else { this.filter = filter; } + return this; }, // `removeFilter` is actually an alias for removing filters. removeFilter: function removeFilter(options) { - this.setFilter(null, options); - return this; + return this.setFilter(null, options); }, @@ -2687,8 +2255,10 @@ // Internal method. Separated so that CompositeView can have more control over events // being triggered, around the rendering process _renderChildren: function _renderChildren() { - this._destroyEmptyView(); - this._destroyChildren({ checkEmpty: false }); + if (this._isRendered) { + this._destroyEmptyView(); + this._destroyChildren({ checkEmpty: false }); + } var models = this._filteredSortedModels(); if (this.isEmpty({ processedModels: models })) { @@ -2716,7 +2286,7 @@ // Allow the collection to be sorted by a custom view comparator _filteredSortedModels: function _filteredSortedModels(addedAt) { - if (!this.collection) { + if (!this.collection || !this.collection.length) { return []; } @@ -2774,7 +2344,7 @@ // Internal method to show an empty view in place of a collection of child views, // when the collection is empty _showEmptyView: function _showEmptyView() { - var EmptyView = this.getEmptyView(); + var EmptyView = this._getEmptyView(); if (EmptyView && !this._showingEmptyView) { this._showingEmptyView = true; @@ -2811,13 +2381,18 @@ // Retrieve the empty view class - getEmptyView: function getEmptyView() { - return this.emptyView; + _getEmptyView: function _getEmptyView() { + var emptyView = this.emptyView; + + if (!emptyView) { + return; + } + + return this._getView(emptyView); }, - // Retrieve the `childView` class, either from `this.options.childView` or from - // the `childView` in the object definition. The "options" takes precedence. + // Retrieve the `childView` class // The `childView` property can be either a view class or a function that // returns a view class. If it is a function, it will receive the model that // will be passed to the view instance (created from the returned view class) @@ -2831,18 +2406,27 @@ }); } - // first check if the `childView` is a view class (the common case) - // then check if it's a function (which we assume that returns a view class) - if (childView.prototype instanceof Backbone.View || childView === Backbone.View) { - return childView; - } else if (_.isFunction(childView)) { - return childView.call(this, child); - } else { + childView = this._getView(childView, child); + + if (!childView) { throw new MarionetteError({ name: 'InvalidChildViewError', message: '"childView" must be a view class or a function that returns a view class' }); } + + return childView; + }, + + + // First check if the `view` is a view class (the common case) + // Then check if it's a function (which we assume that returns a view class) + _getView: function _getView(view, child) { + if (view.prototype instanceof Backbone.View || view === Backbone.View) { + return view; + } else if (_.isFunction(view)) { + return view.call(this, child); + } }, @@ -2956,7 +2540,7 @@ // in the collection in order to keep the children in sync with the collection. removeChildView: function removeChildView(view) { if (!view || view._isDestroyed) { - return; + return view; } this.triggerMethod('before:remove:child', this, view); @@ -2974,6 +2558,8 @@ // decrement the index of views after this one this._updateIndices(view, false); + + return view; }, @@ -3061,7 +2647,7 @@ // Internal method to set up the `children` object for storing all of the child views _initChildViewStorage: function _initChildViewStorage() { - this.children = new ChildViewContainer(); + this.children = new Container(); }, @@ -3105,35 +2691,35 @@ // Set up the child view event forwarding. Uses a "childview:" prefix in front of all forwarded events. _proxyChildEvents: function _proxyChildEvents(view) { + var _this6 = this; + var prefix = _.result(this, 'childViewEventPrefix'); // Forward all child view events through the parent, // prepending "childview:" to the event name this.listenTo(view, 'all', function (eventName) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } var childEventName = prefix + ':' + eventName; - var childViewEvents = this.normalizeMethods(this._childViewEvents); + var childViewEvents = _this6.normalizeMethods(_this6._childViewEvents); // call collectionView childViewEvent if defined - - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - if (typeof childViewEvents !== 'undefined' && _.isFunction(childViewEvents[eventName])) { - childViewEvents[eventName].apply(this, args); + childViewEvents[eventName].apply(_this6, args); } // use the parent view's proxyEvent handlers - var childViewTriggers = this._childViewTriggers; + var childViewTriggers = _this6._childViewTriggers; // Call the event with the proxy name on the parent layout if (childViewTriggers && _.isString(childViewTriggers[eventName])) { - this.triggerMethod.apply(this, [childViewTriggers[eventName]].concat(args)); + _this6.triggerMethod.apply(_this6, [childViewTriggers[eventName]].concat(args)); } - this.triggerMethod.apply(this, [childEventName].concat(args)); + _this6.triggerMethod.apply(_this6, [childEventName].concat(args)); }); } }); @@ -3143,8 +2729,7 @@ var ClassOptions$4 = ['childViewContainer', 'template', 'templateContext']; // Used for rendering a branch-leaf, hierarchical structure. - // Extends directly from CollectionView and also renders an - // a child view as `modelView`, for the top leaf + // Extends directly from CollectionView // @deprecated var CompositeView = CollectionView.extend({ @@ -3197,16 +2782,18 @@ // finally check if it's a function (which we assume that returns a view class) if (!childView) { return this.constructor; - } else if (childView.prototype instanceof Backbone.View || childView === Backbone.View) { - return childView; - } else if (_.isFunction(childView)) { - return childView.call(this, child); - } else { + } + + childView = this._getView(childView, child); + + if (!childView) { throw new MarionetteError({ name: 'InvalidChildViewError', message: '"childView" must be a view class or a function that returns a view class' }); } + + return childView; }, @@ -3340,6 +2927,7 @@ MarionetteObject.apply(this, arguments); }, + // proxy behavior $ method to the view // this is useful for doing jquery DOM lookups // scoped to behaviors view. @@ -3347,6 +2935,7 @@ return this.view.$.apply(this.view, arguments); }, + // Stops the behavior from listening to events. // Overrides Object#destroy to prevent additional events from being triggered. destroy: function destroy() { @@ -3354,44 +2943,39 @@ return this; }, - proxyViewProperties: function proxyViewProperties() { this.$el = this.view.$el; this.el = this.view.el; return this; }, - bindUIElements: function bindUIElements() { this._bindUIElements(); return this; }, - unbindUIElements: function unbindUIElements() { this._unbindUIElements(); return this; }, - getUI: function getUI(name) { this.view._ensureViewIsIntact(); return this._getUI(name); }, + // Handle `modelEvents`, and `collectionEvents` configuration delegateEntityEvents: function delegateEntityEvents() { this._delegateEntityEvents(this.view.model, this.view.collection); return this; }, - undelegateEntityEvents: function undelegateEntityEvents() { this._undelegateEntityEvents(this.view.model, this.view.collection); return this; }, - getEvents: function getEvents() { // Normalize behavior events hash to allow // a user to use the @ui. syntax. @@ -3411,6 +2995,7 @@ }, {}, this); }, + // Internal method to build all trigger handlers for a given behavior getTriggers: function getTriggers() { if (!this.triggers) { @@ -3423,7 +3008,6 @@ return this._getViewTriggers(this.view, behaviorTriggers); } - }); _.extend(Behavior.prototype, DelegateEntityEventsMixin, TriggersMixin, UIMixin); @@ -3444,6 +3028,7 @@ MarionetteObject.prototype.constructor.apply(this, arguments); }, + regionClass: Region, _initRegion: function _initRegion(options) { @@ -3461,11 +3046,9 @@ this._region = region; }, - getRegion: function getRegion() { return this._region; }, - showView: function showView(view) { var region = this.getRegion(); @@ -3475,24 +3058,22 @@ return region.show.apply(region, [view].concat(args)); }, - getView: function getView() { return this.getRegion().currentView; }, + // kick off all of the application's processes. start: function start(options) { this.triggerMethod('before:start', this, options); this.triggerMethod('start', this, options); return this; } - }); var ClassOptions$7 = ['appRoutes', 'controller']; var AppRouter = Backbone.Router.extend({ - constructor: function constructor(options) { this._setOptions(options); @@ -3506,6 +3087,7 @@ this.on('route', this._processOnRoute, this); }, + // Similar to route method on a Backbone Router but // method is called on the controller appRoute: function appRoute(route, methodName) { @@ -3514,6 +3096,7 @@ return this; }, + // process the route event and trigger the onRoute // method call, if it exists _processOnRoute: function _processOnRoute(routeName, routeArgs) { @@ -3525,6 +3108,7 @@ } }, + // Internal method to process the `appRoutes` for the // router, and turn them in to routes that trigger the // specified method on the specified `controller`. @@ -3543,11 +3127,9 @@ return this; }, - _getController: function _getController() { return this.controller; }, - _addAppRoute: function _addAppRoute(controller, route, methodName) { var method = controller[methodName]; @@ -3558,6 +3140,7 @@ this.route(route, methodName, _.bind(method, controller)); }, + triggerMethod: triggerMethod }); @@ -3604,12 +3187,10 @@ }; // Utilities - Marionette.bindEntityEvents = proxy(bindEntityEvents); - Marionette.unbindEntityEvents = proxy(unbindEntityEvents); - Marionette.bindRadioEvents = proxy(bindEntityEvents); - Marionette.unbindRadioEvents = proxy(unbindEntityEvents); - Marionette.bindRadioRequests = proxy(bindRadioRequests); - Marionette.unbindRadioRequests = proxy(unbindRadioRequests); + Marionette.bindEvents = proxy(bindEvents); + Marionette.unbindEvents = proxy(unbindEvents); + Marionette.bindRequests = proxy(bindRequests); + Marionette.unbindRequests = proxy(unbindRequests); Marionette.mergeOptions = proxy(mergeOptions); Marionette.getOption = proxy(getOption); Marionette.normalizeMethods = proxy(normalizeMethods); @@ -3631,6 +3212,7 @@ Marionette.Renderer = Renderer; Marionette.TemplateCache = TemplateCache; Marionette.View = View; + Marionette.ChildViewContainer = Container; Marionette.CollectionView = CollectionView; Marionette.CompositeView = CompositeView; Marionette.Behavior = Behavior; diff --git a/lib/backbone.marionette.js.map b/lib/backbone.marionette.js.map index 7e3a9b38e0..23a5e14b9c 100644 --- a/lib/backbone.marionette.js.map +++ b/lib/backbone.marionette.js.map @@ -1 +1 @@ -{"version":3,"file":"backbone.marionette.js","sources":["package.json","src/utils/_proxy.js","src/utils/extend.js","src/utils/isNodeAttached.js","src/utils/mergeOptions.js","src/utils/getOption.js","src/utils/normalizeMethods.js","src/utils/deprecate.js","src/trigger-method.js","src/monitor-view-events.js","src/utils/_setOptions.js","src/error.js","src/bind-entity-events.js","src/mixins/common.js","node_modules/backbone.radio/build/backbone.radio.js","src/bind-radio-requests.js","src/mixins/radio.js","src/object.js","src/template-cache.js","src/renderer.js","src/utils/_invoke.js","src/mixins/behaviors.js","src/mixins/delegate-entity-events.js","src/utils/getUniqueEventName.js","src/mixins/triggers.js","src/mixins/ui.js","src/mixins/view.js","src/utils/destroyBackboneView.js","src/region.js","src/mixins/regions.js","src/view.js","node_modules/backbone.babysitter/lib/backbone.babysitter.js","src/collection-view.js","src/composite-view.js","src/behavior.js","src/application.js","src/app-router.js","src/config/behaviors-lookup.js","src/config/features.js","src/backbone.marionette.js"],"sourcesContent":["{\n \"name\": \"backbone.marionette\",\n \"description\": \"The Backbone Framework\",\n \"version\": \"3.0.0-pre.4\",\n \"homepage\": \"https://github.com/marionettejs/backbone.marionette\",\n \"main\": \"lib/core/backbone.marionette.js\",\n \"keywords\": [\n \"backbone\",\n \"plugin\",\n \"marionette\",\n \"composite\",\n \"architecture\",\n \"single\",\n \"page\",\n \"app\",\n \"client\",\n \"browser\"\n ],\n \"license\": \"MIT\",\n \"scripts\": {\n \"build\": \"gulp build\",\n \"coverage\": \"gulp coverage\",\n \"coveralls\": \"gulp coveralls\",\n \"test\": \"gulp\",\n \"test-browser\": \"gulp test-browser\"\n },\n \"author\": {\n \"name\": \"Derick Bailey\",\n \"email\": \"derickbailey@gmail.com\",\n \"url\": \"http://derickbailey.com/\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/marionettejs/backbone.marionette/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/marionettejs/backbone.marionette.git\"\n },\n \"github\": \"https://github.com/marionettejs/backbone.marionette\",\n \"dependencies\": {\n \"backbone.babysitter\": \"1.0.0-pre.1\",\n \"backbone.radio\": \"2.0.0-pre.1\"\n },\n \"peerDependencies\": {\n \"backbone\": \"1.2.1 - 1.3.2\",\n \"underscore\": \"1.8 - 1.8.3\"\n },\n \"devDependencies\": {\n \"babel-core\": \"^6.7.0\",\n \"babel-eslint\": \"^6.0.4\",\n \"babel-polyfill\": \"^6.6.1\",\n \"babel-preset-es2015\": \"^6.3.13\",\n \"babel-preset-es2015-rollup\": \"^1.1.1\",\n \"babel-register\": \"^6.4.3\",\n \"backbone\": \"1.2.1 - 1.3.2\",\n \"chai\": \"^3.4.0\",\n \"chai-jq\": \"0.0.9\",\n \"dox\": \"git://github.com/jasonLaster/dox.git#marked\",\n \"eslint\": \"2.9.0\",\n \"gulp\": \"^3.9.0\",\n \"gulp-coveralls\": \"^0.1.4\",\n \"gulp-eslint\": \"^2.0.0\",\n \"gulp-file\": \"^0.2.0\",\n \"gulp-filter\": \"^3.0.1\",\n \"gulp-istanbul\": \"^0.10.2\",\n \"gulp-lintspaces\": \"^0.4.1\",\n \"gulp-livereload\": \"^3.8.1\",\n \"gulp-mocha\": \"^2.1.3\",\n \"gulp-plumber\": \"^1.0.1\",\n \"gulp-rename\": \"^1.2.2\",\n \"gulp-sourcemaps\": \"^1.6.0\",\n \"gulp-uglify\": \"^1.4.2\",\n \"gulp-util\": \"^3.0.7\",\n \"gulp-yaml-validate\": \"^1.0.2\",\n \"isparta\": \"^4.0.0\",\n \"jquery\": \"^2.1.4\",\n \"jsdom\": \"^8.0.2\",\n \"mocha\": \"^2.4.5\",\n \"opn\": \"^4.0.2\",\n \"rollup\": \"^0.26.1\",\n \"rollup-plugin-babel\": \"^2.3.9\",\n \"rollup-plugin-commonjs\": \"^2.2.1\",\n \"rollup-plugin-json\": \"^2.0.0\",\n \"rollup-plugin-multi-entry\": \"^1.2.0\",\n \"rollup-plugin-node-globals\": \"^1.0.5\",\n \"rollup-plugin-node-resolve\": \"^1.5.0\",\n \"run-sequence\": \"^1.1.5\",\n \"sinon\": \"^1.17.2\",\n \"sinon-chai\": \"^2.8.0\",\n \"underscore\": \"1.8 - 1.8.3\"\n }\n}\n","//Internal utility for creating context style global utils\nvar proxy = function(method) {\n return function(context, ...args) {\n return method.apply(context, args);\n };\n};\n\nexport default proxy;\n","// Marionette.extend\n// -----------------\n\nimport Backbone from 'backbone';\n\n// Borrow the Backbone `extend` method so we can use it as needed\nvar extend = Backbone.Model.extend;\n\nexport default extend;\n","// Marionette.isNodeAttached\n// -------------------------\n\nimport Backbone from 'backbone';\n\n// Determine if `el` is a child of the document\nvar isNodeAttached = function(el) {\n return Backbone.$.contains(document.documentElement, el);\n};\n\nexport default isNodeAttached;\n","import _ from 'underscore';\n\n// Merge `keys` from `options` onto `this`\nvar mergeOptions = function(options, keys) {\n if (!options) { return; }\n _.extend(this, _.pick(options, keys));\n};\n\nexport default mergeOptions;\n","// Marionette.getOption\n// --------------------\n\n// Retrieve an object, function or other value from the\n// object or its `options`, with `options` taking precedence.\nvar getOption = function(optionName) {\n if (!optionName) { return; }\n if (this.options && (this.options[optionName] !== undefined)) {\n return this.options[optionName];\n } else {\n return this[optionName];\n }\n};\n\nexport default getOption;\n","import _ from 'underscore';\n\n// Marionette.normalizeMethods\n// ----------------------\n\n// Pass in a mapping of events => functions or function names\n// and return a mapping of events => functions\nvar normalizeMethods = function(hash) {\n return _.reduce(hash, (normalizedHash, method, name) => {\n if (!_.isFunction(method)) {\n method = this[method];\n }\n if (method) {\n normalizedHash[name] = method;\n }\n return normalizedHash;\n }, {});\n};\n\nexport default normalizeMethods;\n","/* global console */\n\nimport _ from 'underscore';\n\nimport Marionette from '../backbone.marionette';\n\nvar deprecate = function(message, test) {\n if (_.isObject(message)) {\n message = (\n message.prev + ' is going to be removed in the future. ' +\n 'Please use ' + message.next + ' instead.' +\n (message.url ? ' See: ' + message.url : '')\n );\n }\n\n if (!Marionette.DEV_MODE) {\n return;\n }\n\n if ((test === undefined || !test) && !deprecate._cache[message]) {\n deprecate._warn('Deprecation warning: ' + message);\n deprecate._cache[message] = true;\n }\n};\n\ndeprecate._console = typeof console !== 'undefined' ? console : {};\ndeprecate._warn = function() {\n var warn = deprecate._console.warn || deprecate._console.log || function() {};\n return warn.apply(deprecate._console, arguments);\n};\ndeprecate._cache = {};\n\nexport default deprecate;\n","// Trigger Method\n// --------------\n\nimport _ from 'underscore';\nimport getOption from './utils/getOption';\n\n// split the event name on the \":\"\nvar splitter = /(^|:)(\\w)/gi;\n\n// take the event section (\"section1:section2:section3\")\n// and turn it in to uppercase name onSection1Section2Section3\nfunction getEventName(match, prefix, eventName) {\n return eventName.toUpperCase();\n}\n\n// Trigger an event and/or a corresponding method name. Examples:\n//\n// `this.triggerMethod(\"foo\")` will trigger the \"foo\" event and\n// call the \"onFoo\" method.\n//\n// `this.triggerMethod(\"foo:bar\")` will trigger the \"foo:bar\" event and\n// call the \"onFooBar\" method.\nexport function triggerMethod(event, ...args) {\n // get the method name from the event name\n var methodName = 'on' + event.replace(splitter, getEventName);\n var method = getOption.call(this, methodName);\n var result;\n\n // call the onMethodName if it exists\n if (_.isFunction(method)) {\n // pass all args, except the event name\n result = method.apply(this, args);\n }\n\n // trigger the event\n this.trigger(event, ...args);\n\n return result;\n}\n\n// triggerMethodOn invokes triggerMethod on a specific context\n//\n// e.g. `Marionette.triggerMethodOn(view, 'show')`\n// will trigger a \"show\" event or invoke onShow the view.\nexport function triggerMethodOn(context, ...args) {\n var fnc = _.isFunction(context.triggerMethod) ? context.triggerMethod : triggerMethod;\n return fnc.apply(context, args);\n}\n","// DOM Refresh\n// -----------\n\nimport { triggerMethodOn } from './trigger-method';\nimport _ from 'underscore';\n\n// Trigger method on children unless a pure Backbone.View\nfunction triggerMethodChildren(view, event, shouldTrigger) {\n if (!view._getImmediateChildren) { return; }\n _.each(view._getImmediateChildren(), child => {\n if (!shouldTrigger(child)) { return; }\n triggerMethodOn(child, event, child);\n });\n}\n\nfunction shouldTriggerAttach(view) {\n return !view._isAttached;\n}\n\nfunction shouldAttach(view) {\n if (!shouldTriggerAttach(view)) { return false; }\n view._isAttached = true;\n return true;\n}\n\nfunction shouldTriggerDetach(view) {\n return view._isAttached;\n}\n\nfunction shouldDetach(view) {\n if (!shouldTriggerDetach(view)) { return false; }\n view._isAttached = false;\n return true;\n}\n\n// Monitor a view's state, propagating attach/detach events to children and firing dom:refresh\n// whenever a rendered view is attached or an attached view is rendered.\nfunction monitorViewEvents(view) {\n if (view._areViewEventsMonitored) { return; }\n\n view._areViewEventsMonitored = true;\n\n function handleBeforeAttach() {\n triggerMethodChildren(view, 'before:attach', shouldTriggerAttach);\n }\n\n function handleAttach() {\n triggerMethodChildren(view, 'attach', shouldAttach);\n triggerDOMRefresh();\n }\n\n function handleBeforeDetach() {\n triggerMethodChildren(view, 'before:detach', shouldTriggerDetach);\n }\n\n function handleDetach() {\n triggerMethodChildren(view, 'detach', shouldDetach);\n }\n\n function handleRender() {\n triggerDOMRefresh();\n }\n\n function triggerDOMRefresh() {\n if (view._isAttached && view._isRendered) {\n triggerMethodOn(view, 'dom:refresh', view);\n }\n }\n\n view.on({\n 'before:attach': handleBeforeAttach,\n 'attach': handleAttach,\n 'before:detach': handleBeforeDetach,\n 'detach': handleDetach,\n 'render': handleRender\n });\n}\n\nexport default monitorViewEvents;\n","import _ from 'underscore';\n\n// Internal utility for setting options consistently across Mn\nvar _setOptions = function(...args) {\n this.options = _.extend({}, _.result(this, 'options'), ...args);\n};\n\nexport default _setOptions;\n","// Error\n// -----\n\nimport _ from 'underscore';\nimport extend from './utils/extend';\nimport {version} from '../package.json';\n\nvar errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number'];\n\nvar MarionetteError = extend.call(Error, {\n urlRoot: 'http://marionettejs.com/docs/v' + version + '/',\n\n constructor: function MarionetteError(message, options) {\n if (_.isObject(message)) {\n options = message;\n message = options.message;\n } else if (!options) {\n options = {};\n }\n\n var error = Error.call(this, message);\n _.extend(this, _.pick(error, errorProps), _.pick(options, errorProps));\n\n this.captureStackTrace();\n\n if (options.url) {\n this.url = this.urlRoot + options.url;\n }\n },\n\n captureStackTrace: function() {\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MarionetteError);\n }\n },\n\n toString: function() {\n return this.name + ': ' + this.message + (this.url ? ' See: ' + this.url : '');\n }\n});\n\nMarionetteError.extend = extend;\n\nexport default MarionetteError;\n","// Bind Entity Events & Unbind Entity Events\n// -----------------------------------------\n//\n// These methods are used to bind/unbind a backbone \"entity\" (e.g. collection/model)\n// to methods on a target object.\n//\n// The first parameter, `target`, must have the Backbone.Events module mixed in.\n//\n// The second parameter is the `entity` (Backbone.Model, Backbone.Collection or\n// any object that has Backbone.Events mixed in) to bind the events from.\n//\n// The third parameter is a hash of { \"event:name\": \"eventHandler\" }\n// configuration. Multiple handlers can be separated by a space. A\n// function can be supplied instead of a string handler name.\n\nimport _ from 'underscore';\nimport MarionetteError from './error';\n\n// Bind/unbind the event to handlers specified as a string of\n// handler names on the target object\nfunction bindFromStrings(target, entity, evt, methods, actionName) {\n var methodNames = methods.split(/\\s+/);\n\n _.each(methodNames, function(methodName) {\n var method = target[methodName];\n if (!method) {\n throw new MarionetteError(`Method \"${methodName}\" was configured as an event handler, but does not exist.`);\n }\n\n target[actionName](entity, evt, method);\n });\n}\n\n// generic looping function\nfunction iterateEvents(target, entity, bindings, actionName) {\n if (!entity || !bindings) { return; }\n\n // type-check bindings\n if (!_.isObject(bindings)) {\n throw new MarionetteError({\n message: 'Bindings must be an object.',\n url: 'marionette.functions.html#marionettebindentityevents'\n });\n }\n\n // iterate the bindings and bind/unbind them\n _.each(bindings, function(method, evt) {\n\n // allow for a list of method names as a string\n if (_.isString(method)) {\n bindFromStrings(target, entity, evt, method, actionName);\n return;\n }\n\n target[actionName](entity, evt, method);\n });\n}\n\nfunction bindEntityEvents(entity, bindings) {\n iterateEvents(this, entity, bindings, 'listenTo');\n return this;\n}\n\nfunction unbindEntityEvents(entity, bindings) {\n iterateEvents(this, entity, bindings, 'stopListening');\n return this;\n}\n\n// Export Public API\nexport {\n bindEntityEvents,\n unbindEntityEvents\n};\n","import getOption from '../utils/getOption';\nimport mergeOptions from '../utils/mergeOptions';\nimport normalizeMethods from '../utils/normalizeMethods';\nimport _setOptions from '../utils/_setOptions';\nimport {\n bindEntityEvents,\n unbindEntityEvents\n} from '../bind-entity-events';\n\nexport default {\n\n // Imports the \"normalizeMethods\" to transform hashes of\n // events=>function references/names to a hash of events=>function references\n normalizeMethods: normalizeMethods,\n\n _setOptions: _setOptions,\n\n // A handy way to merge passed-in options onto the instance\n mergeOptions: mergeOptions,\n\n // Enable getting options from this or this.options by name.\n getOption: getOption,\n\n // Enable binding view's events from another entity.\n bindEntityEvents: bindEntityEvents,\n\n // Enable unbinding view's events from another entity.\n unbindEntityEvents: unbindEntityEvents\n};\n","// Backbone.Radio v2.0.0-pre.1\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('underscore'), require('backbone')) :\n typeof define === 'function' && define.amd ? define(['underscore', 'backbone'], factory) :\n (global.Backbone = global.Backbone || {}, global.Backbone.Radio = factory(global._,global.Backbone));\n}(this, function (_,Backbone) { 'use strict';\n\n _ = 'default' in _ ? _['default'] : _;\n Backbone = 'default' in Backbone ? Backbone['default'] : Backbone;\n\n var babelHelpers = {};\n babelHelpers.typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) {\n return typeof obj;\n } : function (obj) {\n return obj && typeof Symbol === \"function\" && obj.constructor === Symbol ? \"symbol\" : typeof obj;\n };\n babelHelpers;\n\n var previousRadio = Backbone.Radio;\n\n var Radio = Backbone.Radio = {};\n\n Radio.VERSION = '2.0.0-pre.1';\n\n // This allows you to run multiple instances of Radio on the same\n // webapp. After loading the new version, call `noConflict()` to\n // get a reference to it. At the same time the old version will be\n // returned to Backbone.Radio.\n Radio.noConflict = function () {\n Backbone.Radio = previousRadio;\n return this;\n };\n\n // Whether or not we're in DEBUG mode or not. DEBUG mode helps you\n // get around the issues of lack of warnings when events are mis-typed.\n Radio.DEBUG = false;\n\n // Format debug text.\n Radio._debugText = function (warning, eventName, channelName) {\n return warning + (channelName ? ' on the ' + channelName + ' channel' : '') + ': \"' + eventName + '\"';\n };\n\n // This is the method that's called when an unregistered event was called.\n // By default, it logs warning to the console. By overriding this you could\n // make it throw an Error, for instance. This would make firing a nonexistent event\n // have the same consequence as firing a nonexistent method on an Object.\n Radio.debugLog = function (warning, eventName, channelName) {\n if (Radio.DEBUG && console && console.warn) {\n console.warn(Radio._debugText(warning, eventName, channelName));\n }\n };\n\n var eventSplitter = /\\s+/;\n\n // An internal method used to handle Radio's method overloading for Requests.\n // It's borrowed from Backbone.Events. It differs from Backbone's overload\n // API (which is used in Backbone.Events) in that it doesn't support space-separated\n // event names.\n Radio._eventsApi = function (obj, action, name, rest) {\n if (!name) {\n return false;\n }\n\n var results = {};\n\n // Handle event maps.\n if ((typeof name === 'undefined' ? 'undefined' : babelHelpers.typeof(name)) === 'object') {\n for (var key in name) {\n var result = obj[action].apply(obj, [key, name[key]].concat(rest));\n eventSplitter.test(key) ? _.extend(results, result) : results[key] = result;\n }\n return results;\n }\n\n // Handle space separated event names.\n if (eventSplitter.test(name)) {\n var names = name.split(eventSplitter);\n for (var i = 0, l = names.length; i < l; i++) {\n results[names[i]] = obj[action].apply(obj, [names[i]].concat(rest));\n }\n return results;\n }\n\n return false;\n };\n\n // An optimized way to execute callbacks.\n Radio._callHandler = function (callback, context, args) {\n var a1 = args[0],\n a2 = args[1],\n a3 = args[2];\n switch (args.length) {\n case 0:\n return callback.call(context);\n case 1:\n return callback.call(context, a1);\n case 2:\n return callback.call(context, a1, a2);\n case 3:\n return callback.call(context, a1, a2, a3);\n default:\n return callback.apply(context, args);\n }\n };\n\n // A helper used by `off` methods to the handler from the store\n function removeHandler(store, name, callback, context) {\n var event = store[name];\n if ((!callback || callback === event.callback || callback === event.callback._callback) && (!context || context === event.context)) {\n delete store[name];\n return true;\n }\n }\n\n function removeHandlers(store, name, callback, context) {\n store || (store = {});\n var names = name ? [name] : _.keys(store);\n var matched = false;\n\n for (var i = 0, length = names.length; i < length; i++) {\n name = names[i];\n\n // If there's no event by this name, log it and continue\n // with the loop\n if (!store[name]) {\n continue;\n }\n\n if (removeHandler(store, name, callback, context)) {\n matched = true;\n }\n }\n\n return matched;\n }\n\n /*\n * tune-in\n * -------\n * Get console logs of a channel's activity\n *\n */\n\n var _logs = {};\n\n // This is to produce an identical function in both tuneIn and tuneOut,\n // so that Backbone.Events unregisters it.\n function _partial(channelName) {\n return _logs[channelName] || (_logs[channelName] = _.partial(Radio.log, channelName));\n }\n\n _.extend(Radio, {\n\n // Log information about the channel and event\n log: function log(channelName, eventName) {\n if (typeof console === 'undefined') {\n return;\n }\n var args = _.drop(arguments, 2);\n console.log('[' + channelName + '] \"' + eventName + '\"', args);\n },\n\n // Logs all events on this channel to the console. It sets an\n // internal value on the channel telling it we're listening,\n // then sets a listener on the Backbone.Events\n tuneIn: function tuneIn(channelName) {\n var channel = Radio.channel(channelName);\n channel._tunedIn = true;\n channel.on('all', _partial(channelName));\n return this;\n },\n\n // Stop logging all of the activities on this channel to the console\n tuneOut: function tuneOut(channelName) {\n var channel = Radio.channel(channelName);\n channel._tunedIn = false;\n channel.off('all', _partial(channelName));\n delete _logs[channelName];\n return this;\n }\n });\n\n /*\n * Backbone.Radio.Requests\n * -----------------------\n * A messaging system for requesting data.\n *\n */\n\n function makeCallback(callback) {\n return _.isFunction(callback) ? callback : function () {\n return callback;\n };\n }\n\n Radio.Requests = {\n\n // Make a request\n request: function request(name) {\n var args = _.rest(arguments);\n var results = Radio._eventsApi(this, 'request', name, args);\n if (results) {\n return results;\n }\n var channelName = this.channelName;\n var requests = this._requests;\n\n // Check if we should log the request, and if so, do it\n if (channelName && this._tunedIn) {\n Radio.log.apply(this, [channelName, name].concat(args));\n }\n\n // If the request isn't handled, log it in DEBUG mode and exit\n if (requests && (requests[name] || requests['default'])) {\n var handler = requests[name] || requests['default'];\n args = requests[name] ? args : arguments;\n return Radio._callHandler(handler.callback, handler.context, args);\n } else {\n Radio.debugLog('An unhandled request was fired', name, channelName);\n }\n },\n\n // Set up a handler for a request\n reply: function reply(name, callback, context) {\n if (Radio._eventsApi(this, 'reply', name, [callback, context])) {\n return this;\n }\n\n this._requests || (this._requests = {});\n\n if (this._requests[name]) {\n Radio.debugLog('A request was overwritten', name, this.channelName);\n }\n\n this._requests[name] = {\n callback: makeCallback(callback),\n context: context || this\n };\n\n return this;\n },\n\n // Set up a handler that can only be requested once\n replyOnce: function replyOnce(name, callback, context) {\n if (Radio._eventsApi(this, 'replyOnce', name, [callback, context])) {\n return this;\n }\n\n var self = this;\n\n var once = _.once(function () {\n self.stopReplying(name);\n return makeCallback(callback).apply(this, arguments);\n });\n\n return this.reply(name, once, context);\n },\n\n // Remove handler(s)\n stopReplying: function stopReplying(name, callback, context) {\n if (Radio._eventsApi(this, 'stopReplying', name)) {\n return this;\n }\n\n // Remove everything if there are no arguments passed\n if (!name && !callback && !context) {\n delete this._requests;\n } else if (!removeHandlers(this._requests, name, callback, context)) {\n Radio.debugLog('Attempted to remove the unregistered request', name, this.channelName);\n }\n\n return this;\n }\n };\n\n /*\n * Backbone.Radio.channel\n * ----------------------\n * Get a reference to a channel by name.\n *\n */\n\n Radio._channels = {};\n\n Radio.channel = function (channelName) {\n if (!channelName) {\n throw new Error('You must provide a name for the channel.');\n }\n\n if (Radio._channels[channelName]) {\n return Radio._channels[channelName];\n } else {\n return Radio._channels[channelName] = new Radio.Channel(channelName);\n }\n };\n\n /*\n * Backbone.Radio.Channel\n * ----------------------\n * A Channel is an object that extends from Backbone.Events,\n * and Radio.Requests.\n *\n */\n\n Radio.Channel = function (channelName) {\n this.channelName = channelName;\n };\n\n _.extend(Radio.Channel.prototype, Backbone.Events, Radio.Requests, {\n\n // Remove all handlers from the messaging systems of this channel\n reset: function reset() {\n this.off();\n this.stopListening();\n this.stopReplying();\n return this;\n }\n });\n\n /*\n * Top-level API\n * -------------\n * Supplies the 'top-level API' for working with Channels directly\n * from Backbone.Radio.\n *\n */\n\n var channel;\n var args;\n var systems = [Backbone.Events, Radio.Requests];\n _.each(systems, function (system) {\n _.each(system, function (method, methodName) {\n Radio[methodName] = function (channelName) {\n args = _.rest(arguments);\n channel = this.channel(channelName);\n return channel[methodName].apply(channel, args);\n };\n });\n });\n\n Radio.reset = function (channelName) {\n var channels = !channelName ? this._channels : [this._channels[channelName]];\n _.invoke(channels, 'reset');\n };\n\n return Radio;\n\n}));\n//# sourceMappingURL=./backbone.radio.js.map","// Bind/Unbind Radio Requests\n// -----------------------------------------\n//\n// These methods are used to bind/unbind a backbone.radio request\n// to methods on a target object.\n//\n// The first parameter, `target`, will set the context of the reply method\n//\n// The second parameter is the `Radio.channel` to bind the reply to.\n//\n// The third parameter is a hash of { \"request:name\": \"replyHandler\" }\n// configuration. A function can be supplied instead of a string handler name.\n\nimport _ from 'underscore';\nimport normalizeMethods from './utils/normalizeMethods';\nimport MarionetteError from './error';\n\nfunction iterateReplies(target, channel, bindings, actionName) {\n if (!channel || !bindings) { return; }\n\n // type-check bindings\n if (!_.isObject(bindings)) {\n throw new MarionetteError({\n message: 'Bindings must be an object.',\n url: 'marionette.functions.html#marionettebindradiorequests'\n });\n }\n\n var normalizedRadioRequests = normalizeMethods.call(target, bindings);\n\n channel[actionName](normalizedRadioRequests, target);\n}\n\nfunction bindRadioRequests(channel, bindings) {\n iterateReplies(this, channel, bindings, 'reply');\n return this;\n}\n\nfunction unbindRadioRequests(channel, bindings) {\n iterateReplies(this, channel, bindings, 'stopReplying');\n return this;\n}\n\nexport {\n bindRadioRequests,\n unbindRadioRequests\n};\n","import _ from 'underscore';\nimport Radio from 'backbone.radio';\n\nimport {\n bindRadioRequests,\n unbindRadioRequests\n} from '../bind-radio-requests';\n\nimport {\n bindEntityEvents as bindRadioEvents,\n unbindEntityEvents as unbindRadioEvents\n} from '../bind-entity-events';\n\n// MixinOptions\n// - channelName\n// - radioEvents\n// - radioRequests\n\nexport default {\n\n _initRadio: function() {\n var channelName = _.result(this, 'channelName');\n\n if (!channelName) {\n return;\n }\n\n var channel = this._channel = Radio.channel(channelName);\n\n var radioEvents = _.result(this, 'radioEvents');\n this.bindRadioEvents(channel, radioEvents);\n\n var radioRequests = _.result(this, 'radioRequests');\n this.bindRadioRequests(channel, radioRequests);\n\n this.on('destroy', this._destroyRadio);\n },\n\n _destroyRadio: function() {\n this._channel.stopReplying(null, null, this);\n },\n\n getChannel: function() {\n return this._channel;\n },\n\n // Proxy `bindRadioEvents`\n bindRadioEvents: bindRadioEvents,\n\n // Proxy `unbindRadioEvents`\n unbindRadioEvents: unbindRadioEvents,\n\n // Proxy `bindRadioRequests`\n bindRadioRequests: bindRadioRequests,\n\n // Proxy `unbindRadioRequests`\n unbindRadioRequests: unbindRadioRequests\n\n};\n","// Object\n// ------\n\nimport _ from 'underscore';\nimport Backbone from 'backbone';\nimport extend from './utils/extend';\nimport CommonMixin from './mixins/common';\nimport RadioMixin from './mixins/radio';\nimport { triggerMethod } from './trigger-method';\n\nconst ClassOptions = [\n 'channelName',\n 'radioEvents',\n 'radioRequests'\n];\n\n// A Base Class that other Classes should descend from.\n// Object borrows many conventions and utilities from Backbone.\nvar MarionetteObject = function(options) {\n this._setOptions(options);\n this.mergeOptions(options, ClassOptions);\n this.cid = _.uniqueId(this.cidPrefix);\n this._initRadio();\n this.initialize.apply(this, arguments);\n};\n\nMarionetteObject.extend = extend;\n\n// Object Methods\n// --------------\n\n// Ensure it can trigger events with Backbone.Events\n_.extend(MarionetteObject.prototype, Backbone.Events, CommonMixin, RadioMixin, {\n cidPrefix: 'mno',\n\n // for parity with Marionette.AbstractView lifecyle\n _isDestroyed: false,\n\n isDestroyed: function() {\n return this._isDestroyed;\n },\n\n //this is a noop method intended to be overridden by classes that extend from this base\n initialize: function() {},\n\n destroy: function(...args) {\n if (this._isDestroyed) { return this; }\n\n this.triggerMethod('before:destroy', this, ...args);\n\n this._isDestroyed = true;\n this.triggerMethod('destroy', this, ...args);\n this.stopListening();\n\n return this;\n },\n\n triggerMethod: triggerMethod\n});\n\nexport default MarionetteObject;\n","// Template Cache\n// --------------\n\nimport _ from 'underscore';\nimport Backbone from 'backbone';\nimport MarionetteError from './error';\n\n// Manage templates stored in `