marionette.toolkit
Advanced tools
Comparing version 4.0.0 to 5.0.0-alpha.1
/** | ||
* marionette.toolkit - A collection of opinionated Backbone.Marionette extensions for large scale application architecture. | ||
* @version v4.0.0 | ||
* @version v5.0.0-alpha.1 | ||
* @link https://github.com/RoundingWellOS/marionette.toolkit | ||
@@ -9,9 +9,8 @@ * @license MIT | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('underscore'), require('backbone.marionette'), require('backbone')) : | ||
typeof define === 'function' && define.amd ? define(['underscore', 'backbone.marionette', 'backbone'], factory) : | ||
(global.Marionette = global.Marionette || {}, global.Marionette.Toolkit = factory(global._,global.Marionette,global.Backbone)); | ||
}(this, function (_,Marionette,Backbone) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('underscore'), require('backbone'), require('backbone.marionette')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'underscore', 'backbone', 'backbone.marionette'], factory) : | ||
(factory((global.Marionette = global.Marionette || {}, global.Marionette.Toolkit = global.Marionette.Toolkit || {}),global._,global.Backbone,global.Marionette)); | ||
}(this, function (exports,_,Backbone,backbone_marionette) { 'use strict'; | ||
_ = 'default' in _ ? _['default'] : _; | ||
Marionette = 'default' in Marionette ? Marionette['default'] : Marionette; | ||
Backbone = 'default' in Backbone ? Backbone['default'] : Backbone; | ||
@@ -23,3 +22,3 @@ | ||
* This provides methods used for keeping state using a Backbone.Model. It's meant to | ||
* be used with either a Marionette.Object or Backbone.View. | ||
* be used with either a Marionette.MnObject or Backbone.View. | ||
* | ||
@@ -144,6 +143,3 @@ * @mixin | ||
throw new Marionette.Error({ | ||
name: 'InvalidStateModelError', | ||
message: '"StateModel" must be a model class or a function that returns a model class' | ||
}); | ||
throw new Error('"StateModel" must be a model class or a function that returns a model class'); | ||
}, | ||
@@ -200,2 +196,31 @@ | ||
/** | ||
* Toggle a property on the _stateModel. | ||
* | ||
* @public | ||
* @method toggleState | ||
* @param {String} attr - Attribute name of stateModel. | ||
* @param {val} [value] - Attribute value. | ||
* @returns {Backbone.Model} - The _stateModel or attribute value. | ||
*/ | ||
toggleState: function toggleState(attr, val) { | ||
if (arguments.length > 1) return this._stateModel.set(attr, !!val); | ||
return this._stateModel.set(attr, !this._stateModel.get(attr)); | ||
}, | ||
/** | ||
* Check if _stateModel has a property | ||
* | ||
* @public | ||
* @method hasState | ||
* @param {String} [attr] - Attribute name of stateModel. | ||
* @returns {Boolean} | ||
*/ | ||
hasState: function hasState(attr) { | ||
return this._stateModel.has(attr); | ||
}, | ||
/** | ||
* Clean up any listeners on the _stateModel. | ||
@@ -256,20 +281,17 @@ * | ||
} | ||
this._initListeners(); | ||
}, | ||
_getChildStartOpts: function _getChildStartOpts(childApp) { | ||
var _this = this; | ||
var tkOpts = childApp._tkOpts || {}; | ||
/** | ||
* The child apps should be handled while the app is running; | ||
* After start, before stop, and before destroy. | ||
* | ||
* @private | ||
* @method _initListeners | ||
*/ | ||
_initListeners: function _initListeners() { | ||
this.on({ | ||
'start': this._startChildApps, | ||
'before:stop': this._stopChildApps, | ||
'before:destroy': this._destroyChildApps | ||
var opts = { | ||
region: this.getRegion(tkOpts.regionName) | ||
}; | ||
_.each(tkOpts.getOptions, function (opt) { | ||
opts[opt] = _this.getOption(opt); | ||
}); | ||
return opts; | ||
}, | ||
@@ -285,6 +307,10 @@ | ||
_startChildApps: function _startChildApps() { | ||
var _this2 = this; | ||
var shouldStartOption = this._isRestarting ? 'restartWithParent' : 'startWithParent'; | ||
_.each(this._childApps, function (childApp) { | ||
if (_.result(childApp, 'startWithParent')) { | ||
childApp.start(); | ||
} | ||
if (!_.result(childApp, shouldStartOption)) return; | ||
var opts = _this2._getChildStartOpts(childApp); | ||
childApp.start(opts); | ||
}); | ||
@@ -301,4 +327,5 @@ }, | ||
_stopChildApps: function _stopChildApps() { | ||
var shouldStopOption = this._isRestarting ? 'restartWithParent' : 'stopWithParent'; | ||
_.each(this._childApps, function (childApp) { | ||
if (_.result(childApp, 'stopWithParent')) { | ||
if (_.result(childApp, shouldStopOption)) { | ||
childApp.stop(); | ||
@@ -319,3 +346,5 @@ } | ||
startChildApp: function startChildApp(appName, options) { | ||
return this.getChildApp(appName).start(options); | ||
var childApp = this.getChildApp(appName); | ||
var opts = this._getChildStartOpts(childApp); | ||
return childApp.start(_.extend(opts, options)); | ||
}, | ||
@@ -328,7 +357,8 @@ | ||
* @param {String} appName - Name of childApp to stop | ||
* @param {Object} options - Stop options for app | ||
* @public | ||
* @method stopChildApp | ||
*/ | ||
stopChildApp: function stopChildApp(appName) { | ||
return this.getChildApp(appName).stop(); | ||
stopChildApp: function stopChildApp(appName, options) { | ||
return this.getChildApp(appName).stop(options); | ||
}, | ||
@@ -362,5 +392,9 @@ | ||
var AppClass = appConfig.AppClass; | ||
var options = _.omit(appConfig, 'AppClass'); | ||
var options = _.omit(appConfig, 'AppClass', 'regionName', 'getOptions'); | ||
return this.buildApp(AppClass, options); | ||
var app = this.buildApp(AppClass, options); | ||
app._tkOpts = _.pick(appConfig, 'regionName', 'getOptions'); | ||
return app; | ||
}, | ||
@@ -417,6 +451,3 @@ | ||
if (this._childApps[appName]) { | ||
throw new Marionette.Error({ | ||
name: 'DuplicateChildAppError', | ||
message: 'A child App with name "' + appName + '" has already been added.' | ||
}); | ||
throw new Error('A child App with name "' + appName + '" has already been added.'); | ||
} | ||
@@ -459,6 +490,3 @@ }, | ||
if (!childApp) { | ||
throw new Marionette.Error({ | ||
name: 'AddChildAppError', | ||
message: 'App build failed. Incorrect configuration.' | ||
}); | ||
throw new Error('App build failed. Incorrect configuration.'); | ||
} | ||
@@ -471,3 +499,4 @@ | ||
// When the app is destroyed remove the cached app. | ||
childApp.on('destroy', _.partial(this._removeChildApp, appName), this); | ||
// Listener setup relative to the childApp's running state (using _on) | ||
childApp._on('destroy', _.partial(this._removeChildApp, appName), this); | ||
@@ -630,3 +659,3 @@ if (this.isRunning() && _.result(childApp, 'startWithParent')) { | ||
return Marionette.Object.prototype.on.apply(this, arguments); | ||
return backbone_marionette.MnObject.prototype.on.apply(this, arguments); | ||
}, | ||
@@ -636,2 +665,11 @@ | ||
/** | ||
* Keep a copy of non-running on for internal use | ||
* | ||
* @private | ||
* @method _on | ||
* @returns {EventListeners} | ||
*/ | ||
_on: backbone_marionette.MnObject.prototype.on, | ||
/** | ||
* Overrides `Backbone.Event.listenTo()` | ||
@@ -649,3 +687,3 @@ * If this `App` is running it will register the listener for removal `onStop` | ||
} | ||
return Marionette.Object.prototype.listenTo.apply(this, arguments); | ||
return backbone_marionette.MnObject.prototype.listenTo.apply(this, arguments); | ||
}, | ||
@@ -655,2 +693,11 @@ | ||
/** | ||
* Keep a copy of non-running on for internal use | ||
* | ||
* @private | ||
* @method _listenTo | ||
* @returns {EventListeners} | ||
*/ | ||
_listenTo: backbone_marionette.MnObject.prototype.listenTo, | ||
/** | ||
* Overrides `Backbone.Event.listenToOnce()` | ||
@@ -669,3 +716,3 @@ * If this `App` is running it will register the listener for removal `onStop` | ||
return Marionette.Object.prototype.listenToOnce.apply(this, arguments); | ||
return backbone_marionette.MnObject.prototype.listenToOnce.apply(this, arguments); | ||
} | ||
@@ -750,3 +797,3 @@ }; | ||
var ClassOptions$1 = ['startWithParent', 'stopWithParent', 'startAfterInitialized', 'preventDestroy', 'StateModel', 'stateEvents', 'viewEventPrefix', 'viewEvents', 'viewTriggers']; | ||
var ClassOptions$1 = ['startWithParent', 'restartWithParent', 'stopWithParent', 'startAfterInitialized', 'preventDestroy', 'StateModel', 'stateEvents', 'viewEventPrefix', 'viewEvents', 'viewTriggers']; | ||
@@ -761,3 +808,3 @@ /** | ||
*/ | ||
var App = Marionette.Application.extend({ | ||
var App = backbone_marionette.Application.extend({ | ||
@@ -774,2 +821,11 @@ /** | ||
/** | ||
* Internal flag indiciate when `App` is in the process of stopping then starting. | ||
* | ||
* @private | ||
* @type {Boolean} | ||
* @default false | ||
*/ | ||
_isRestarting: false, | ||
/** | ||
* Set to true if a parent `App` should not be able to destroy this `App`. | ||
@@ -807,2 +863,10 @@ * | ||
/** | ||
* Set to true if a parent `App` should be able to restart this `App`. | ||
* | ||
* @type {Boolean|Function} | ||
* @default false | ||
*/ | ||
restartWithParent: false, | ||
/** | ||
* @public | ||
@@ -812,2 +876,3 @@ * @constructs App | ||
* @param {Boolean} [options.startWithParent] | ||
* @param {Boolean} [options.restartWithParent] | ||
* @param {Boolean} [options.stopWithParent] | ||
@@ -825,9 +890,6 @@ * @param {Boolean} [options.startAfterInitialized] | ||
// ViewEventMixin | ||
this._buildEventProxies(); | ||
// ChildAppsMixin | ||
this._initChildApps(options); | ||
Marionette.Application.call(this, options); | ||
backbone_marionette.Application.call(this, options); | ||
@@ -841,16 +903,2 @@ if (_.result(this, 'startAfterInitialized')) { | ||
/** | ||
* Override of Marionette's Application._initRegion | ||
* Allows region monitor to be setup prior to initialize | ||
* | ||
* @private | ||
* @method _initRegion | ||
* @memberOf App | ||
*/ | ||
_initRegion: function _initRegion() { | ||
Marionette.Application.prototype._initRegion.call(this); | ||
this._regionEventMonitor(); | ||
}, | ||
/** | ||
* Internal helper to verify if `App` has been destroyed | ||
@@ -865,6 +913,3 @@ * | ||
if (this._isDestroyed) { | ||
throw new Marionette.Error({ | ||
name: 'AppDestroyedError', | ||
message: 'App has already been destroyed and cannot be used.' | ||
}); | ||
throw new Error('App has already been destroyed and cannot be used.'); | ||
} | ||
@@ -930,2 +975,5 @@ }, | ||
// ViewEventMixin | ||
this._buildEventProxies(); | ||
this.triggerMethod('before:start', options); | ||
@@ -935,5 +983,6 @@ | ||
// StateMixin | ||
this.delegateStateEvents(); | ||
this._bindRunningEvents(); | ||
this._startChildApps(); | ||
this.triggerStart(options); | ||
@@ -946,2 +995,24 @@ | ||
/** | ||
* Sets up region, view, and state events. | ||
* To only be called after `isRunning` is true | ||
* | ||
* @private | ||
* @method _bindRunningEvents | ||
* @memberOf App | ||
*/ | ||
_bindRunningEvents: function _bindRunningEvents() { | ||
if (this._region) { | ||
this._regionEventMonitor(); | ||
} | ||
if (this._view) { | ||
this._proxyViewEvents(this._view); | ||
} | ||
// StateMixin | ||
this.delegateStateEvents(); | ||
}, | ||
/** | ||
* Sets the app lifecycle to not running | ||
@@ -1001,2 +1072,4 @@ * then sets the app lifecycle to running with ending state | ||
this._stopChildApps(); | ||
this._isRunning = false; | ||
@@ -1029,6 +1102,8 @@ | ||
delete this._view; | ||
this._removeView(); | ||
Marionette.Application.prototype.destroy.apply(this, arguments); | ||
this._destroyChildApps(); | ||
backbone_marionette.Application.prototype.destroy.apply(this, arguments); | ||
return this; | ||
@@ -1054,4 +1129,10 @@ }, | ||
this._regionEventMonitor(); | ||
if (region.currentView) { | ||
this.setView(region.currentView); | ||
} | ||
if (this._isRunning) { | ||
this._regionEventMonitor(); | ||
} | ||
return region; | ||
@@ -1070,3 +1151,6 @@ }, | ||
_regionEventMonitor: function _regionEventMonitor() { | ||
this.listenTo(this._region, 'before:show', this._onBeforeShow); | ||
this.listenTo(this._region, { | ||
'before:show': this._onBeforeShow, | ||
'empty': this._onEmpty | ||
}); | ||
}, | ||
@@ -1088,2 +1172,29 @@ | ||
/** | ||
* Region monitor handler which empties the region's view | ||
* | ||
* @private | ||
* @method _onEmpty | ||
* @memberOf App | ||
*/ | ||
_onEmpty: function _onEmpty() { | ||
this._removeView(); | ||
}, | ||
/** | ||
* Region monitor handler which deletes the region's view and listeners to view | ||
* | ||
* @private | ||
* @method _removeView | ||
* @memberOf App | ||
*/ | ||
_removeView: function _removeView() { | ||
if (this._view) { | ||
this.stopListening(this._view); | ||
delete this._view; | ||
} | ||
}, | ||
/** | ||
* Get the Application's Region or | ||
@@ -1128,4 +1239,9 @@ * Get a region from the Application's View | ||
// ViewEventsMixin | ||
this._proxyViewEvents(view); | ||
if (this._isRunning) { | ||
this._proxyViewEvents(view); | ||
} | ||
// Internal non-running listener | ||
this._listenTo(this._view, 'destroy', this._removeView); | ||
return view; | ||
@@ -1144,3 +1260,3 @@ }, | ||
getView: function getView() { | ||
return this._view; | ||
return this._view || this._region && this._region.currentView; | ||
}, | ||
@@ -1214,3 +1330,3 @@ | ||
/** | ||
* Reusable Marionette.Object with View management boilerplate | ||
* Reusable Marionette.MnObject with View management boilerplate | ||
* | ||
@@ -1222,3 +1338,3 @@ * @public | ||
*/ | ||
var Component = Marionette.Object.extend({ | ||
var Component = backbone_marionette.MnObject.extend({ | ||
@@ -1230,3 +1346,3 @@ /** | ||
*/ | ||
ViewClass: Marionette.View, | ||
ViewClass: backbone_marionette.View, | ||
@@ -1259,3 +1375,3 @@ /** | ||
Marionette.Object.call(this, options); | ||
backbone_marionette.MnObject.call(this, options); | ||
@@ -1313,13 +1429,7 @@ // StateMixin | ||
if (this._isShown) { | ||
throw new Marionette.Error({ | ||
name: 'ComponentShowError', | ||
message: 'Component has already been shown in a region.' | ||
}); | ||
throw new Error('Component has already been shown in a region.'); | ||
} | ||
if (!region) { | ||
throw new Marionette.Error({ | ||
name: 'ComponentRegionError', | ||
message: 'Component has no defined region.' | ||
}); | ||
throw new Error('Component has no defined region.'); | ||
} | ||
@@ -1377,6 +1487,3 @@ | ||
throw new Marionette.Error({ | ||
name: 'InvalidViewClassError', | ||
message: '"ViewClass" must be a view class or a function that returns a view class' | ||
}); | ||
throw new Error('"ViewClass" must be a view class or a function that returns a view class'); | ||
}, | ||
@@ -1415,4 +1522,3 @@ | ||
// Show the view in the region | ||
this.getRegion().show(view); | ||
this.showView(view); | ||
@@ -1428,2 +1534,16 @@ this._shouldDestroy = true; | ||
/** | ||
* Override this to change how the component's view is shown in the region | ||
* | ||
* @public | ||
* @method showView | ||
* @memberOf Component | ||
* @param {Object} view - view built from a viewClass and viewOptions | ||
*/ | ||
showView: function showView(view) { | ||
// Show the view in the region | ||
this.getRegion().show(view); | ||
}, | ||
/** | ||
* Mixin initial State with any other viewOptions | ||
@@ -1472,3 +1592,3 @@ * | ||
if (this._shouldDestroy) { | ||
Marionette.Object.prototype.destroy.apply(this, arguments); | ||
backbone_marionette.MnObject.prototype.destroy.apply(this, arguments); | ||
} | ||
@@ -1521,12 +1641,5 @@ }, | ||
var previousToolkit = Marionette.Toolkit; | ||
var VERSION = '5.0.0-alpha.1'; | ||
var Toolkit = Marionette.Toolkit = {}; | ||
Toolkit.noConflict = function () { | ||
Marionette.Toolkit = previousToolkit; | ||
return this; | ||
}; | ||
Toolkit.MixinState = function (classDefinition) { | ||
function MixinState(classDefinition) { | ||
var _StateMixin = StateMixin; | ||
@@ -1539,16 +1652,21 @@ | ||
_.extend(classDefinition.prototype, _StateMixin); | ||
} | ||
var marionette_toolkit = { | ||
MixinState: MixinState, | ||
VERSION: VERSION, | ||
StateMixin: StateMixin, | ||
App: App, | ||
Component: Component | ||
}; | ||
Toolkit.VERSION = '4.0.0'; | ||
exports.MixinState = MixinState; | ||
exports.VERSION = VERSION; | ||
exports.StateMixin = StateMixin; | ||
exports.App = App; | ||
exports.Component = Component; | ||
exports['default'] = marionette_toolkit; | ||
Toolkit.StateMixin = StateMixin; | ||
Toolkit.App = App; | ||
Toolkit.Component = Component; | ||
return Toolkit; | ||
})); | ||
//# sourceMappingURL=marionette.toolkit.js.map |
/** | ||
* marionette.toolkit - A collection of opinionated Backbone.Marionette extensions for large scale application architecture. | ||
* @version v4.0.0 | ||
* @version v5.0.0-alpha.1 | ||
* @link https://github.com/RoundingWellOS/marionette.toolkit | ||
* @license MIT | ||
*/ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("underscore"),require("backbone.marionette"),require("backbone")):"function"==typeof define&&define.amd?define(["underscore","backbone.marionette","backbone"],e):(t.Marionette=t.Marionette||{},t.Marionette.Toolkit=e(t._,t.Marionette,t.Backbone))}(this,function(t,e,i){"use strict";t="default"in t?t.default:t,e="default"in e?e.default:e,i="default"in i?i.default:i;var n=["StateModel","stateEvents"],s={StateModel:i.Model,initState:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this._initState(t),this.delegateStateEvents(),this},_initState:function(t){this.mergeOptions(t,n),this._removeEventHandlers();var e=this._getStateModel(t);this._stateModel=new e(t.state),this._setEventHandlers()},delegateStateEvents:function(){return this.undelegateStateEvents(),this.bindEvents(this._stateModel,t.result(this,"stateEvents")),this},undelegateStateEvents:function(){return this.unbindEvents(this._stateModel),this},_setEventHandlers:function(){this.on("destroy",this._destroyState)},_removeEventHandlers:function(){this._stateModel&&(this.undelegateStateEvents(),this._stateModel.stopListening(),this.off("destroy",this._destroyState))},_getStateModel:function(n){if(this.StateModel.prototype instanceof i.Model||this.StateModel===i.Model)return this.StateModel;if(t.isFunction(this.StateModel))return this.StateModel.call(this,n);throw new e.Error({name:"InvalidStateModelError",message:'"StateModel" must be a model class or a function that returns a model class'})},setState:function(){return this._stateModel.set.apply(this._stateModel,arguments)},resetStateDefaults:function(){var e=t.result(this._stateModel,"defaults");return this._stateModel.set(e)},getState:function(t){return t?this._stateModel.get.apply(this._stateModel,arguments):this._stateModel},_destroyState:function(){this._stateModel.stopListening()}},r=["childApps","childAppOptions"],o={_initChildApps:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this._childApps={},this.mergeOptions(e,r);var i=this.childApps;i&&(t.isFunction(i)&&(i=i.call(this,e)),this.addChildApps(i)),this._initListeners()},_initListeners:function(){this.on({start:this._startChildApps,"before:stop":this._stopChildApps,"before:destroy":this._destroyChildApps})},_startChildApps:function(){t.each(this._childApps,function(e){t.result(e,"startWithParent")&&e.start()})},_stopChildApps:function(){t.each(this._childApps,function(e){t.result(e,"stopWithParent")&&e.stop()})},startChildApp:function(t,e){return this.getChildApp(t).start(e)},stopChildApp:function(t){return this.getChildApp(t).stop()},_destroyChildApps:function(){t.each(this._childApps,function(e){t.result(e,"preventDestroy")||e.destroy()})},_buildAppFromObject:function(e){var i=e.AppClass,n=t.omit(e,"AppClass");return this.buildApp(i,n)},_buildApp:function(e,i){return t.isFunction(e)?this.buildApp(e,i):t.isObject(e)?this._buildAppFromObject(e):void 0},buildApp:function(e,i){return i=t.extend({},this.childAppOptions,i),new e(i)},_ensureAppIsUnique:function(t){if(this._childApps[t])throw new e.Error({name:"DuplicateChildAppError",message:'A child App with name "'+t+'" has already been added.'})},addChildApps:function(e){t.each(e,t.bind(function(t,e){this.addChildApp(e,t)},this))},addChildApp:function(i,n,s){this._ensureAppIsUnique(i);var r=this._buildApp(n,s);if(!r)throw new e.Error({name:"AddChildAppError",message:"App build failed. Incorrect configuration."});return r._name=i,this._childApps[i]=r,r.on("destroy",t.partial(this._removeChildApp,i),this),this.isRunning()&&t.result(r,"startWithParent")&&r.start(),r},getName:function(){return this._name},getChildApps:function(){return t.clone(this._childApps)},getChildApp:function(t){return this._childApps[t]},_removeChildApp:function(t){delete this._childApps[t]._name,delete this._childApps[t]},removeChildApps:function(){var e=this.getChildApps();return t.each(this._childApps,t.bind(function(t,e){this.removeChildApp(e)},this)),e},removeChildApp:function(e,i){i=t.extend({},i);var n=this.getChildApp(e);if(n)return i.preventDestroy||t.result(n,"preventDestroy")?this._removeChildApp(e):n.destroy(),n}},h={_stopRunningEvents:function(){t.each(this._runningEvents,t.bind(function(t){this.off.apply(this,t)},this))},_stopRunningListeners:function(){t.each(this._runningListeningTo,t.bind(function(t){this.stopListening.apply(this,t)},this))},on:function(){return this._isRunning&&(this._runningEvents=this._runningEvents||[],this._runningEvents.push(arguments)),e.Object.prototype.on.apply(this,arguments)},listenTo:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),e.Object.prototype.listenTo.apply(this,arguments)},listenToOnce:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),e.Object.prototype.listenToOnce.apply(this,arguments)}},a={viewEventPrefix:!1,_buildEventProxies:function(){var e=t.result(this,"viewEvents")||{};this._viewEvents=this.normalizeMethods(e),this._viewTriggers=t.result(this,"viewTriggers")||{},this._viewEventPrefix=t.result(this,"viewEventPrefix")},_proxyViewEvents:function(t){this.listenTo(t,"all",this._childViewEventHandler)},_childViewEventHandler:function(e){for(var i=this._viewEvents,n=arguments.length,s=Array(n>1?n-1:0),r=1;r<n;r++)s[r-1]=arguments[r];t.isFunction(i[e])&&i[e].apply(this,s);var o=this._viewTriggers;t.isString(o[e])&&this.triggerMethod.apply(this,[o[e]].concat(s));var h=this._viewEventPrefix;if(h!==!1){var a=h+":"+e;this.triggerMethod.apply(this,[a].concat(s))}}},p=["startWithParent","stopWithParent","startAfterInitialized","preventDestroy","StateModel","stateEvents","viewEventPrefix","viewEvents","viewTriggers"],u=e.Application.extend({_isRunning:!1,preventDestroy:!1,startAfterInitialized:!1,startWithParent:!1,stopWithParent:!0,constructor:function(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.mergeOptions(i,p),this.options=t.extend({},t.result(this,"options"),i),this._buildEventProxies(),this._initChildApps(i),e.Application.call(this,i),t.result(this,"startAfterInitialized")&&this.start(i)},_initRegion:function(){e.Application.prototype._initRegion.call(this),this._regionEventMonitor()},_ensureAppIsIntact:function(){if(this._isDestroyed)throw new e.Error({name:"AppDestroyedError",message:"App has already been destroyed and cannot be used."})},isRunning:function(){return this._isRunning},isRestarting:function(){return this._isRestarting},start:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this._ensureAppIsIntact(),this._isRunning?this:(t.region&&this.setRegion(t.region),t.view&&this.setView(t.view),this._initState(t),this.triggerMethod("before:start",t),this._isRunning=!0,this.delegateStateEvents(),this.triggerStart(t),this)},restart:function(){var t=this.getState().attributes;return this._isRestarting=!0,this.stop().start({state:t}),this._isRestarting=!1,this},triggerStart:function(t){this.triggerMethod("start",t)},stop:function(t){return this._isRunning?(this.triggerMethod("before:stop",t),this._isRunning=!1,this.triggerMethod("stop",t),this._stopRunningListeners(),this._stopRunningEvents(),this):this},destroy:function(){return this._isDestroyed?this:(this.stop(),delete this._view,e.Application.prototype.destroy.apply(this,arguments),this)},setRegion:function(t){return this._region&&this.stopListening(this._region),this._region=t,this._regionEventMonitor(),t},_regionEventMonitor:function(){this.listenTo(this._region,"before:show",this._onBeforeShow)},_onBeforeShow:function(t,e){this.setView(e)},getRegion:function(t){return t?this.getView().getRegion(t):this._region},setView:function(t){return this._view===t?t:(this._view&&this.stopListening(this._view),this._view=t,this._proxyViewEvents(t),t)},getView:function(){return this._view},showView:function(){for(var t,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this._view,i=arguments.length,n=Array(i>1?i-1:0),s=1;s<i;s++)n[s-1]=arguments[s];return(t=this.getRegion()).show.apply(t,[e].concat(n)),e},showChildView:function(t,e){for(var i,n=arguments.length,s=Array(n>2?n-2:0),r=2;r<n;r++)s[r-2]=arguments[r];return(i=this.getView()).showChildView.apply(i,[t,e].concat(s)),e},getChildView:function(t){return this.getView().getChildView(t)}});t.extend(u.prototype,s,o,h,a);var d=["ViewClass","viewEventPrefix","viewEvents","viewTriggers","viewOptions","region"],l=e.Object.extend({ViewClass:e.View,constructor:function(){var i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.mergeOptions(i,d),this.options=t.extend({},t.result(this,"options"),i),this._buildEventProxies(),this._initState(i),e.Object.call(this,i),this.delegateStateEvents()},_shouldDestroy:!0,showIn:function(t,e){return this.region=t,this.show(e),this},show:function(t){var i=this.getRegion();if(this._isShown)throw new e.Error({name:"ComponentShowError",message:"Component has already been shown in a region."});if(!i)throw new e.Error({name:"ComponentRegionError",message:"Component has no defined region."});return this.triggerMethod("before:show"),this.renderView(t),this._isShown=!0,this.triggerMethod("show"),this.listenTo(i,"empty",this._destroy),this},getRegion:function(){return this.region},_getViewClass:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},s=this.ViewClass;if(s.prototype instanceof i.View||s===i.View)return s;if(t.isFunction(s))return s.call(this,n);throw new e.Error({name:"InvalidViewClassError",message:'"ViewClass" must be a view class or a function that returns a view class'})},renderView:function(t){var e=this._getViewClass(t),i=this.mixinOptions(t),n=this.buildView(e,i);return this.currentView=n,this._proxyViewEvents(n),this.triggerMethod("before:render:view",n),this._shouldDestroy=!1,this.getRegion().show(n),this._shouldDestroy=!0,this.triggerMethod("render:view",n),this},mixinOptions:function(e){var i=t.result(this,"viewOptions");return t.extend({state:this.getState().attributes},i,e)},buildView:function(t,e){return new t(e)},_destroy:function(){this._shouldDestroy&&e.Object.prototype.destroy.apply(this,arguments)},_emptyRegion:function(t){var e=this.getRegion();e&&(this.stopListening(e,"empty"),e.empty(t))},destroy:function(t){return this._emptyRegion(t),this._shouldDestroy=!0,this._destroy(t),this}});t.extend(l.prototype,s,a);var c=e.Toolkit,g=e.Toolkit={};return g.noConflict=function(){return e.Toolkit=c,this},g.MixinState=function(e){var i=s;e.prototype.StateModel&&(i=t.omit(s,"StateModel")),t.extend(e.prototype,i)},g.VERSION="4.0.0",g.StateMixin=s,g.App=u,g.Component=l,g}); | ||
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports,require("underscore"),require("backbone"),require("backbone.marionette")):"function"==typeof define&&define.amd?define(["exports","underscore","backbone","backbone.marionette"],i):i((t.Marionette=t.Marionette||{},t.Marionette.Toolkit=t.Marionette.Toolkit||{}),t._,t.Backbone,t.Marionette)}(this,function(t,i,e,n){"use strict";function s(t){var e=o;t.prototype.StateModel&&(e=i.omit(o,"StateModel")),i.extend(t.prototype,e)}i="default"in i?i.default:i,e="default"in e?e.default:e;var r=["StateModel","stateEvents"],o={StateModel:e.Model,initState:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this._initState(t),this.delegateStateEvents(),this},_initState:function(t){this.mergeOptions(t,r),this._removeEventHandlers();var i=this._getStateModel(t);this._stateModel=new i(t.state),this._setEventHandlers()},delegateStateEvents:function(){return this.undelegateStateEvents(),this.bindEvents(this._stateModel,i.result(this,"stateEvents")),this},undelegateStateEvents:function(){return this.unbindEvents(this._stateModel),this},_setEventHandlers:function(){this.on("destroy",this._destroyState)},_removeEventHandlers:function(){this._stateModel&&(this.undelegateStateEvents(),this._stateModel.stopListening(),this.off("destroy",this._destroyState))},_getStateModel:function(t){if(this.StateModel.prototype instanceof e.Model||this.StateModel===e.Model)return this.StateModel;if(i.isFunction(this.StateModel))return this.StateModel.call(this,t);throw new Error('"StateModel" must be a model class or a function that returns a model class')},setState:function(){return this._stateModel.set.apply(this._stateModel,arguments)},resetStateDefaults:function(){var t=i.result(this._stateModel,"defaults");return this._stateModel.set(t)},getState:function(t){return t?this._stateModel.get.apply(this._stateModel,arguments):this._stateModel},toggleState:function(t,i){return arguments.length>1?this._stateModel.set(t,!!i):this._stateModel.set(t,!this._stateModel.get(t))},hasState:function(t){return this._stateModel.has(t)},_destroyState:function(){this._stateModel.stopListening()}},h=["childApps","childAppOptions"],a={_initChildApps:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this._childApps={},this.mergeOptions(t,h);var e=this.childApps;e&&(i.isFunction(e)&&(e=e.call(this,t)),this.addChildApps(e))},_getChildStartOpts:function(t){var e=this,n=t._tkOpts||{},s={region:this.getRegion(n.regionName)};return i.each(n.getOptions,function(t){s[t]=e.getOption(t)}),s},_startChildApps:function(){var t=this,e=this._isRestarting?"restartWithParent":"startWithParent";i.each(this._childApps,function(n){if(i.result(n,e)){var s=t._getChildStartOpts(n);n.start(s)}})},_stopChildApps:function(){var t=this._isRestarting?"restartWithParent":"stopWithParent";i.each(this._childApps,function(e){i.result(e,t)&&e.stop()})},startChildApp:function(t,e){var n=this.getChildApp(t),s=this._getChildStartOpts(n);return n.start(i.extend(s,e))},stopChildApp:function(t,i){return this.getChildApp(t).stop(i)},_destroyChildApps:function(){i.each(this._childApps,function(t){i.result(t,"preventDestroy")||t.destroy()})},_buildAppFromObject:function(t){var e=t.AppClass,n=i.omit(t,"AppClass","regionName","getOptions"),s=this.buildApp(e,n);return s._tkOpts=i.pick(t,"regionName","getOptions"),s},_buildApp:function(t,e){return i.isFunction(t)?this.buildApp(t,e):i.isObject(t)?this._buildAppFromObject(t):void 0},buildApp:function(t,e){return e=i.extend({},this.childAppOptions,e),new t(e)},_ensureAppIsUnique:function(t){if(this._childApps[t])throw new Error('A child App with name "'+t+'" has already been added.')},addChildApps:function(t){i.each(t,i.bind(function(t,i){this.addChildApp(i,t)},this))},addChildApp:function(t,e,n){this._ensureAppIsUnique(t);var s=this._buildApp(e,n);if(!s)throw new Error("App build failed. Incorrect configuration.");return s._name=t,this._childApps[t]=s,s._on("destroy",i.partial(this._removeChildApp,t),this),this.isRunning()&&i.result(s,"startWithParent")&&s.start(),s},getName:function(){return this._name},getChildApps:function(){return i.clone(this._childApps)},getChildApp:function(t){return this._childApps[t]},_removeChildApp:function(t){delete this._childApps[t]._name,delete this._childApps[t]},removeChildApps:function(){var t=this.getChildApps();return i.each(this._childApps,i.bind(function(t,i){this.removeChildApp(i)},this)),t},removeChildApp:function(t,e){e=i.extend({},e);var n=this.getChildApp(t);if(n)return e.preventDestroy||i.result(n,"preventDestroy")?this._removeChildApp(t):n.destroy(),n}},p={_stopRunningEvents:function(){i.each(this._runningEvents,i.bind(function(t){this.off.apply(this,t)},this))},_stopRunningListeners:function(){i.each(this._runningListeningTo,i.bind(function(t){this.stopListening.apply(this,t)},this))},on:function(){return this._isRunning&&(this._runningEvents=this._runningEvents||[],this._runningEvents.push(arguments)),n.MnObject.prototype.on.apply(this,arguments)},_on:n.MnObject.prototype.on,listenTo:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),n.MnObject.prototype.listenTo.apply(this,arguments)},_listenTo:n.MnObject.prototype.listenTo,listenToOnce:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),n.MnObject.prototype.listenToOnce.apply(this,arguments)}},u={viewEventPrefix:!1,_buildEventProxies:function(){var t=i.result(this,"viewEvents")||{};this._viewEvents=this.normalizeMethods(t),this._viewTriggers=i.result(this,"viewTriggers")||{},this._viewEventPrefix=i.result(this,"viewEventPrefix")},_proxyViewEvents:function(t){this.listenTo(t,"all",this._childViewEventHandler)},_childViewEventHandler:function(t){for(var e=this._viewEvents,n=arguments.length,s=Array(n>1?n-1:0),r=1;r<n;r++)s[r-1]=arguments[r];i.isFunction(e[t])&&e[t].apply(this,s);var o=this._viewTriggers;i.isString(o[t])&&this.triggerMethod.apply(this,[o[t]].concat(s));var h=this._viewEventPrefix;if(h!==!1){var a=h+":"+t;this.triggerMethod.apply(this,[a].concat(s))}}},d=["startWithParent","restartWithParent","stopWithParent","startAfterInitialized","preventDestroy","StateModel","stateEvents","viewEventPrefix","viewEvents","viewTriggers"],l=n.Application.extend({_isRunning:!1,_isRestarting:!1,preventDestroy:!1,startAfterInitialized:!1,startWithParent:!1,stopWithParent:!0,restartWithParent:!1,constructor:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.mergeOptions(t,d),this.options=i.extend({},i.result(this,"options"),t),this._initChildApps(t),n.Application.call(this,t),i.result(this,"startAfterInitialized")&&this.start(t)},_ensureAppIsIntact:function(){if(this._isDestroyed)throw new Error("App has already been destroyed and cannot be used.")},isRunning:function(){return this._isRunning},isRestarting:function(){return this._isRestarting},start:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this._ensureAppIsIntact(),this._isRunning?this:(t.region&&this.setRegion(t.region),t.view&&this.setView(t.view),this._initState(t),this._buildEventProxies(),this.triggerMethod("before:start",t),this._isRunning=!0,this._bindRunningEvents(),this._startChildApps(),this.triggerStart(t),this)},_bindRunningEvents:function(){this._region&&this._regionEventMonitor(),this._view&&this._proxyViewEvents(this._view),this.delegateStateEvents()},restart:function(){var t=this.getState().attributes;return this._isRestarting=!0,this.stop().start({state:t}),this._isRestarting=!1,this},triggerStart:function(t){this.triggerMethod("start",t)},stop:function(t){return this._isRunning?(this.triggerMethod("before:stop",t),this._stopChildApps(),this._isRunning=!1,this.triggerMethod("stop",t),this._stopRunningListeners(),this._stopRunningEvents(),this):this},destroy:function(){return this._isDestroyed?this:(this.stop(),this._removeView(),this._destroyChildApps(),n.Application.prototype.destroy.apply(this,arguments),this)},setRegion:function(t){return this._region&&this.stopListening(this._region),this._region=t,t.currentView&&this.setView(t.currentView),this._isRunning&&this._regionEventMonitor(),t},_regionEventMonitor:function(){this.listenTo(this._region,{"before:show":this._onBeforeShow,empty:this._onEmpty})},_onBeforeShow:function(t,i){this.setView(i)},_onEmpty:function(){this._removeView()},_removeView:function(){this._view&&(this.stopListening(this._view),delete this._view)},getRegion:function(t){return t?this.getView().getRegion(t):this._region},setView:function(t){return this._view===t?t:(this._view&&this.stopListening(this._view),this._view=t,this._isRunning&&this._proxyViewEvents(t),this._listenTo(this._view,"destroy",this._removeView),t)},getView:function(){return this._view||this._region&&this._region.currentView},showView:function(){for(var t,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this._view,e=arguments.length,n=Array(e>1?e-1:0),s=1;s<e;s++)n[s-1]=arguments[s];return(t=this.getRegion()).show.apply(t,[i].concat(n)),i},showChildView:function(t,i){for(var e,n=arguments.length,s=Array(n>2?n-2:0),r=2;r<n;r++)s[r-2]=arguments[r];return(e=this.getView()).showChildView.apply(e,[t,i].concat(s)),i},getChildView:function(t){return this.getView().getChildView(t)}});i.extend(l.prototype,o,a,p,u);var g=["ViewClass","viewEventPrefix","viewEvents","viewTriggers","viewOptions","region"],c=n.MnObject.extend({ViewClass:n.View,constructor:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.mergeOptions(t,g),this.options=i.extend({},i.result(this,"options"),t),this._buildEventProxies(),this._initState(t),n.MnObject.call(this,t),this.delegateStateEvents()},_shouldDestroy:!0,showIn:function(t,i){return this.region=t,this.show(i),this},show:function(t){var i=this.getRegion();if(this._isShown)throw new Error("Component has already been shown in a region.");if(!i)throw new Error("Component has no defined region.");return this.triggerMethod("before:show"),this.renderView(t),this._isShown=!0,this.triggerMethod("show"),this.listenTo(i,"empty",this._destroy),this},getRegion:function(){return this.region},_getViewClass:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=this.ViewClass;if(n.prototype instanceof e.View||n===e.View)return n;if(i.isFunction(n))return n.call(this,t);throw new Error('"ViewClass" must be a view class or a function that returns a view class')},renderView:function(t){var i=this._getViewClass(t),e=this.mixinOptions(t),n=this.buildView(i,e);return this.currentView=n,this._proxyViewEvents(n),this.triggerMethod("before:render:view",n),this._shouldDestroy=!1,this.showView(n),this._shouldDestroy=!0,this.triggerMethod("render:view",n),this},showView:function(t){this.getRegion().show(t)},mixinOptions:function(t){var e=i.result(this,"viewOptions");return i.extend({state:this.getState().attributes},e,t)},buildView:function(t,i){return new t(i)},_destroy:function(){this._shouldDestroy&&n.MnObject.prototype.destroy.apply(this,arguments)},_emptyRegion:function(t){var i=this.getRegion();i&&(this.stopListening(i,"empty"),i.empty(t))},destroy:function(t){return this._emptyRegion(t),this._shouldDestroy=!0,this._destroy(t),this}});i.extend(c.prototype,o,u);var _="5.0.0-alpha.1",v={MixinState:s,VERSION:_,StateMixin:o,App:l,Component:c};t.MixinState=s,t.VERSION=_,t.StateMixin=o,t.App=l,t.Component=c,t.default=v}); | ||
//# sourceMappingURL=marionette.toolkit.min.js.map |
@@ -16,2 +16,3 @@ # Marionette.Toolkit.App | ||
* [App's `startWithParent`](#apps-startwithparent) | ||
* [App's `restartWithParent`](#apps-restartwithparent) | ||
* [App's `stopWithParent`](#apps-stopwithparent) | ||
@@ -23,2 +24,3 @@ * [Lifecycle API](#lifecycle-api) | ||
* [App `isRunning`](#app-isrunning) | ||
* [App `isRestarting`](#app-isrestarting) | ||
* [App `destroy`](#app-destroy) | ||
@@ -129,2 +131,38 @@ * [Lifecycle Events](#lifecycle-events) | ||
### App's `restartWithParent` | ||
Calls `stop` then `start` on the child app when the parent app restarts. Default value is `false`. | ||
It can also be defined as a function returning a boolean value. | ||
```js | ||
var myApp = new Marionette.Toolkit.App(); | ||
var persistantChildApp = myApp.addChildApp('persistantChildApp', { | ||
AppClass: Marionette.Toolkit.App, | ||
restartWithParent: false | ||
}); | ||
persistantChildApp.on('stop start', function(options) { | ||
console.log(this.isRestarting()); | ||
}); | ||
// does not log | ||
myApp.restart(); | ||
var restartingChildApp = myApp.addChildApp('restartingChildApp', { | ||
AppClass: Marionette.Toolkit.App, | ||
restartWithParent: true | ||
}); | ||
myApp.start(); | ||
restartingChildApp.on('stop start', function(options) { | ||
console.log(this.isRestarting()); | ||
}); | ||
// logs true twice | ||
myApp.restart(); | ||
``` | ||
### App's `stopWithParent` | ||
@@ -131,0 +169,0 @@ |
@@ -24,2 +24,3 @@ # Marionette.Toolkit.Component | ||
* [Component `currentView`](#component-currentview) | ||
* [Component `showView`](#component-showview) | ||
* [Component `mixinOptions`](#component-mixinoptions) | ||
@@ -270,3 +271,3 @@ * [Component `buildView`](#component-buildview) | ||
Builds the view from the ViewClass with the options from [`mixinOptions`](#component-mixinoptions) | ||
and attaches it to the component's `currentView`. It then shows the `currentView` in the component's `region`. | ||
and attaches it to the component's `currentView`. It then shows the `currentView` in the component's `region` via `showView`. | ||
During this `region.show` the component will not destroy itself on the region's empty event. | ||
@@ -317,2 +318,24 @@ While a component can only be shown once, it can be re-rendered many times. | ||
### Component `showView` | ||
Called by `renderView`, it shows the `view` in the component's `region`. | ||
This method can be overridden to change a component's behavior. | ||
```js | ||
var MyComponent = Marionette.Toolkit.Component.extend({ | ||
ViewClass: MyViewClass, | ||
region: someRegion, | ||
showView(view) { | ||
this.getRegion().show(view, { replaceElement: true }); | ||
} | ||
}); | ||
var myComponent = new MyComponent(); | ||
// region el is now <div class="my-component-class"> | ||
myComponent.renderView({ | ||
className: 'my-component-class' | ||
}); | ||
``` | ||
### Component `mixinOptions` | ||
@@ -319,0 +342,0 @@ |
@@ -272,8 +272,11 @@ # ChildAppsMixin | ||
// This is equivalent to childAppInstance.stop(); | ||
myApp.stopChildApp('cA1'); | ||
myApp.stopChildApp('cA1', { foo: 'bar' }); | ||
// false | ||
console.log(childAppInstance.isRunning()); | ||
// bar | ||
console.log(childAppInstance.getOption('foo')); | ||
``` | ||
Note: The parentApp instance is returned for chaining. |
@@ -17,2 +17,4 @@ # Marionette.Toolkit.StateMixin | ||
* [Getting State `getState`](#getting-state) | ||
* [Toggling State `toggleState`](#toggle-state) | ||
* [Checking State `hasState`](#checking-state) | ||
* [Binding State Events `delegateStateEvents`](#binding-events) | ||
@@ -191,3 +193,3 @@ * [Unbinding State Events `undelegateStateEvents`](#unbinding-events) | ||
```js | ||
var MyToolKitApp = new Marionette.Toolkit.App({ | ||
var myToolKitApp = new Marionette.Toolkit.App({ | ||
stateEvents: { | ||
@@ -210,10 +212,12 @@ 'change:foo': 'alert' | ||
```js | ||
var MyToolKitApp = new Marionette.Toolkit.App({ | ||
StateModel: { | ||
defaults: { | ||
'foo': 'bar' | ||
} | ||
} | ||
var MyStateModel = Backbone.Model.extend({ | ||
defaults: { | ||
foo: 'bar' | ||
} | ||
}); | ||
var myToolKitApp = new Marionette.Toolkit.App({ | ||
StateModel: MyStateModel | ||
}); | ||
// This will trigger the "change:foo" event and log "alert!" to the console. | ||
@@ -237,3 +241,3 @@ myToolKitApp.setState('foo', 'hello'); | ||
```js | ||
var MyToolKitApp = Backbone.Model.extend({ | ||
var MyStateModel = Backbone.Model.extend({ | ||
defaults: { | ||
@@ -255,2 +259,46 @@ foo: 'bar' | ||
### Toggling State | ||
`StateMixin` has a `toggleState` method that sets the `StateModel` instance attribute to a boolean value. | ||
Attributes that do not exist on the state will be created. | ||
Not passing in a value will toggle the attribute's current value, while non-boolean values will be coerced to `true` or `false`. | ||
```js | ||
var myToolKitApp = new Marionette.Toolkit.App(); | ||
myToolKitApp.setState('foo', true); | ||
// sets "foo" attribute to false | ||
myToolKitApp.toggleState('foo'); | ||
// coerces "bar" string into boolean, setting "foo" attribute to true | ||
myToolKitApp.toggleState('foo', 'bar'); | ||
// sets a "baz" attribute on the state with a true value | ||
myToolKitApp.toggleState('baz'); | ||
``` | ||
### Checking State | ||
`StateMixin` has a `hasState` method that checks the `StateModel` instance for a specified attribute. | ||
Passing an attribute that does not exist on the state will return `false`. | ||
```js | ||
var myToolKitApp = new Marionette.Toolkit.App(); | ||
// returns false | ||
myToolKitApp.hasState('foo') | ||
myToolKitApp.setState('foo', 'bar'); | ||
// returns true | ||
myToolKitApp.hasState('foo'); | ||
// coerces "bar" string into boolean, setting "foo" attribute to true | ||
myToolKitApp.setState('foo', false); | ||
// Still returns true | ||
myToolKitApp.hasState('foo'); | ||
``` | ||
### Binding State Events | ||
@@ -262,3 +310,3 @@ | ||
```js | ||
var MyToolKitApp = new Marionette.Toolkit.App({ | ||
var myToolKitApp = new Marionette.Toolkit.App({ | ||
stateEvents: { | ||
@@ -295,3 +343,3 @@ 'change:foo': 'alert' | ||
```js | ||
var MyToolKitApp = new Marionette.Toolkit.App({ | ||
var myToolKitApp = new Marionette.Toolkit.App({ | ||
stateEvents: { | ||
@@ -298,0 +346,0 @@ 'change:foo': 'alert' |
{ | ||
"name": "marionette.toolkit", | ||
"version": "4.0.0", | ||
"version": "5.0.0-alpha.1", | ||
"description": "A collection of opinionated Backbone.Marionette extensions for large scale application architecture.", | ||
@@ -41,4 +41,4 @@ "main": "./dist/marionette.toolkit.js", | ||
"babel-register": "6.7.2", | ||
"backbone": "^1.3.3", | ||
"backbone.marionette": "^3.0.0", | ||
"backbone": "~1.3.3", | ||
"backbone.marionette": "^4.0.0 || 4.0.0-alpha.1", | ||
"chai": "2.0.0", | ||
@@ -71,3 +71,3 @@ "del": "1.1.1", | ||
"sinon-chai": "2.7.0", | ||
"underscore": "^1.8.3" | ||
"underscore": "~1.8.3" | ||
}, | ||
@@ -79,6 +79,6 @@ "babelBoilerplateOptions": { | ||
"peerDependencies": { | ||
"backbone.marionette": "^3.0.0", | ||
"backbone": "^1.3.3", | ||
"underscore": "^1.8.3" | ||
"backbone.marionette": "^4.0.0 || 4.0.0-alpha.1", | ||
"backbone": "~1.3.3", | ||
"underscore": "~1.8.3" | ||
} | ||
} |
130
src/app.js
import _ from 'underscore'; | ||
import Marionette from 'backbone.marionette'; | ||
import { Application } from 'backbone.marionette'; | ||
import StateMixin from './mixins/state'; | ||
@@ -10,2 +10,3 @@ import ChildAppsMixin from './mixins/child-apps'; | ||
'startWithParent', | ||
'restartWithParent', | ||
'stopWithParent', | ||
@@ -29,3 +30,3 @@ 'startAfterInitialized', | ||
*/ | ||
const App = Marionette.Application.extend({ | ||
const App = Application.extend({ | ||
@@ -42,2 +43,11 @@ /** | ||
/** | ||
* Internal flag indiciate when `App` is in the process of stopping then starting. | ||
* | ||
* @private | ||
* @type {Boolean} | ||
* @default false | ||
*/ | ||
_isRestarting: false, | ||
/** | ||
* Set to true if a parent `App` should not be able to destroy this `App`. | ||
@@ -74,3 +84,12 @@ * | ||
/** | ||
* Set to true if a parent `App` should be able to restart this `App`. | ||
* | ||
* @type {Boolean|Function} | ||
* @default false | ||
*/ | ||
restartWithParent: false, | ||
/** | ||
* @public | ||
@@ -80,2 +99,3 @@ * @constructs App | ||
* @param {Boolean} [options.startWithParent] | ||
* @param {Boolean} [options.restartWithParent] | ||
* @param {Boolean} [options.stopWithParent] | ||
@@ -91,9 +111,6 @@ * @param {Boolean} [options.startAfterInitialized] | ||
// ViewEventMixin | ||
this._buildEventProxies(); | ||
// ChildAppsMixin | ||
this._initChildApps(options); | ||
Marionette.Application.call(this, options); | ||
Application.call(this, options); | ||
@@ -106,15 +123,2 @@ if(_.result(this, 'startAfterInitialized')) { | ||
/** | ||
* Override of Marionette's Application._initRegion | ||
* Allows region monitor to be setup prior to initialize | ||
* | ||
* @private | ||
* @method _initRegion | ||
* @memberOf App | ||
*/ | ||
_initRegion() { | ||
Marionette.Application.prototype._initRegion.call(this); | ||
this._regionEventMonitor(); | ||
}, | ||
/** | ||
* Internal helper to verify if `App` has been destroyed | ||
@@ -129,6 +133,3 @@ * | ||
if(this._isDestroyed) { | ||
throw new Marionette.Error({ | ||
name: 'AppDestroyedError', | ||
message: 'App has already been destroyed and cannot be used.' | ||
}); | ||
throw new Error('App has already been destroyed and cannot be used.'); | ||
} | ||
@@ -189,2 +190,5 @@ }, | ||
// ViewEventMixin | ||
this._buildEventProxies(); | ||
this.triggerMethod('before:start', options); | ||
@@ -194,5 +198,6 @@ | ||
// StateMixin | ||
this.delegateStateEvents(); | ||
this._bindRunningEvents(); | ||
this._startChildApps(); | ||
this.triggerStart(options); | ||
@@ -203,3 +208,23 @@ | ||
/** | ||
* Sets up region, view, and state events. | ||
* To only be called after `isRunning` is true | ||
* | ||
* @private | ||
* @method _bindRunningEvents | ||
* @memberOf App | ||
*/ | ||
_bindRunningEvents() { | ||
if(this._region) { | ||
this._regionEventMonitor(); | ||
} | ||
if(this._view) { | ||
this._proxyViewEvents(this._view); | ||
} | ||
// StateMixin | ||
this.delegateStateEvents(); | ||
}, | ||
/** | ||
@@ -258,2 +283,4 @@ * Sets the app lifecycle to not running | ||
this._stopChildApps(); | ||
this._isRunning = false; | ||
@@ -285,6 +312,8 @@ | ||
delete this._view; | ||
this._removeView(); | ||
Marionette.Application.prototype.destroy.apply(this, arguments); | ||
this._destroyChildApps(); | ||
Application.prototype.destroy.apply(this, arguments); | ||
return this; | ||
@@ -309,4 +338,10 @@ }, | ||
this._regionEventMonitor(); | ||
if(region.currentView) { | ||
this.setView(region.currentView); | ||
} | ||
if(this._isRunning) { | ||
this._regionEventMonitor(); | ||
} | ||
return region; | ||
@@ -324,3 +359,6 @@ }, | ||
_regionEventMonitor() { | ||
this.listenTo(this._region, 'before:show', this._onBeforeShow); | ||
this.listenTo(this._region, { | ||
'before:show': this._onBeforeShow, | ||
'empty': this._onEmpty | ||
}); | ||
}, | ||
@@ -340,2 +378,27 @@ | ||
/** | ||
* Region monitor handler which empties the region's view | ||
* | ||
* @private | ||
* @method _onEmpty | ||
* @memberOf App | ||
*/ | ||
_onEmpty() { | ||
this._removeView(); | ||
}, | ||
/** | ||
* Region monitor handler which deletes the region's view and listeners to view | ||
* | ||
* @private | ||
* @method _removeView | ||
* @memberOf App | ||
*/ | ||
_removeView() { | ||
if(this._view) { | ||
this.stopListening(this._view); | ||
delete this._view; | ||
} | ||
}, | ||
/** | ||
* Get the Application's Region or | ||
@@ -379,4 +442,9 @@ * Get a region from the Application's View | ||
// ViewEventsMixin | ||
this._proxyViewEvents(view); | ||
if(this._isRunning) { | ||
this._proxyViewEvents(view); | ||
} | ||
// Internal non-running listener | ||
this._listenTo(this._view, 'destroy', this._removeView); | ||
return view; | ||
@@ -394,3 +462,3 @@ }, | ||
getView() { | ||
return this._view; | ||
return this._view || this._region && this._region.currentView; | ||
}, | ||
@@ -397,0 +465,0 @@ |
import _ from 'underscore'; | ||
import Backbone from 'backbone'; | ||
import Marionette from 'backbone.marionette'; | ||
import { MnObject, View } from 'backbone.marionette'; | ||
import StateMixin from './mixins/state'; | ||
@@ -17,3 +17,3 @@ import ViewEventsMixin from './mixins/view-events'; | ||
/** | ||
* Reusable Marionette.Object with View management boilerplate | ||
* Reusable Marionette.MnObject with View management boilerplate | ||
* | ||
@@ -25,3 +25,3 @@ * @public | ||
*/ | ||
const Component = Marionette.Object.extend({ | ||
const Component = MnObject.extend({ | ||
@@ -33,3 +33,3 @@ /** | ||
*/ | ||
ViewClass: Marionette.View, | ||
ViewClass: View, | ||
@@ -60,3 +60,3 @@ /** | ||
Marionette.Object.call(this, options); | ||
MnObject.call(this, options); | ||
@@ -112,13 +112,7 @@ // StateMixin | ||
if(this._isShown) { | ||
throw new Marionette.Error({ | ||
name: 'ComponentShowError', | ||
message: 'Component has already been shown in a region.' | ||
}); | ||
throw new Error('Component has already been shown in a region.'); | ||
} | ||
if(!region) { | ||
throw new Marionette.Error({ | ||
name: 'ComponentRegionError', | ||
message: 'Component has no defined region.' | ||
}); | ||
throw new Error('Component has no defined region.'); | ||
} | ||
@@ -172,6 +166,3 @@ | ||
throw new Marionette.Error({ | ||
name: 'InvalidViewClassError', | ||
message: '"ViewClass" must be a view class or a function that returns a view class' | ||
}); | ||
throw new Error('"ViewClass" must be a view class or a function that returns a view class'); | ||
}, | ||
@@ -209,4 +200,3 @@ | ||
// Show the view in the region | ||
this.getRegion().show(view); | ||
this.showView(view); | ||
@@ -221,2 +211,15 @@ this._shouldDestroy = true; | ||
/** | ||
* Override this to change how the component's view is shown in the region | ||
* | ||
* @public | ||
* @method showView | ||
* @memberOf Component | ||
* @param {Object} view - view built from a viewClass and viewOptions | ||
*/ | ||
showView(view) { | ||
// Show the view in the region | ||
this.getRegion().show(view); | ||
}, | ||
/** | ||
* Mixin initial State with any other viewOptions | ||
@@ -263,3 +266,3 @@ * | ||
if(this._shouldDestroy) { | ||
Marionette.Object.prototype.destroy.apply(this, arguments); | ||
MnObject.prototype.destroy.apply(this, arguments); | ||
} | ||
@@ -266,0 +269,0 @@ }, |
import _ from 'underscore'; | ||
import Marionette from 'backbone.marionette'; | ||
@@ -12,12 +11,5 @@ import StateMixin from './mixins/state'; | ||
const previousToolkit = Marionette.Toolkit; | ||
const VERSION = '<%VERSION%>'; | ||
const Toolkit = Marionette.Toolkit = {}; | ||
Toolkit.noConflict = function() { | ||
Marionette.Toolkit = previousToolkit; | ||
return this; | ||
}; | ||
Toolkit.MixinState = function(classDefinition) { | ||
function MixinState(classDefinition) { | ||
let _StateMixin = StateMixin; | ||
@@ -30,12 +22,18 @@ | ||
_.extend(classDefinition.prototype, _StateMixin); | ||
} | ||
export { | ||
MixinState, | ||
VERSION, | ||
StateMixin, | ||
App, | ||
Component | ||
}; | ||
Toolkit.VERSION = '<%VERSION%>'; | ||
Toolkit.StateMixin = StateMixin; | ||
Toolkit.App = App; | ||
Toolkit.Component = Component; | ||
export default Toolkit; | ||
export default { | ||
MixinState, | ||
VERSION, | ||
StateMixin, | ||
App, | ||
Component | ||
}; |
import _ from 'underscore'; | ||
import Marionette from 'backbone.marionette'; | ||
@@ -50,19 +49,16 @@ const ClassOptions = [ | ||
} | ||
this._initListeners(); | ||
}, | ||
/** | ||
* The child apps should be handled while the app is running; | ||
* After start, before stop, and before destroy. | ||
* | ||
* @private | ||
* @method _initListeners | ||
*/ | ||
_initListeners() { | ||
this.on({ | ||
'start': this._startChildApps, | ||
'before:stop': this._stopChildApps, | ||
'before:destroy': this._destroyChildApps | ||
_getChildStartOpts(childApp) { | ||
const tkOpts = childApp._tkOpts || {}; | ||
const opts = { | ||
region: this.getRegion(tkOpts.regionName) | ||
}; | ||
_.each(tkOpts.getOptions, opt => { | ||
opts[opt] = this.getOption(opt); | ||
}); | ||
return opts; | ||
}, | ||
@@ -77,6 +73,8 @@ | ||
_startChildApps() { | ||
_.each(this._childApps, function(childApp) { | ||
if(_.result(childApp, 'startWithParent')) { | ||
childApp.start(); | ||
} | ||
const shouldStartOption = this._isRestarting ? 'restartWithParent' : 'startWithParent'; | ||
_.each(this._childApps, childApp => { | ||
if(!_.result(childApp, shouldStartOption)) return; | ||
const opts = this._getChildStartOpts(childApp); | ||
childApp.start(opts); | ||
}); | ||
@@ -92,4 +90,5 @@ }, | ||
_stopChildApps() { | ||
const shouldStopOption = this._isRestarting ? 'restartWithParent' : 'stopWithParent'; | ||
_.each(this._childApps, function(childApp) { | ||
if(_.result(childApp, 'stopWithParent')) { | ||
if(_.result(childApp, shouldStopOption)) { | ||
childApp.stop(); | ||
@@ -109,3 +108,5 @@ } | ||
startChildApp(appName, options) { | ||
return this.getChildApp(appName).start(options); | ||
const childApp = this.getChildApp(appName); | ||
const opts = this._getChildStartOpts(childApp); | ||
return childApp.start(_.extend(opts, options)); | ||
}, | ||
@@ -117,7 +118,8 @@ | ||
* @param {String} appName - Name of childApp to stop | ||
* @param {Object} options - Stop options for app | ||
* @public | ||
* @method stopChildApp | ||
*/ | ||
stopChildApp(appName) { | ||
return this.getChildApp(appName).stop(); | ||
stopChildApp(appName, options) { | ||
return this.getChildApp(appName).stop(options); | ||
}, | ||
@@ -149,5 +151,9 @@ | ||
const AppClass = appConfig.AppClass; | ||
const options = _.omit(appConfig, 'AppClass'); | ||
const options = _.omit(appConfig, 'AppClass', 'regionName', 'getOptions'); | ||
return this.buildApp(AppClass, options); | ||
const app = this.buildApp(AppClass, options); | ||
app._tkOpts = _.pick(appConfig, 'regionName', 'getOptions'); | ||
return app; | ||
}, | ||
@@ -201,6 +207,3 @@ | ||
if(this._childApps[appName]) { | ||
throw new Marionette.Error({ | ||
name: 'DuplicateChildAppError', | ||
message: `A child App with name "${ appName }" has already been added.` | ||
}); | ||
throw new Error(`A child App with name "${ appName }" has already been added.`); | ||
} | ||
@@ -241,6 +244,3 @@ }, | ||
if(!childApp) { | ||
throw new Marionette.Error({ | ||
name: 'AddChildAppError', | ||
message: 'App build failed. Incorrect configuration.' | ||
}); | ||
throw new Error('App build failed. Incorrect configuration.'); | ||
} | ||
@@ -253,3 +253,4 @@ | ||
// When the app is destroyed remove the cached app. | ||
childApp.on('destroy', _.partial(this._removeChildApp, appName), this); | ||
// Listener setup relative to the childApp's running state (using _on) | ||
childApp._on('destroy', _.partial(this._removeChildApp, appName), this); | ||
@@ -256,0 +257,0 @@ if(this.isRunning() && _.result(childApp, 'startWithParent')) { |
import _ from 'underscore'; | ||
import Marionette from 'backbone.marionette'; | ||
import { MnObject } from 'backbone.marionette'; | ||
@@ -50,6 +50,15 @@ /** | ||
return Marionette.Object.prototype.on.apply(this, arguments); | ||
return MnObject.prototype.on.apply(this, arguments); | ||
}, | ||
/** | ||
* Keep a copy of non-running on for internal use | ||
* | ||
* @private | ||
* @method _on | ||
* @returns {EventListeners} | ||
*/ | ||
_on: MnObject.prototype.on, | ||
/** | ||
* Overrides `Backbone.Event.listenTo()` | ||
@@ -67,6 +76,15 @@ * If this `App` is running it will register the listener for removal `onStop` | ||
} | ||
return Marionette.Object.prototype.listenTo.apply(this, arguments); | ||
return MnObject.prototype.listenTo.apply(this, arguments); | ||
}, | ||
/** | ||
* Keep a copy of non-running on for internal use | ||
* | ||
* @private | ||
* @method _listenTo | ||
* @returns {EventListeners} | ||
*/ | ||
_listenTo: MnObject.prototype.listenTo, | ||
/** | ||
* Overrides `Backbone.Event.listenToOnce()` | ||
@@ -85,4 +103,4 @@ * If this `App` is running it will register the listener for removal `onStop` | ||
return Marionette.Object.prototype.listenToOnce.apply(this, arguments); | ||
return MnObject.prototype.listenToOnce.apply(this, arguments); | ||
} | ||
}; |
import _ from 'underscore'; | ||
import Backbone from 'backbone'; | ||
import Marionette from 'backbone.marionette'; | ||
@@ -12,3 +11,3 @@ const ClassOptions = [ | ||
* This provides methods used for keeping state using a Backbone.Model. It's meant to | ||
* be used with either a Marionette.Object or Backbone.View. | ||
* be used with either a Marionette.MnObject or Backbone.View. | ||
* | ||
@@ -126,6 +125,3 @@ * @mixin | ||
throw new Marionette.Error({ | ||
name: 'InvalidStateModelError', | ||
message: '"StateModel" must be a model class or a function that returns a model class' | ||
}); | ||
throw new Error('"StateModel" must be a model class or a function that returns a model class'); | ||
}, | ||
@@ -179,2 +175,29 @@ | ||
/** | ||
* Toggle a property on the _stateModel. | ||
* | ||
* @public | ||
* @method toggleState | ||
* @param {String} attr - Attribute name of stateModel. | ||
* @param {val} [value] - Attribute value. | ||
* @returns {Backbone.Model} - The _stateModel or attribute value. | ||
*/ | ||
toggleState(attr, val) { | ||
if(arguments.length > 1) return this._stateModel.set(attr, !!val); | ||
return this._stateModel.set(attr, !this._stateModel.get(attr)); | ||
}, | ||
/** | ||
* Check if _stateModel has a property | ||
* | ||
* @public | ||
* @method hasState | ||
* @param {String} [attr] - Attribute name of stateModel. | ||
* @returns {Boolean} | ||
*/ | ||
hasState(attr) { | ||
return this._stateModel.has(attr); | ||
}, | ||
/** | ||
* Clean up any listeners on the _stateModel. | ||
@@ -181,0 +204,0 @@ * |
@@ -0,8 +1,9 @@ | ||
import _ from 'underscore'; | ||
import Backbone from 'backbone'; | ||
import * as Marionette from 'backbone.marionette'; | ||
module.exports = function() { | ||
const _ = require('underscore'); | ||
const Backbone = require('backbone'); | ||
const $ = require('jquery'); | ||
Backbone.$ = $; | ||
const Marionette = require('backbone.marionette'); | ||
require('../../src/marionette.toolkit'); | ||
Marionette.Toolkit = require('../../src/marionette.toolkit'); | ||
@@ -9,0 +10,0 @@ // Set up test div |
@@ -114,2 +114,47 @@ function createNewApp(startWParent, stopWParent, prevDestroy) { | ||
}); | ||
describe('when restarting the application with child apps', function() { | ||
describe('and a childApp has restartWithParent = true', function() { | ||
it('should stop and start the specific childApp', function() { | ||
const childApps = { | ||
cA1: { | ||
AppClass: Marionette.Toolkit.App, | ||
restartWithParent: true | ||
} | ||
}; | ||
const myApp = new Marionette.Toolkit.App({ childApps: childApps }); | ||
myApp.start(); | ||
const stopSpy = sinon.spy(myApp.getChildApp('cA1'), 'stop'); | ||
const startSpy = sinon.spy(myApp.getChildApp('cA1'), 'start'); | ||
myApp.restart(); | ||
expect(stopSpy).to.be.called.once; | ||
expect(startSpy).to.be.called.once; | ||
}); | ||
}); | ||
describe('and a childApp has restartWithParent = false', function() { | ||
it('should not stop or start the specific childApp', function() { | ||
const childApps = { | ||
cA1: Marionette.Toolkit.App | ||
}; | ||
const myApp = new Marionette.Toolkit.App({ childApps: childApps }); | ||
myApp.start(); | ||
const stopSpy = sinon.spy(myApp.getChildApp('cA1'), 'stop'); | ||
const startSpy = sinon.spy(myApp.getChildApp('cA1'), 'start'); | ||
myApp.restart(); | ||
expect(stopSpy).to.not.be.called; | ||
expect(startSpy).to.not.be.called; | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -114,2 +114,6 @@ import App from '../../src/app'; | ||
it('should start with _isRestarting flag set to false', function() { | ||
expect(this.myApp.isRestarting()).to.equal(false); | ||
}); | ||
it('should be stopped', function() { | ||
@@ -116,0 +120,0 @@ this.myApp.restart(); |
@@ -93,2 +93,17 @@ import $ from 'jquery'; | ||
}); | ||
describe('when setting a region with a view', function() { | ||
beforeEach(function() { | ||
this.myRegion = new Marionette.Region({ el: $('<div>')[0] }); | ||
this.myRegion.show(new Marionette.View({ template: false })); | ||
this.myApp = new this.MyApp(); | ||
this.myApp.setRegion(this.myRegion); | ||
}); | ||
it('should set the app view to the region view', function() { | ||
expect(this.myApp.getRegion().currentView).to.equal(this.myApp.getView()); | ||
}); | ||
}); | ||
}); | ||
@@ -210,2 +225,40 @@ | ||
}); | ||
describe('when a view is emptied in the app\'s region', function() { | ||
it('should delete the shown view', function() { | ||
this.myApp.setRegion(this.region); | ||
this.myApp.showView(this.view); | ||
this.myApp.getRegion().empty(); | ||
expect(this.myApp.getView()).to.be.undefined; | ||
}); | ||
it('should remove any listeners to the view', function() { | ||
this.sinon.spy(this.myApp, 'stopListening'); | ||
this.myApp.setRegion(this.region); | ||
this.myApp.showView(this.view); | ||
this.myApp.stopListening.reset(); | ||
this.myApp.getRegion().empty(); | ||
expect(this.myApp.stopListening).to.have.been.calledWith(this.view); | ||
}); | ||
}); | ||
describe('when a view in the app\'s region is destroyed', function() { | ||
it('should delete the shown view', function() { | ||
this.myApp.setRegion(this.region); | ||
this.myApp.showView(this.view); | ||
this.myApp.getView().destroy(); | ||
expect(this.myApp.getView()).to.be.undefined; | ||
}); | ||
it('should remove any listeners to the view', function() { | ||
this.sinon.spy(this.myApp, 'stopListening'); | ||
this.myApp.setRegion(this.region); | ||
this.myApp.showView(this.view); | ||
this.myApp.stopListening.reset(); | ||
this.myApp.getView().destroy(); | ||
expect(this.myApp.stopListening).to.have.been.calledWith(this.view); | ||
}); | ||
}); | ||
}); | ||
@@ -212,0 +265,0 @@ |
@@ -5,3 +5,3 @@ describe('Marionette.Toolkit.Component', function() { | ||
this.el = Backbone.$('#testRegion'); | ||
this.myRegion = new Backbone.Marionette.Region({ | ||
this.myRegion = new Marionette.Region({ | ||
el: this.el | ||
@@ -124,2 +124,10 @@ }); | ||
it('should call showView with the current view instance', function() { | ||
this.sinon.spy(this.myComponent, 'showView'); | ||
this.myComponent.renderView(); | ||
expect(this.myComponent.showView).to.have.been.calledOnce.and.calledWith(this.myComponent.currentView); | ||
}); | ||
describe('and checking the currentView', function() { | ||
@@ -126,0 +134,0 @@ it('should have the correct className on currentView', function() { |
@@ -223,2 +223,11 @@ describe('ChildAppMixin', function() { | ||
}); | ||
describe('when the child startAfterInitialize', function() { | ||
it('should remove the child if destroyed', function() { | ||
this.myApp.addChildApp('foo', Marionette.Toolkit.App.extend({ startAfterInitialized: true })); | ||
this.myApp.getChildApp('foo').destroy(); | ||
expect(this.myApp.getChildApp('foo')).to.be.undefined; | ||
}); | ||
}); | ||
}); | ||
@@ -385,2 +394,11 @@ | ||
it('should stop childApp with options', function() { | ||
const spy = sinon.spy(this.myChildApp, 'stop'); | ||
this.myApp.stopChildApp('cA1', { foo: 'bar' }); | ||
expect(spy.calledWith({ foo: 'bar' })).to.be.true; | ||
}); | ||
it('should return parentApp instance', function() { | ||
@@ -387,0 +405,0 @@ const spy = sinon.spy(this.myApp, 'stopChildApp'); |
@@ -1,2 +0,2 @@ | ||
import Marionette from 'backbone.marionette'; | ||
import * as Marionette from 'backbone.marionette'; | ||
@@ -9,3 +9,3 @@ describe('StateMixin', function() { | ||
this.StateClass = Marionette.Object.extend(); | ||
this.StateClass = Marionette.MnObject.extend(); | ||
@@ -62,3 +62,3 @@ _.extend(this.StateClass.prototype, Marionette.Toolkit.StateMixin); | ||
beforeEach(function() { | ||
this.StateClass = Marionette.Object.extend(); | ||
this.StateClass = Marionette.MnObject.extend(); | ||
_.extend(this.StateClass.prototype, Marionette.Toolkit.StateMixin); | ||
@@ -175,3 +175,3 @@ }); | ||
beforeEach(function() { | ||
this.StateClass = Marionette.Object.extend(); | ||
this.StateClass = Marionette.MnObject.extend(); | ||
_.extend(this.StateClass.prototype, Marionette.Toolkit.StateMixin); | ||
@@ -203,3 +203,3 @@ | ||
beforeEach(function() { | ||
this.StateClass = Marionette.Object.extend(); | ||
this.StateClass = Marionette.MnObject.extend(); | ||
_.extend(this.StateClass.prototype, Marionette.Toolkit.StateMixin); | ||
@@ -244,2 +244,114 @@ | ||
describe('when calling toggleState with an attribute on a statemodel', function() { | ||
beforeEach(function() { | ||
this.myStateClass.setState('test', true); | ||
}); | ||
describe('with no attribute', function() { | ||
it('should not modify the state', function() { | ||
this.myStateClass.toggleState(); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with no value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test'); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('with a string', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 'foo'); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with a non-zero number', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 123); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with a zero value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 0); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('with a null value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', null); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('with an undefined value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', undefined); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
}); | ||
describe('when calling toggleState without an attribute on a statemodel', function() { | ||
describe('with no value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test'); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with a string', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 'foo'); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with a non-zero number', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 123); | ||
expect(this.myStateClass.getState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('with a zero value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', 0); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('with a null value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', null); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('with an undefined value', function() { | ||
it('should return the modified state', function() { | ||
this.myStateClass.toggleState('test', undefined); | ||
expect(this.myStateClass.getState('test')).to.be.false; | ||
}); | ||
}); | ||
}); | ||
describe('when calling hasState with no attribute on a state model', function() { | ||
it('should return false', function() { | ||
expect(this.myStateClass.hasState('test')).to.be.false; | ||
}); | ||
}); | ||
describe('when calling haState with an attribute on a state model', function() { | ||
it('should return true', function() { | ||
this.myStateClass.setState('test', 'testing'); | ||
expect(this.myStateClass.hasState('test')).to.be.true; | ||
}); | ||
}); | ||
describe('when destroying a state object', function() { | ||
@@ -246,0 +358,0 @@ it('should be gone', function() { |
@@ -19,3 +19,3 @@ import _ from 'underscore'; | ||
describe('when initializing an app', function() { | ||
describe('when starting an app', function() { | ||
let myApp; | ||
@@ -27,2 +27,3 @@ | ||
myApp = new MyApp(mergeOptions); | ||
myApp.start(); | ||
}); | ||
@@ -69,2 +70,4 @@ | ||
myApp.start(); | ||
expect(myApp._proxyViewEvents) | ||
@@ -154,2 +157,4 @@ .to.have.been.calledOnce.and.calledWith(myView); | ||
myApp.setView(myView); | ||
myApp.start(); | ||
}); | ||
@@ -190,2 +195,4 @@ | ||
myApp.setView(myView); | ||
myApp.start(); | ||
}); | ||
@@ -192,0 +199,0 @@ |
@@ -1,15 +0,5 @@ | ||
describe('Marionette.Toolkit.noConflict()', function() { | ||
describe('when calling noConflict', function() { | ||
it('should make Marionette.Toolkit return undefined', function() { | ||
const preNoConflict = Marionette.Toolkit.noConflict(); | ||
expect(Marionette.Toolkit).to.equal(undefined); | ||
Marionette.Toolkit = preNoConflict; | ||
}); | ||
}); | ||
}); | ||
describe('Marionette.Toolkit.MixinState', function() { | ||
describe('when classDefinition has no StateModel definition', function() { | ||
it('should use the StateModel defined on StateMixin', function() { | ||
const MyClass = Marionette.Object.extend(); | ||
const MyClass = Marionette.MnObject.extend(); | ||
Marionette.Toolkit.MixinState(MyClass); | ||
@@ -29,3 +19,3 @@ | ||
const MyClass = Marionette.Object.extend({ | ||
const MyClass = Marionette.MnObject.extend({ | ||
StateModel: MyModel | ||
@@ -32,0 +22,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
494381
4734
2