From d5ad642f70e8dbc0292b66cb2d1612a8edbef4dd Mon Sep 17 00:00:00 2001 From: Paul Falgout Date: Tue, 3 Oct 2017 15:05:39 +0900 Subject: [PATCH] Bump and build v3.4.4 --- bower.json | 2 +- changelog.md | 11 ++++ lib/backbone.marionette.esm.js | 94 +++++++++++++++--------------- lib/backbone.marionette.esm.js.map | 2 +- lib/backbone.marionette.js | 94 +++++++++++++++--------------- lib/backbone.marionette.js.map | 2 +- lib/backbone.marionette.min.js | 4 +- lib/backbone.marionette.min.js.map | 2 +- package.json | 2 +- 9 files changed, 110 insertions(+), 103 deletions(-) diff --git a/bower.json b/bower.json index c19110454b..963672405b 100644 --- a/bower.json +++ b/bower.json @@ -3,7 +3,7 @@ "description": "The Backbone Framework", "homepage": "https://marionettejs.com/", "main": "./lib/backbone.marionette.js", - "version": "3.4.3", + "version": "3.4.4", "license": "MIT", "keywords": [ "backbone", diff --git a/changelog.md b/changelog.md index 191626245d..e448e4e118 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,14 @@ +### v3.4.4 [view commit logs](https://github.com/marionettejs/backbone.marionette/compare/v3.4.3...v3.4.4) + +#### Fixes +* Prevent exception when a view is instantiated with a non-existing selector `el`. +* When a collection defines the `NextCollectionView` sort order, the add at end performance improvement was removed to prevent edge case errors. +* `NextCollectionView` no longer sorts according to the collection if `sortWithCollection` is set to false. +* When views added to `NextCollectionView` from a collection don't have a matching model, removing the model no longer throws an error. + +#### Misc +* `NextCollectionView` now uses backbone update flags instead of calculating changes for sorting + ### v3.4.3 [view commit logs](https://github.com/marionettejs/backbone.marionette/compare/v3.4.2...v3.4.3) #### Fixes diff --git a/lib/backbone.marionette.esm.js b/lib/backbone.marionette.esm.js index cea6f186c7..e1e9fd42aa 100644 --- a/lib/backbone.marionette.esm.js +++ b/lib/backbone.marionette.esm.js @@ -2,7 +2,7 @@ * @license * MarionetteJS (Backbone.Marionette) * ---------------------------------- -* v3.4.3 +* v3.4.4 * * Copyright (c)2017 Derick Bailey, Muted Solutions, LLC. * Distributed under MIT license @@ -15,7 +15,7 @@ import Backbone from 'backbone'; import _ from 'underscore'; import Radio from 'backbone.radio'; -var version = "3.4.3"; +var version = "3.4.4"; //Internal utility for creating context style global utils var proxy = function proxy(method) { @@ -1127,7 +1127,7 @@ var DomApi = { // Does the el have child nodes hasContents: function hasContents(el) { - return el.hasChildNodes(); + return !!el && el.hasChildNodes(); }, @@ -2132,14 +2132,10 @@ var View = Backbone.View.extend({ // if an el was previously defined. If so, the view might be // rendered or attached on setElement. setElement: function setElement() { - var hasEl = !!this.el; - Backbone.View.prototype.setElement.apply(this, arguments); - if (hasEl) { - this._isRendered = this.Dom.hasContents(this.el); - this._isAttached = isNodeAttached(this.el); - } + this._isRendered = this.Dom.hasContents(this.el); + this._isAttached = isNodeAttached(this.el); if (this._isRendered) { this.bindUIElements(); @@ -2584,13 +2580,9 @@ var CollectionView = Backbone.View.extend({ // if an el was previously defined. If so, the view might be // attached on setElement. setElement: function setElement() { - var hasEl = !!this.el; - Backbone.View.prototype.setElement.apply(this, arguments); - if (hasEl) { - this._isAttached = isNodeAttached(this.el); - } + this._isAttached = isNodeAttached(this.el); return this; }, @@ -3419,24 +3411,17 @@ var CollectionView$2 = Backbone.View.extend({ // Internal method. This checks for any changes in the order of the collection. // If the index of any view doesn't match, it will re-sort. - _onCollectionSort: function _onCollectionSort() { - var _this = this; + _onCollectionSort: function _onCollectionSort(collection, _ref) { + var add = _ref.add, + merge = _ref.merge, + remove = _ref.remove; if (!this.sortWithCollection || this.viewComparator === false) { return; } - // If the data is changing we will handle the sort later - if (this.collection.length !== this.children.length) { - return; - } - - // Additional check if the data is changing - var hasAddedModel = this.collection.some(function (model) { - return !_this.children.findByModel(model); - }); - - if (hasAddedModel) { + // If the data is changing we will handle the sort later in `_onCollectionUpdate` + if (add || remove || merge) { return; } @@ -3465,12 +3450,24 @@ var CollectionView$2 = Backbone.View.extend({ this._removeChildViews(removedViews); }, _removeChildModels: function _removeChildModels(models) { - return _.map(models, _.bind(this._removeChildModel, this)); + var _this = this; + + return _.reduce(models, function (views, model) { + var removeView = _this._removeChildModel(model); + + if (removeView) { + views.push(removeView); + } + + return views; + }, []); }, _removeChildModel: function _removeChildModel(model) { var view = this.children.findByModel(model); - this._removeChild(view); + if (view) { + this._removeChild(view); + } return view; }, @@ -3586,13 +3583,9 @@ var CollectionView$2 = Backbone.View.extend({ // if an el was previously defined. If so, the view might be // attached on setElement. setElement: function setElement() { - var hasEl = !!this.el; - Backbone.View.prototype.setElement.apply(this, arguments); - if (hasEl) { - this._isAttached = isNodeAttached(this.el); - } + this._isAttached = isNodeAttached(this.el); return this; }, @@ -3708,14 +3701,14 @@ var CollectionView$2 = Backbone.View.extend({ // Sorts views by viewComparator and sets the children to the new order _sortChildren: function _sortChildren() { - if (this.viewComparator === false) { + var viewComparator = this.getComparator(); + + if (!viewComparator) { return; } this.triggerMethod('before:sort', this); - var viewComparator = this.getComparator(); - if (_.isFunction(viewComparator)) { // Must use native bind to preserve length viewComparator = viewComparator.bind(this); @@ -3730,8 +3723,8 @@ var CollectionView$2 = Backbone.View.extend({ // Sets the view's `viewComparator` and applies the sort if the view is ready. // To prevent the render pass `{ preventRender: true }` as the 2nd argument. setComparator: function setComparator(comparator) { - var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - preventRender = _ref.preventRender; + var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + preventRender = _ref2.preventRender; var comparatorChanged = this.viewComparator !== comparator; var shouldSort = comparatorChanged && !preventRender; @@ -3756,16 +3749,21 @@ var CollectionView$2 = Backbone.View.extend({ // Additionally override this function to provide custom // viewComparator logic getComparator: function getComparator() { - return this.viewComparator || this._viewComparator; + if (this.viewComparator) { + return this.viewComparator; + } + + if (!this.sortWithCollection || this.viewComparator === false || !this.collection) { + return false; + } + + return this._viewComparator; }, // Default internal view comparator that order the views by // the order of the collection _viewComparator: function _viewComparator(view) { - if (!this.collection) { - return; - } return this.collection.indexOf(view.model); }, @@ -3797,7 +3795,7 @@ var CollectionView$2 = Backbone.View.extend({ delete this._addedViews; if (!viewFilter) { - if (addedViews && _.every(addedViews, _.bind(this._isAddedAtEnd, this))) { + if (!this.sortWithCollection && addedViews && _.every(addedViews, _.bind(this._isAddedAtEnd, this))) { return addedViews; } @@ -3860,8 +3858,8 @@ var CollectionView$2 = Backbone.View.extend({ // Sets the view's `viewFilter` and applies the filter if the view is ready. // To prevent the render pass `{ preventRender: true }` as the 2nd argument. setFilter: function setFilter(filter) { - var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - preventRender = _ref2.preventRender; + var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + preventRender = _ref3.preventRender; var filterChanged = this.viewFilter !== filter; var shouldRender = filterChanged && !preventRender; @@ -4027,8 +4025,8 @@ var CollectionView$2 = Backbone.View.extend({ _.each(views, _.bind(this._removeChildView, this)); }, _removeChildView: function _removeChildView(view) { - var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - shouldDetach = _ref3.shouldDetach; + var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, + shouldDetach = _ref4.shouldDetach; view.off('destroy', this.removeChildView, this); diff --git a/lib/backbone.marionette.esm.js.map b/lib/backbone.marionette.esm.js.map index f9e16819a0..a5b74d1816 100644 --- a/lib/backbone.marionette.esm.js.map +++ b/lib/backbone.marionette.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"backbone.marionette.esm.js","sources":["src/utils/proxy.js","src/utils/extend.js","src/utils/deprecate.js","src/common/is-node-attached.js","src/common/merge-options.js","src/common/get-option.js","src/common/normalize-methods.js","src/common/trigger-method.js","src/common/monitor-view-events.js","src/error.js","src/common/bind-events.js","src/common/bind-requests.js","src/utils/set-options.js","src/mixins/common.js","src/mixins/radio.js","src/object.js","src/template-cache.js","src/utils/invoke.js","src/mixins/behaviors.js","src/mixins/delegate-entity-events.js","src/utils/get-unique-event-name.js","src/config/features.js","src/mixins/triggers.js","src/mixins/ui.js","src/config/dom.js","src/mixins/view.js","src/common/view.js","src/region.js","src/common/build-region.js","src/mixins/regions.js","src/config/renderer.js","src/view.js","src/utils/emulate-collection.js","src/child-view-container.js","src/collection-view.js","src/next-child-view-container.js","src/next-collection-view.js","src/composite-view.js","src/behavior.js","src/application.js","src/app-router.js","src/config/behaviors-lookup.js","src/backbone.marionette.js"],"sourcesContent":["//Internal utility for creating context style global utils\nconst 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\nconst extend = Backbone.Model.extend;\n\nexport default extend;\n","/* global console */\n\nimport _ from 'underscore';\n\nimport Marionette from '../backbone.marionette';\n\nconst 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\n/* istanbul ignore next: can't clear console */\ndeprecate._console = typeof console !== 'undefined' ? console : {};\ndeprecate._warn = function() {\n const warn = deprecate._console.warn || deprecate._console.log || _.noop;\n return warn.apply(deprecate._console, arguments);\n};\ndeprecate._cache = {};\n\nexport default deprecate;\n","// Marionette.isNodeAttached\n// -------------------------\n\n// Determine if `el` is a child of the document\nconst isNodeAttached = function(el) {\n return document.documentElement.contains(el && el.parentNode);\n};\n\nexport default isNodeAttached;\n","import _ from 'underscore';\n\n// Merge `keys` from `options` onto `this`\nconst mergeOptions = function(options, keys) {\n if (!options) { return; }\n\n _.each(keys, (key) => {\n const option = options[key];\n if (option !== undefined) {\n this[key] = option;\n }\n });\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.\nconst 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\nconst 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","// Trigger Method\n// --------------\n\nimport _ from 'underscore';\nimport getOption from './get-option';\n\n// split the event name on the \":\"\nconst 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\nconst getOnMethodName = _.memoize(function(event) {\n return 'on' + event.replace(splitter, getEventName);\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 const methodName = getOnMethodName(event);\n const method = getOption.call(this, methodName);\n let 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.apply(this, arguments);\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 if (_.isFunction(context.triggerMethod)) {\n return context.triggerMethod.apply(context, args);\n }\n\n return triggerMethod.apply(context, args);\n}\n","// DOM Refresh\n// -----------\n\nimport _ from 'underscore';\nimport { triggerMethodOn } from './trigger-method';\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\nfunction triggerDOMRefresh(view) {\n if (view._isAttached && view._isRendered) {\n triggerMethodOn(view, 'dom:refresh', view);\n }\n}\n\nfunction triggerDOMRemove(view) {\n if (view._isAttached && view._isRendered) {\n triggerMethodOn(view, 'dom:remove', view);\n }\n}\n\nfunction handleBeforeAttach() {\n triggerMethodChildren(this, 'before:attach', shouldTriggerAttach);\n}\n\nfunction handleAttach() {\n triggerMethodChildren(this, 'attach', shouldAttach);\n triggerDOMRefresh(this);\n}\n\nfunction handleBeforeDetach() {\n triggerMethodChildren(this, 'before:detach', shouldTriggerDetach);\n triggerDOMRemove(this);\n}\n\nfunction handleDetach() {\n triggerMethodChildren(this, 'detach', shouldDetach);\n}\n\nfunction handleBeforeRender() {\n triggerDOMRemove(this);\n}\n\nfunction handleRender() {\n triggerDOMRefresh(this);\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 || view.monitorViewEvents === false) { return; }\n\n view._areViewEventsMonitored = true;\n\n view.on({\n 'before:attach': handleBeforeAttach,\n 'attach': handleAttach,\n 'before:detach': handleBeforeDetach,\n 'detach': handleDetach,\n 'before:render': handleBeforeRender,\n 'render': handleRender\n });\n}\n\nexport default monitorViewEvents;\n","// Error\n// -----\n\nimport _ from 'underscore';\nimport extend from './utils/extend';\nimport {version} from '../package.json';\n\nconst errorProps = ['description', 'fileName', 'lineNumber', 'name', 'message', 'number'];\n\nconst MarionetteError = extend.call(Error, {\n urlRoot: `http://marionettejs.com/docs/v${version}/`,\n\n constructor(message, options) {\n if (_.isObject(message)) {\n options = message;\n message = options.message;\n } else if (!options) {\n options = {};\n }\n\n const 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() {\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, MarionetteError);\n }\n },\n\n toString() {\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 const methodNames = methods.split(/\\s+/);\n\n _.each(methodNames, function(methodName) {\n const 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#marionettebindevents'\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 bindEvents(entity, bindings) {\n iterateEvents(this, entity, bindings, 'listenTo');\n return this;\n}\n\nfunction unbindEvents(entity, bindings) {\n iterateEvents(this, entity, bindings, 'stopListening');\n return this;\n}\n\n// Export Public API\nexport {\n bindEvents,\n unbindEvents\n};\n","// 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 './normalize-methods';\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#marionettebindrequests'\n });\n }\n\n const normalizedRadioRequests = normalizeMethods.call(target, bindings);\n\n channel[actionName](normalizedRadioRequests, target);\n}\n\nfunction bindRequests(channel, bindings) {\n iterateReplies(this, channel, bindings, 'reply');\n return this;\n}\n\nfunction unbindRequests(channel, bindings) {\n iterateReplies(this, channel, bindings, 'stopReplying');\n return this;\n}\n\nexport {\n bindRequests,\n unbindRequests\n};\n","import _ from 'underscore';\n\n// Internal utility for setting options consistently across Mn\nconst setOptions = function(options) {\n this.options = _.extend({}, _.result(this, 'options'), options);\n};\n\nexport default setOptions;\n","import _setOptions from '../utils/set-options';\nimport getOption from '../common/get-option';\nimport mergeOptions from '../common/merge-options';\nimport normalizeMethods from '../common/normalize-methods';\nimport {\n bindEvents,\n unbindEvents\n} from '../common/bind-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,\n\n _setOptions,\n\n // A handy way to merge passed-in options onto the instance\n mergeOptions,\n\n // Enable getting options from this or this.options by name.\n getOption,\n\n // Enable binding view's events from another entity.\n bindEvents,\n\n // Enable unbinding view's events from another entity.\n unbindEvents\n};\n","import _ from 'underscore';\nimport Radio from 'backbone.radio';\n\nimport {\n bindRequests,\n unbindRequests\n} from '../common/bind-requests';\n\nimport {\n bindEvents,\n unbindEvents\n} from '../common/bind-events';\n\nimport MarionetteError from '../error';\n\n// MixinOptions\n// - channelName\n// - radioEvents\n// - radioRequests\n\nexport default {\n\n _initRadio() {\n const channelName = _.result(this, 'channelName');\n\n if (!channelName) {\n return;\n }\n\n /* istanbul ignore next */\n if (!Radio) {\n throw new MarionetteError({\n name: 'BackboneRadioMissing',\n message: 'The dependency \"backbone.radio\" is missing.'\n });\n }\n\n const channel = this._channel = Radio.channel(channelName);\n\n const radioEvents = _.result(this, 'radioEvents');\n this.bindEvents(channel, radioEvents);\n\n const radioRequests = _.result(this, 'radioRequests');\n this.bindRequests(channel, radioRequests);\n\n this.on('destroy', this._destroyRadio);\n },\n\n _destroyRadio() {\n this._channel.stopReplying(null, null, this);\n },\n\n getChannel() {\n return this._channel;\n },\n\n // Proxy `bindEvents`\n bindEvents: bindEvents,\n\n // Proxy `unbindEvents`\n unbindEvents: unbindEvents,\n\n // Proxy `bindRequests`\n bindRequests: bindRequests,\n\n // Proxy `unbindRequests`\n unbindRequests: unbindRequests\n\n};\n","// Object\n// ------\n\nimport _ from 'underscore';\nimport Backbone from 'backbone';\nimport extend from './utils/extend';\nimport { triggerMethod } from './common/trigger-method';\nimport CommonMixin from './mixins/common';\nimport RadioMixin from './mixins/radio';\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.\nconst MarionetteObject = function(options) {\n if (!this.hasOwnProperty('options')) {\n this._setOptions(options);\n }\n this.mergeOptions(options, ClassOptions);\n this._setCid();\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() {\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() {},\n\n _setCid() {\n if (this.cid) { return; }\n this.cid = _.uniqueId(this.cidPrefix);\n },\n\n destroy(...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\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 `