New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

marionette.toolkit

Package Overview
Dependencies
Maintainers
5
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

marionette.toolkit - npm Package Compare versions

Comparing version 3.1.0 to 4.0.0

docs/mixins/view-events.md

2

bower.json
{
"name": "marionette.toolkit",
"version": "3.1.0",
"version": "4.0.0",
"description": "A collection of opinionated Backbone.Marionette extensions for large scale application architecture.",

@@ -5,0 +5,0 @@ "main": "./dist/marionette.toolkit.js",

@@ -0,1 +1,38 @@

#### v4.0.0
* `App`
* **Breaking Changes:**
* Inits the state model before `before:start` and waits until before `start` to delegate `stateEvents`
* Remove `getInitState` in favor of setting the state in `before:start`
* `setRegion` now returns the set region instance
* `destroy` now returns the app instance
* `destroy` now calls the Application prototype instead of Object
* Added `ViewEventsMixin`
* Added `restart` and `isRestarting` feature
* `start` now accepts a view option
* Added `setView` / `getView` feature allowing setting up children with the App's API prior to `showView`
* `getRegion` now accepts an argument of a region name that is sugar for `myApp.getView().getRegion('regionName')`
* `showView` will show the "set" view if not passed a 1st argument
* Added `showChildView` / `getChildView` for interaction with the children of the App's view
* `Component`
* **Breaking Changes:**
* `viewEventPrefix` is now defaulted to `false`
* `stateEvents` is now delegated after `initialize` this allows for setState in `initialize`
* Added `ViewEventsMixin`
* `destroy` now returns the component instance
* `ChildAppMixin`
* **Breaking Changes:**
* `startChildApp` / `stopChildApp` now return the child app instance
* `StateMixin`
* Add `delegateStateEvents` / `undelegateStateEvents` for binding and unbinding `stateEvents`
* `ViewEventsMixin` - This new mixin adds Marionette.View like support for view event proxying
The API is analogous to `childViewEventPrefix`, `childViewEvents` and `childViewTriggers`
* `viewEventPrefix` defaulting to false allows for auto-proxying events from the view to the app or component
* `viewEvents` allows app or component handlers of view events
* `viewTriggers` triggers an event on the app or component when an event is triggered on the view
#### v3.1.0

@@ -2,0 +39,0 @@

/**
* marionette.toolkit - A collection of opinionated Backbone.Marionette extensions for large scale application architecture.
* @version v3.1.0
* @version v4.0.0
* @link https://github.com/RoundingWellOS/marionette.toolkit

@@ -45,2 +45,15 @@ * @license MIT

this._initState(options);
this.delegateStateEvents();
return this;
},
/**
* @private
* @method _initState
* @param {Object} [options] - Settings for the StateMixin.
*/
_initState: function _initState(options) {
// Make defaults available to this

@@ -57,3 +70,15 @@ this.mergeOptions(options, ClassOptions);

this._setEventHandlers();
},
/**
* Bind events from the _stateModel defined in stateEvents hash
*
* @public
* @method delegateStateEvents
*/
delegateStateEvents: function delegateStateEvents() {
this.undelegateStateEvents();
this.bindEvents(this._stateModel, _.result(this, 'stateEvents'));
return this;

@@ -64,14 +89,11 @@ },

/**
* Unbind all entity events and remove any listeners on _stateModel
* Clean up destroy event handler
* Unbind all entity events on _stateModel
*
* @private
* @method _removeEventHandlers
* @public
* @method undelegateStateEvents
*/
_removeEventHandlers: function _removeEventHandlers() {
if (!this._stateModel) return;
undelegateStateEvents: function undelegateStateEvents() {
this.unbindEvents(this._stateModel);
this.unbindEvents(this._stateModel);
this._stateModel.stopListening();
this.off('destroy', this._destroyState);
return this;
},

@@ -81,3 +103,2 @@

/**
* Bind events from the _stateModel defined in stateEvents hash
* Setup destroy event handle

@@ -89,4 +110,2 @@ *

_setEventHandlers: function _setEventHandlers() {
this.bindEvents(this._stateModel, _.result(this, 'stateEvents'));
this.on('destroy', this._destroyState);

@@ -97,2 +116,17 @@ },

/**
* Clean up destroy event handler, remove any listeners on _stateModel
*
* @private
* @method _removeEventHandlers
*/
_removeEventHandlers: function _removeEventHandlers() {
if (!this._stateModel) return;
this.undelegateStateEvents();
this._stateModel.stopListening();
this.off('destroy', this._destroyState);
},
/**
* Get the StateMixin StateModel class.

@@ -284,5 +318,3 @@ * Checks if the `StateModel` is a model class (the common case)

startChildApp: function startChildApp(appName, options) {
this.getChildApp(appName).start(options);
return this;
return this.getChildApp(appName).start(options);
},

@@ -299,5 +331,3 @@

stopChildApp: function stopChildApp(appName) {
this.getChildApp(appName).stop();
return this;
return this.getChildApp(appName).stop();
},

@@ -544,48 +574,2 @@

return childApp;
},
/**
* Shows a view in the region of the app's view
*
* @public
* @method showChildView
* @param {String} regionName - Name of region to show in
* @param {View} view - Child view instance
* @param {...args} Additional args that get passed along
* @returns {View} - Child view instance
*/
showChildView: function showChildView(regionName, view) {
var appView = this.getView();
if (!appView) {
return false;
}
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
appView.showChildView.apply(appView, [regionName, view].concat(args));
return view;
},
/**
* Returns view from the App view by region name.
*
* @public
* @method getChildView
* @param {String} regionName - Name of region to get view from
* @returns {View}
*/
getChildView: function getChildView(regionName) {
var appView = this.getView();
if (!appView) {
return false;
}
return appView.getChildView(regionName);
}

@@ -681,4 +665,80 @@ };

var ClassOptions$1 = ['startWithParent', 'stopWithParent', 'startAfterInitialized', 'preventDestroy', 'StateModel', 'stateEvents'];
var ViewEventsMixin = {
/**
* Used as the prefix for events forwarded from
* the component's view to the component
* @type {String}
* @default false
*/
viewEventPrefix: false,
/**
* Constructs hashes and options for view event proxy
*
* @private
* @method _buildEventProxies
*/
_buildEventProxies: function _buildEventProxies() {
var viewEvents = _.result(this, 'viewEvents') || {};
this._viewEvents = this.normalizeMethods(viewEvents);
this._viewTriggers = _.result(this, 'viewTriggers') || {};
this._viewEventPrefix = _.result(this, 'viewEventPrefix');
},
/**
* Proxies the ViewClass's viewEvents to the Component itself
* Similar to CollectionView childEvents
* (http://marionettejs.com/docs/v2.3.2/marionette.collectionview.html#collectionviews-childevents)
*
* @private
* @method _proxyViewEvents
* @param {Mn.View|Mn.CollectionView} view -
* The instantiated ViewClass.
*/
_proxyViewEvents: function _proxyViewEvents(view) {
this.listenTo(view, 'all', this._childViewEventHandler);
},
/**
* Event handler for view proxy
* Similar to CollectionView childEvents
* (http://marionettejs.com/docs/v2.3.2/marionette.collectionview.html#collectionviews-childevents)
*
* @private
* @method _childViewEventHandler
* @param {String} - event name
*/
_childViewEventHandler: function _childViewEventHandler(eventName) {
var viewEvents = this._viewEvents;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if (_.isFunction(viewEvents[eventName])) {
viewEvents[eventName].apply(this, args);
}
// use the parent view's proxyEvent handlers
var viewTriggers = this._viewTriggers;
// Call the event with the proxy name on the parent layout
if (_.isString(viewTriggers[eventName])) {
this.triggerMethod.apply(this, [viewTriggers[eventName]].concat(args));
}
var prefix = this._viewEventPrefix;
if (prefix !== false) {
var viewEventName = prefix + ':' + eventName;
this.triggerMethod.apply(this, [viewEventName].concat(args));
}
}
};
var ClassOptions$1 = ['startWithParent', 'stopWithParent', 'startAfterInitialized', 'preventDestroy', 'StateModel', 'stateEvents', 'viewEventPrefix', 'viewEvents', 'viewTriggers'];
/**

@@ -750,2 +810,8 @@ * Marionette.Application with an `initialize` / `start` / `stop` / `destroy` lifecycle.

this.options = _.extend({}, _.result(this, 'options'), options);
// ViewEventMixin
this._buildEventProxies();
// ChildAppsMixin
this._initChildApps(options);

@@ -762,2 +828,16 @@

/**
* 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

@@ -794,2 +874,15 @@ *

/**
* Gets the value of internal `_isRestarting` flag
*
* @public
* @method isRestarting
* @memberOf App
* @returns {Boolean}
*/
isRestarting: function isRestarting() {
return this._isRestarting;
},
/**
* Sets the app lifecycle to running.

@@ -804,3 +897,5 @@ *

*/
start: function start(options) {
start: function start() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this._ensureAppIsIntact();

@@ -812,15 +907,21 @@

var opts = _.extend({}, options);
if (options.region) {
this.setRegion(options.region);
}
this.setRegion(opts.region);
if (options.view) {
this.setView(options.view);
}
// StateMixin
this._initState(options);
this.triggerMethod('before:start', options);
opts.state = this.getInitState(opts.state);
this._isRunning = true;
this.initState(opts);
// StateMixin
this.delegateStateEvents();
this.triggerStart(opts);
this.triggerStart(options);

@@ -832,18 +933,17 @@ return this;

/**
* Set the Application's Region after instantiation
* Sets the app lifecycle to not running
* then sets the app lifecycle to running with ending state
*
* @public
* @method setRegion
* @method restart
* @memberOf App
* @param {Region} [region] - Region to use with the app
* @returns {App}
*/
restart: function restart() {
var state = this.getState().attributes;
setRegion: function setRegion(region) {
if (!region) {
return this;
}
this._isRestarting = true;
this.stop().start({ state: state });
this._isRestarting = false;
this._region = region;
return this;

@@ -854,18 +954,2 @@ },

/**
* Returns state.
* Override to extend state
*
* @public
* @method getInitState
* @memberOf App
* @param {Object} [state] - initial app state
* @returns state
*/
getInitState: function getInitState(state) {
return state;
},
/**
* Triggers start event.

@@ -927,3 +1011,3 @@ * Override to introduce async start

if (this._isDestroyed) {
return;
return this;
}

@@ -933,9 +1017,180 @@

Marionette.Object.prototype.destroy.apply(this, arguments);
delete this._view;
Marionette.Application.prototype.destroy.apply(this, arguments);
return this;
},
/**
* Set the Application's Region
*
* @public
* @method setRegion
* @memberOf App
* @param {Region} [region] - Region to use with the app
* @returns {Region}
*/
setRegion: function setRegion(region) {
if (this._region) {
this.stopListening(this._region);
}
this._region = region;
this._regionEventMonitor();
return region;
},
/**
* Monitors the apps region before:show event so the region's view
* is available to the app
*
* @private
* @method _regionEventMonitor
* @memberOf App
*/
_regionEventMonitor: function _regionEventMonitor() {
this.listenTo(this._region, 'before:show', this._onBeforeShow);
},
/**
* Region monitor handler which sets the app's view to the region's view
*
* @private
* @method _onBeforeShow
* @memberOf App
*/
_onBeforeShow: function _onBeforeShow(region, view) {
this.setView(view);
},
/**
* Get the Application's Region or
* Get a region from the Application's View
*
* @public
* @method getRegion
* @memberOf App
* @param {String} [regionName] - Optional regionName to get from the view
* @returns {Region}
*/
getRegion: function getRegion(regionName) {
if (!regionName) {
return this._region;
}
return this.getView().getRegion(regionName);
},
/**
* Set the Application's View
*
* @public
* @method setView
* @memberOf App
* @param {View} [view] - View to use with the app
* @returns {View}
*/
setView: function setView(view) {
if (this._view === view) {
return view;
}
if (this._view) {
this.stopListening(this._view);
}
this._view = view;
// ViewEventsMixin
this._proxyViewEvents(view);
return view;
},
/**
* Get the Application's View
*
* @public
* @method getView
* @memberOf App
* @returns {View}
*/
getView: function getView() {
return this._view;
},
/**
* Shows a view in the Application's region
*
* @public
* @method showView
* @param {View} view - Child view instance defaults to App's view
* @param {...args} Additional args that get passed along
* @returns {View}
*/
showView: function showView() {
var _getRegion;
var view = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this._view;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_getRegion = this.getRegion()).show.apply(_getRegion, [view].concat(args));
return view;
},
/**
* Shows a view in the region of the app's view
*
* @public
* @method showChildView
* @param {String} regionName - Name of region to show in
* @param {View} view - Child view instance
* @param {...args} Additional args that get passed along
* @returns {View} - Child view instance
*/
showChildView: function showChildView(regionName, view) {
var _getView;
for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
args[_key2 - 2] = arguments[_key2];
}
(_getView = this.getView()).showChildView.apply(_getView, [regionName, view].concat(args));
return view;
},
/**
* Returns view from the App view by region name.
*
* @public
* @method getChildView
* @param {String} regionName - Name of region to get view from
* @returns {View}
*/
getChildView: function getChildView(regionName) {
return this.getView().getChildView(regionName);
}
});
_.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin);
_.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin, ViewEventsMixin);
var ClassOptions$3 = ['ViewClass', 'viewEventPrefix', 'viewOptions', 'region'];
var ClassOptions$3 = ['ViewClass', 'viewEventPrefix', 'viewEvents', 'viewTriggers', 'viewOptions', 'region'];
/**

@@ -959,17 +1214,2 @@ * Reusable Marionette.Object with View management boilerplate

/**
* Used as the prefix for events forwarded from
* the component's view to the component
* @type {String}
* @default 'view'
*/
viewEventPrefix: 'view',
/**
* Options hash passed to the view when built.
* @type {Object|Function}
* @default '{}'
*/
viewOptions: {},
/**
* @public

@@ -992,5 +1232,14 @@ * @constructs Component

this.initState(options);
this.options = _.extend({}, _.result(this, 'options'), options);
// ViewEventMixin
this._buildEventProxies();
// StateMixin
this._initState(options);
Marionette.Object.call(this, options);
// StateMixin
this.delegateStateEvents();
},

@@ -1136,2 +1385,3 @@

// ViewEventMixin
this._proxyViewEvents(view);

@@ -1157,28 +1407,2 @@

/**
* Proxies the ViewClass's viewEvents to the Component itself
* Similar to CollectionView childEvents
* (http://marionettejs.com/docs/v2.3.2/marionette.collectionview.html#collectionviews-childevents)
*
* @private
* @method _proxyViewEvents
* @memberOf Component
* @param {Mn.View|Mn.CollectionView} view -
* The instantiated ViewClass.
*/
_proxyViewEvents: function _proxyViewEvents(view) {
var prefix = this.viewEventPrefix;
view.on('all', function () {
var args = _.toArray(arguments);
var rootEvent = args[0];
args[0] = prefix + ':' + rootEvent;
args.splice(1, 0, view);
this.triggerMethod.apply(this, args);
}, this);
},
/**
* Mixin initial State with any other viewOptions

@@ -1264,6 +1488,8 @@ *

this._destroy(options);
return this;
}
});
_.extend(Component.prototype, StateMixin);
_.extend(Component.prototype, StateMixin, ViewEventsMixin);

@@ -1293,3 +1519,3 @@ /**

Toolkit.VERSION = '3.1.0';
Toolkit.VERSION = '4.0.0';

@@ -1296,0 +1522,0 @@ Toolkit.StateMixin = StateMixin;

/**
* marionette.toolkit - A collection of opinionated Backbone.Marionette extensions for large scale application architecture.
* @version v3.1.0
* @version v4.0.0
* @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]:{};this.mergeOptions(t,n),this._removeEventHandlers();var e=this._getStateModel(t);return this._stateModel=new e(t.state),this._setEventHandlers(),this},_removeEventHandlers:function(){this._stateModel&&(this.unbindEvents(this._stateModel),this._stateModel.stopListening(),this.off("destroy",this._destroyState))},_setEventHandlers:function(){this.bindEvents(this._stateModel,t.result(this,"stateEvents")),this.on("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),this},stopChildApp:function(t){return this.getChildApp(t).stop(),this},_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},showChildView:function(t,e){var i=this.getView();if(!i)return!1;for(var n=arguments.length,s=Array(n>2?n-2:0),r=2;r<n;r++)s[r-2]=arguments[r];return i.showChildView.apply(i,[t,e].concat(s)),e},getChildView:function(t){var e=this.getView();return!!e&&e.getChildView(t)}},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)}},p=["startWithParent","stopWithParent","startAfterInitialized","preventDestroy","StateModel","stateEvents"],a=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._initChildApps(i),e.Application.call(this,i),t.result(this,"startAfterInitialized")&&this.start(i)},_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},start:function(e){if(this._ensureAppIsIntact(),this._isRunning)return this;var i=t.extend({},e);return this.setRegion(i.region),this.triggerMethod("before:start",e),i.state=this.getInitState(i.state),this._isRunning=!0,this.initState(i),this.triggerStart(i),this},setRegion:function(t){return t?(this._region=t,this):this},getInitState:function(t){return t},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(){this._isDestroyed||(this.stop(),e.Object.prototype.destroy.apply(this,arguments))}});t.extend(a.prototype,s,o,h);var d=["ViewClass","viewEventPrefix","viewOptions","region"],u=e.Object.extend({ViewClass:e.View,viewEventPrefix:"view",viewOptions:{},constructor:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.mergeOptions(t,d),this.initState(t),e.Object.call(this,t)},_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},_proxyViewEvents:function(e){var i=this.viewEventPrefix;e.on("all",function(){var n=t.toArray(arguments),s=n[0];n[0]=i+":"+s,n.splice(1,0,e),this.triggerMethod.apply(this,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){this._emptyRegion(t),this._shouldDestroy=!0,this._destroy(t)}});t.extend(u.prototype,s);var l=e.Toolkit,c=e.Toolkit={};return c.noConflict=function(){return e.Toolkit=l,this},c.MixinState=function(e){var i=s;e.prototype.StateModel&&(i=t.omit(s,"StateModel")),t.extend(e.prototype,i)},c.VERSION="3.1.0",c.StateMixin=s,c.App=a,c.Component=u,c});
!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});
//# sourceMappingURL=marionette.toolkit.min.js.map

@@ -8,2 +8,3 @@ # Marionette.Toolkit.App

* [`ChildAppsMixin`](./mixins/child-apps.md) to manage the addition and removal of child `App`s and relating the child `App` lifecycle with the parent `App` lifecycle.
* [`ViewEventsMixin`](./mixins/view-events.md) for proxying events from the app's view to the app.

@@ -19,2 +20,3 @@ ## Documentation Index

* [App `start`](#app-start)
* [App `restart`](#app-restart)
* [App `stop`](#app-stop)

@@ -26,11 +28,15 @@ * [App `isRunning`](#app-isrunning)

* ["before:stop" / "stop" events](#beforestop--stop-events)
* [Application Region](#application-region)
* [App `setRegion`](#app-setregion)
* [Application State](#application-state)
* [App `getInitState`](#app-getinitstate)
* [App `StateModel`](#app-statemodel)
* [App `stateEvents`](#app-stateevents)
* [Application Region](#application-region)
* [App `setRegion`](#app-setregion)
* [App `getRegion`](#app-getregion)
* [Application View](#application-view)
* [App `setView`](#application-setview)
* [App `getView`](#application-getview)
* [App `showView`](#application-showview)
* [App `showChildView`](#app-showchildview)
* [App `getChildView`](#app-getchildview)
* [View Events](#view-events)

@@ -196,2 +202,29 @@ ## Using Toolkit App

### App `restart`
This method saves the current state of the app before stopping it.
It then starts the app again with the preserved state attributes.
```js
var myApp = new Marionette.Toolkit.App();
myApp.on('before:start', function(options) {
console.log(options.state);
});
var initialState = {
foo: 'bar'
};
// logs { foo: 'bar' }
myApp.start({
state: initialState
});
myApp.setState('foo', 'baz');
// logs { foo: 'baz' }
myApp.restart();
```
### App `stop`

@@ -249,2 +282,23 @@

### App `isRestarting`
Returns a Boolean indicating whether or not the `App` is restarting.
```js
var myApp = new Marionette.Toolkit.App();
myApp.on('before:stop', function(options) {
console.log(this.isRestarting());
});
myApp.start();
// logs true
myApp.restart();
// logs false
myApp.stop();
```
### App `destroy`

@@ -274,6 +328,6 @@

The "before:start" event and corresponding `onBeforeStart`
method are triggered just before the `App` `isRunning` is set `true`.
method are triggered just before the `App` `isRunning` is set `true`. This is the appropriate place to set up your app's initial state. Calling `setState` in `onBeforeStart` will not trigger any events.
The "start" event and corresponding `onStart`
method are triggered after the `App` `isRunning` is set `true`.
method are triggered after the `App` `isRunning` is set `true`. Once `onStart` is run, state event listeners have been applied.

@@ -336,56 +390,194 @@ ```js

### App `StateModel`
A [`StateModel`](./mixins/state.md#statemixins-statemodel) class can be passed to App instantiation as an option or defined on the App.
```js
var myApp = new MyApp({
StateModel: MyStateModel
});
```
### App `stateEvents`
A [`stateEvents`](./mixins/state.md#statemixins-stateevents) hash can be passed to App instantiation as an option or defined on the App.
```js
var MyApp = Marionette.Toolkit.App.extend({
stateEvents: {
'change': 'onChangeState'
}
onChangeState: function() {
// Handle state change event
}
});
```
## Application State
Application state can be passed to [App `start`](#app-start) as a `state` option. The state is maintained while the app is running.
```js
myApp.start({
state: {
limit: 10
}
});
myApp.getState('limit') === 10;
```
## Application Region
A `Marionette.Region` instance can be passed to [App `start`](#app-start) as an option,
A `Marionette.Region` instance can be passed to [App `start`](#app-start) as a `region` option,
[setting the App region](#app-setregion), making it available to both [`before:start` and `start`](#beforestart--start-events) events.
```js
myApp.start({
region: myRegion
});
myApp.getRegion() === myRegion;
```
### App `setRegion`
Calling `setRegion` will replace the `App`'s region making it available to the App's [Region API](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md#getregion).
Unlike `Marionette.Appliation`'s [region attribute](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md#root-layout), `setRegion` only accepts a Region instance.
## Application State
```js
myApp.setRegion(myView.getRegion('appRegion'));
Application state can be passed to [App `start`](#app-start) as an option. The state is maintained while the app is running.
```
### App `getInitState`
### App `getRegion`
Override `getInitState` to modify state object passed into the App constructor.
`getRegion` performs two functions. When passed no arguments returns the app's region.
You can also give `getRegion` a region name string which will attempt to return a region from the app's view.
It is sugar for `myApp.getView().getRegion('regionName') === myApp.getRegion('regionName');`.
```js
getInitState(state){
const modState = _.extend({}, {foo: 'bar'}, state);
const MyApp = Toolkit.App.extend({
region: '#app-hook'
});
return return modState;
}
const myView = new Mn.View({
template: MyTemplate,
regions: {
foo: '#foo'
}
});
myApp.getRegion(); // #app-hook region
myApp.showView(myView);
myApp.getRegion('foo'); // foo region on myView
```
### App `StateModel`
## Application View
A [`StateModel`](./mixins/state.md#statemixins-statemodel) class can be passed to App instantiation as an option or defined on the App.
A [`Marionette.Application`](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md) can have an associated view shown in its region with `showView`.
Toolkit takes this a step further and proxies that view's `showChildView` and `getChildView`. This is simply sugar for common patterns.
A View instance can be passed to [App `start`](#app-start) as an option,
[setting the App view](#app-setview), making it available to both [`before:start` and `start`](#beforestart--start-events) events.
```js
var myApp = new MyApp({
StateModel: MyStateModel
myApp.start({
view: myView
});
myApp.getView() === myView;
```
### App `stateEvents`
### App `setView`
A [`stateEvents`](./mixins/state.md#statemixins-stateevents) hash can be passed to App instantiation as an option or defined on the App.
Assign a view to an App. There are two notable use cases for this.
First, this allows you to use `App.getRegion('regionName')` or `App.showChildView`
without first showing the view in the App's region. This way all of the App's children
can be setup and rendered prior to attaching anything to the DOM.
```js
var MyApp = Marionette.Toolkit.App.extend({
stateEvents: {
'change': 'onChangeState'
const MyApp = Toolkit.App.extend({
onStart() {
this.setView(myLayoutView);
this.startChildApp('child', { region: this.getRegion('child') });
this.showChildView('title', 'My App');
this.showView();
}
onChangeState: function() {
// Handle state change event
});
```
The second is when you want to associate an App with a view already in a region or one that
won't be shown in a region.
```js
const myView = new Mn.View.extend({
el: $('#existing-dom'),
regions: {
foo: '#foo'
}
});
const myApp = new MyApp();
myApp.start();
myApp.setView(myView);
myApp.getRegion('foo') === myView.getRegion('foo');
```
## Application View
`setView` returns the view.
A [`Marionette.Application`](https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.application.md) can have an associated view shown in its region with `showView`. Toolkit takes this a step further and proxies that view's `showChildView` and `getChildView`. This is simply sugar for common patterns.
```js
const myView = myApp.setView(new MyView());
// myApp.listenTo(myView, ...);
```
### App `getView`
Returns the view instance associated with the App.
```js
myApp.setView(fooView);
myApp.getView() === fooView;
myApp.showView(barView);
myApp.getView() === barView;
myApp.getRegion().show(bazView);
myApp.getView() === bazView;
```
### App `showView`
Shows a view instance in the App's region.
The first argument for `showView` is the view instance, but if left undefined the App will
attempt to use the current view ie: `myApp.getView()`. The second argument is region.show options
```js
myApp.showView(myView, { replaceElement: true });
// Alternatively
myApp.setView(fooView);
// Show fooView
myApp.showView();
```
`showView` returns the view.
```js
const myView = myApp.showView(new MyView());
// myApp.listenTo(myView, ...);
```
### App `showChildView`

@@ -404,2 +596,11 @@

`showChildView` returns the child view.
```js
const myView = myApp.showChildView('fooRegion', new MyView());
// myApp.listenTo(myView, ...);
```
### App `getChildView`

@@ -417,1 +618,10 @@

```
### View events
View events for an App's view can be proxied following a very similar API to what you would
expect on a `Marionette.View` and `Marionette.CollectionView` with their children.
You can use `viewEvents`, `viewTriggers` and `viewEventPrefix` for auto-proxying events.
For more information see the [ViewEventsMixin documentation](./mixins/view-events.md).
# Marionette.Toolkit.Component
`Marionette.Toolkit.Component` is heavily influenced by **@jfairbank**'s [Marionette.Component](https://github.com/jfairbank/marionette.component) and is an extension of `Marionette.Application`. It mixes in [`StateMixin`](./mixins/state.md) that manages a view (or views) whose lifecycle is tied to the region it is shown in.
`Marionette.Toolkit.Component` is heavily influenced by **@jfairbank**'s [Marionette.Component](https://github.com/jfairbank/marionette.component) and is an extension of `Marionette.Application`.
The Component provides a consistent interface for which to package state-view-logic.
It utilizes the following mixins:
* [`StateMixin`](./mixins/state.md) manages a view (or views) whose lifecycle is tied to the region it is shown in.
* [`ViewEventsMixin`](./mixins/view-events.md) for proxying events from the component's view to the app.
## Documentation Index
* [Using a Component](#using-a-component)
* [Component's `ViewClass`](#components-viewclass)
* [Component's `viewEventPrefix`](#components-vieweventprefix)
* [Component's `viewOptions`](#components-viewoptions)

@@ -15,3 +18,3 @@ * [Component's `region`](#components-region)

* ["before:render:view" / "render:view" events](#beforerenderview--renderview-events)
* ["view:*" event bubbling from the component view](#view-event-bubbling-from-the-component-view)
* [View Events](#view-events)
* [Component API](#component-api)

@@ -103,28 +106,2 @@ * [Component `showIn`](#component-showin)

### Component's `viewEventPrefix`
You can customize the event prefix for events that are forwarded
through the component. To do this, set the `viewEventPrefix`
on the component. For more information on the `viewEventPrefix` see
["view:*" event bubbling from the component view](#view-event-bubbling-from-the-component-view)
```js
var MyComponent = Marionette.Toolkit.Component.extend({
viewEventPrefix: 'some:prefix'
});
var myComponent = new MyComponent({});
myComponent.showIn(MyRegion);
myComponent.on('some:prefix:render', function(){
// view was rendered
});
myComponent.currentView.render();
```
The `viewEventPrefix` can be provided in the component definition or
in the constructor function call, to get a component instance.
### Component's `viewOptions`

@@ -245,36 +222,11 @@

### `view:*` event bubbling from the component view
### View Events
When the current view within a component triggers an
event, that event will bubble up through the component
with "view:" prepended to the event name. Override the
prefix by setting [`viewEventPrefix`](#components-vieweventprefix).
View events for a Component's view can be proxied following a very similar API to what you would
expect on a `Marionette.View` and `Marionette.CollectionView` with their children.
That is, if a current view triggers "do:something", the
component will then trigger "childview:do:something".
You can use `viewEvents`, `viewTriggers` and `viewEventPrefix` for auto-proxying events.
```js
var MyView = Marionette.View.extend({
triggers: {
'click button': 'do:something'
}
});
For more information see the [ViewEventsMixin documentation](./mixins/view-events.md).
// get the collection view in place
var myComponent = new Marionette.Toolkit.Component(null, {
ViewClass: MyView,
onViewDoSomething: function(currentView, args*) {
console.log("I said, 'do something!'");
}
});
myComponent.on('view:do:something', function(currentView, args*){
console.log("My component said, 'do something!'");
});
```
Now, whenever the button inside the `currentView` is clicked,
both messages will log to the console.
## Component API

@@ -281,0 +233,0 @@

@@ -245,6 +245,5 @@ # ChildAppsMixin

myApp.startChildApp('cA1', { foo: 'bar' });
// Once you have the childApp instance stored, you can also do childAppInstance.start();
var childAppInstance = myApp.startChildApp('cA1', { foo: 'bar' });
var childAppInstance = myApp.getChildApp('cA1');
// true

@@ -268,13 +267,14 @@ console.log(childAppInstance.isRunning());

myApp.startChildApp('cA1');
var childAppInstance = myApp.startChildApp('cA1');
// true
console.log(myApp.getChildApp('cA1').isRunning());
console.log(childAppInstance.isRunning());
// This is equivalent to childAppInstance.stop();
myApp.stopChildApp('cA1');
// false
console.log(myApp.getChildApp('cA1').isRunning());
console.log(childAppInstance.isRunning());
```
Note: The parentApp instance is returned for chaining.

@@ -17,2 +17,4 @@ # Marionette.Toolkit.StateMixin

* [Getting State `getState`](#getting-state)
* [Binding State Events `delegateStateEvents`](#binding-events)
* [Unbinding State Events `undelegateStateEvents`](#unbinding-events)

@@ -249,1 +251,57 @@ ### Using StateMixin

```
### Binding State Events
`StateMixin` has a `delegateStateEvents` that will bind all events specified
in the `stateEvents` option. Implementation matches [Backbone.View.delegateEvents](http://backbonejs.org/#View-delegateEvents).
```js
var MyToolKitApp = new Marionette.Toolkit.App({
stateEvents: {
'change:foo': 'alert'
},
alert: function(){
console.log('alert!');
}
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'bar');
myToolKitApp.undelegateStateEvents();
// This will still trigger the `change:foo` event, but will NOT log 'alert!'
// in the console
myToolKitApp.setState('foo', 'baz');
myToolKitApp.delegateStateEvents();
// This will trigger the "change:foo" event and log "alert!" to the console
// once again.
myToolKitApp.setState('foo', 'bar');
```
### Unbinding State Events
`StateMixin` has a `undelegateStateEvents` that will unbind all event listeners
specified on the `StateModel` option. Implementation matches [Backbone.View.undelegateEvents](http://backbonejs.org/#View-undelegateEvents).
```js
var MyToolKitApp = new Marionette.Toolkit.App({
stateEvents: {
'change:foo': 'alert'
},
alert: function(){
console.log('alert!');
}
});
// This will trigger the "change:foo" event and log "alert!" to the console.
myToolKitApp.setState('foo', 'bar');
myToolKitApp.undelegateStateEvents();
// This will still trigger the `change:foo` event, but will NOT log 'alert!' in the console
myToolKitApp.setState('foo', 'baz');
```
{
"name": "marionette.toolkit",
"version": "3.1.0",
"version": "4.0.0",
"description": "A collection of opinionated Backbone.Marionette extensions for large scale application architecture.",

@@ -5,0 +5,0 @@ "main": "./dist/marionette.toolkit.js",

@@ -6,2 +6,3 @@ import _ from 'underscore';

import EventListenersMixin from './mixins/event-listeners';
import ViewEventsMixin from './mixins/view-events';

@@ -14,3 +15,6 @@ const ClassOptions = [

'StateModel',
'stateEvents'
'stateEvents',
'viewEventPrefix',
'viewEvents',
'viewTriggers'
];

@@ -82,2 +86,8 @@

this.options = _.extend({}, _.result(this, 'options'), options);
// ViewEventMixin
this._buildEventProxies();
// ChildAppsMixin
this._initChildApps(options);

@@ -93,2 +103,15 @@

/**
* 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

@@ -123,2 +146,14 @@ *

/**
* Gets the value of internal `_isRestarting` flag
*
* @public
* @method isRestarting
* @memberOf App
* @returns {Boolean}
*/
isRestarting() {
return this._isRestarting;
},
/**
* Sets the app lifecycle to running.

@@ -133,3 +168,3 @@ *

*/
start(options) {
start(options = {}) {
this._ensureAppIsIntact();

@@ -141,15 +176,21 @@

const opts = _.extend({}, options);
if(options.region) {
this.setRegion(options.region);
}
this.setRegion(opts.region);
if(options.view) {
this.setView(options.view);
}
// StateMixin
this._initState(options);
this.triggerMethod('before:start', options);
opts.state = this.getInitState(opts.state);
this._isRunning = true;
this.initState(opts);
// StateMixin
this.delegateStateEvents();
this.triggerStart(opts);
this.triggerStart(options);

@@ -159,19 +200,19 @@ return this;

/**
* Set the Application's Region after instantiation
* Sets the app lifecycle to not running
* then sets the app lifecycle to running with ending state
*
* @public
* @method setRegion
* @method restart
* @memberOf App
* @param {Region} [region] - Region to use with the app
* @returns {App}
*/
restart() {
const state = this.getState().attributes;
setRegion(region) {
if(!region) {
return this;
}
this._isRestarting = true;
this.stop().start({ state });
this._isRestarting = false;
this._region = region;
return this;

@@ -181,17 +222,2 @@ },

/**
* Returns state.
* Override to extend state
*
* @public
* @method getInitState
* @memberOf App
* @param {Object} [state] - initial app state
* @returns state
*/
getInitState(state) {
return state;
},
/**
* Triggers start event.

@@ -251,3 +277,3 @@ * Override to introduce async start

if(this._isDestroyed) {
return;
return this;
}

@@ -257,8 +283,155 @@

Marionette.Object.prototype.destroy.apply(this, arguments);
delete this._view;
Marionette.Application.prototype.destroy.apply(this, arguments);
return this;
},
/**
* Set the Application's Region
*
* @public
* @method setRegion
* @memberOf App
* @param {Region} [region] - Region to use with the app
* @returns {Region}
*/
setRegion(region) {
if(this._region) {
this.stopListening(this._region);
}
this._region = region;
this._regionEventMonitor();
return region;
},
/**
* Monitors the apps region before:show event so the region's view
* is available to the app
*
* @private
* @method _regionEventMonitor
* @memberOf App
*/
_regionEventMonitor() {
this.listenTo(this._region, 'before:show', this._onBeforeShow);
},
/**
* Region monitor handler which sets the app's view to the region's view
*
* @private
* @method _onBeforeShow
* @memberOf App
*/
_onBeforeShow(region, view) {
this.setView(view);
},
/**
* Get the Application's Region or
* Get a region from the Application's View
*
* @public
* @method getRegion
* @memberOf App
* @param {String} [regionName] - Optional regionName to get from the view
* @returns {Region}
*/
getRegion(regionName) {
if(!regionName) {
return this._region;
}
return this.getView().getRegion(regionName);
},
/**
* Set the Application's View
*
* @public
* @method setView
* @memberOf App
* @param {View} [view] - View to use with the app
* @returns {View}
*/
setView(view) {
if(this._view === view) {
return view;
}
if(this._view) {
this.stopListening(this._view);
}
this._view = view;
// ViewEventsMixin
this._proxyViewEvents(view);
return view;
},
/**
* Get the Application's View
*
* @public
* @method getView
* @memberOf App
* @returns {View}
*/
getView() {
return this._view;
},
/**
* Shows a view in the Application's region
*
* @public
* @method showView
* @param {View} view - Child view instance defaults to App's view
* @param {...args} Additional args that get passed along
* @returns {View}
*/
showView(view = this._view, ...args) {
this.getRegion().show(view, ...args);
return view;
},
/**
* Shows a view in the region of the app's view
*
* @public
* @method showChildView
* @param {String} regionName - Name of region to show in
* @param {View} view - Child view instance
* @param {...args} Additional args that get passed along
* @returns {View} - Child view instance
*/
showChildView(regionName, view, ...args) {
this.getView().showChildView(regionName, view, ...args);
return view;
},
/**
* Returns view from the App view by region name.
*
* @public
* @method getChildView
* @param {String} regionName - Name of region to get view from
* @returns {View}
*/
getChildView(regionName) {
return this.getView().getChildView(regionName);
}
});
_.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin);
_.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin, ViewEventsMixin);
export default App;

@@ -5,2 +5,3 @@ import _ from 'underscore';

import StateMixin from './mixins/state';
import ViewEventsMixin from './mixins/view-events';

@@ -10,5 +11,8 @@ const ClassOptions = [

'viewEventPrefix',
'viewEvents',
'viewTriggers',
'viewOptions',
'region'
];
/**

@@ -32,17 +36,2 @@ * Reusable Marionette.Object with View management boilerplate

/**
* Used as the prefix for events forwarded from
* the component's view to the component
* @type {String}
* @default 'view'
*/
viewEventPrefix: 'view',
/**
* Options hash passed to the view when built.
* @type {Object|Function}
* @default '{}'
*/
viewOptions: {},
/**
* @public

@@ -63,5 +52,14 @@ * @constructs Component

this.initState(options);
this.options = _.extend({}, _.result(this, 'options'), options);
// ViewEventMixin
this._buildEventProxies();
// StateMixin
this._initState(options);
Marionette.Object.call(this, options);
// StateMixin
this.delegateStateEvents();
},

@@ -200,2 +198,3 @@

// ViewEventMixin
this._proxyViewEvents(view);

@@ -220,27 +219,2 @@

/**
* Proxies the ViewClass's viewEvents to the Component itself
* Similar to CollectionView childEvents
* (http://marionettejs.com/docs/v2.3.2/marionette.collectionview.html#collectionviews-childevents)
*
* @private
* @method _proxyViewEvents
* @memberOf Component
* @param {Mn.View|Mn.CollectionView} view -
* The instantiated ViewClass.
*/
_proxyViewEvents(view) {
const prefix = this.viewEventPrefix;
view.on('all', function() {
const args = _.toArray(arguments);
const rootEvent = args[0];
args[0] = `${ prefix }:${ rootEvent }`;
args.splice(1, 0, view);
this.triggerMethod.apply(this, args);
}, this);
},
/**
* Mixin initial State with any other viewOptions

@@ -322,7 +296,9 @@ *

this._destroy(options);
return this;
}
});
_.extend(Component.prototype, StateMixin);
_.extend(Component.prototype, StateMixin, ViewEventsMixin);
export default Component;

@@ -106,5 +106,3 @@ import _ from 'underscore';

startChildApp(appName, options) {
this.getChildApp(appName).start(options);
return this;
return this.getChildApp(appName).start(options);
},

@@ -120,5 +118,3 @@

stopChildApp(appName) {
this.getChildApp(appName).stop();
return this;
return this.getChildApp(appName).stop();
},

@@ -353,43 +349,3 @@

return childApp;
},
/**
* Shows a view in the region of the app's view
*
* @public
* @method showChildView
* @param {String} regionName - Name of region to show in
* @param {View} view - Child view instance
* @param {...args} Additional args that get passed along
* @returns {View} - Child view instance
*/
showChildView(regionName, view, ...args) {
const appView = this.getView();
if(!appView) {
return false;
}
appView.showChildView(regionName, view, ...args);
return view;
},
/**
* Returns view from the App view by region name.
*
* @public
* @method getChildView
* @param {String} regionName - Name of region to get view from
* @returns {View}
*/
getChildView(regionName) {
const appView = this.getView();
if(!appView) {
return false;
}
return appView.getChildView(regionName);
}
};

@@ -33,2 +33,14 @@ import _ from 'underscore';

initState(options = {}) {
this._initState(options);
this.delegateStateEvents();
return this;
},
/**
* @private
* @method _initState
* @param {Object} [options] - Settings for the StateMixin.
*/
_initState(options) {
// Make defaults available to this

@@ -45,3 +57,14 @@ this.mergeOptions(options, ClassOptions);

this._setEventHandlers();
},
/**
* Bind events from the _stateModel defined in stateEvents hash
*
* @public
* @method delegateStateEvents
*/
delegateStateEvents() {
this.undelegateStateEvents();
this.bindEvents(this._stateModel, _.result(this, 'stateEvents'));
return this;

@@ -51,18 +74,14 @@ },

/**
* Unbind all entity events and remove any listeners on _stateModel
* Clean up destroy event handler
* Unbind all entity events on _stateModel
*
* @private
* @method _removeEventHandlers
* @public
* @method undelegateStateEvents
*/
_removeEventHandlers() {
if(!this._stateModel) return;
undelegateStateEvents() {
this.unbindEvents(this._stateModel);
this.unbindEvents(this._stateModel);
this._stateModel.stopListening();
this.off('destroy', this._destroyState);
return this;
},
/**
* Bind events from the _stateModel defined in stateEvents hash
* Setup destroy event handle

@@ -74,4 +93,2 @@ *

_setEventHandlers() {
this.bindEvents(this._stateModel, _.result(this, 'stateEvents'));
this.on('destroy', this._destroyState);

@@ -81,2 +98,17 @@ },

/**
* Clean up destroy event handler, remove any listeners on _stateModel
*
* @private
* @method _removeEventHandlers
*/
_removeEventHandlers() {
if(!this._stateModel) return;
this.undelegateStateEvents();
this._stateModel.stopListening();
this.off('destroy', this._destroyState);
},
/**
* Get the StateMixin StateModel class.

@@ -83,0 +115,0 @@ * Checks if the `StateModel` is a model class (the common case)

@@ -25,2 +25,3 @@ import App from '../../src/app';

beforeEach(function() {
this.sinon.spy(this.myApp, 'delegateStateEvents');
this.myApp.start();

@@ -33,2 +34,6 @@ });

it('should delegate state events', function() {
expect(this.myApp.delegateStateEvents).to.be.calledOnce;
});
it('should trigger start event', function() {

@@ -104,2 +109,48 @@ expect(this.startStub).to.have.been.calledOnce;

describe('when restarting the app', function() {
beforeEach(function() {
this.myApp.start({
state: { 'foo': 'bar' }
});
});
it('should be stopped', function() {
this.myApp.restart();
expect(this.stopStub).to.have.been.calledOnce;
});
it('should be started again', function() {
expect(this.startStub).to.have.been.calledOnce;
this.myApp.restart();
expect(this.startStub).to.have.been.calledTwice;
});
it('should maintain the app\'s previous state', function() {
expect(this.beforeStartStub).to.be.calledWith({ state: { foo: 'bar' } });
this.myApp.setState('foo', 'baz');
this.myApp.restart();
expect(this.beforeStartStub).to.be.calledWith({ state: { foo: 'baz' } });
});
it('should set isRestarting() during restart process', function() {
const newApp = App.extend({
onBeforeStop() {
expect(this.isRestarting()).to.equal(true);
}
});
this.myApp = new newApp();
this.myApp.start();
this.myApp.restart();
expect(this.myApp.isRestarting()).to.equal(false);
});
});
describe('when an application is yet to be destroyed', function() {

@@ -106,0 +157,0 @@ it('should have isDestroyed() to return false', function() {

@@ -49,4 +49,7 @@ import $ from 'jquery';

beforeEach(function() {
this.myApp = new this.MyApp({
region: new Marionette.Region({ el: $('<div>')[0] })
this.region = new Marionette.Region({ el: $('<div>')[0] });
this.myApp = new this.MyApp();
this.myApp.start({
region: this.region
});

@@ -56,3 +59,3 @@ });

it('should pass the region to App#setRegion', function() {
expect(this.myApp.setRegion.called);
expect(this.myApp.setRegion).to.be.calledOnce.and.calledWith(this.region);
});

@@ -84,58 +87,171 @@ });

this.myApp.start();
expect(this.spy.returned(this.myRegion)).to.be.true;
expect(this.spy).to.have.returned(this.myRegion);
});
it('should return the set region', function() {
const region = new Marionette.Region({ el: $('<div>')[0] });
expect(this.myApp.setRegion(region)).to.equal(region);
});
});
});
describe('#showChildView', function() {
describe('#getRegion', function() {
beforeEach(function() {
const MyApp = App.extend();
this.myRegion = new Marionette.Region({ el: $('<div>')[0] });
this.sinon.spy(MyApp.prototype, 'showChildView');
this.myApp = new App();
this.myApp = new MyApp({
region: new Marionette.Region({ el: $('<div>')[0] })
this.myApp.setRegion(this.myRegion);
this.view = new Marionette.View({
template: _.template('<div></div>'),
regions: { region: 'div' }
});
this.myApp.showView(this.view);
});
describe('when the app region has a view', function() {
describe('when not passing any arguments', function() {
it('should get the app\'s region', function() {
expect(this.myApp.getRegion()).to.equal(this.myRegion);
});
});
describe('when passing a region name', function() {
it('should get the app view\'s region', function() {
expect(this.myApp.getRegion('region')).to.equal(this.view.getRegion('region'));
});
});
});
describe('#setView', function() {
beforeEach(function() {
this.MyApp = App.extend();
this.sinon.spy(this.MyApp.prototype, 'setView');
});
describe('when passing a view to App#start', function() {
beforeEach(function() {
const myView = new Marionette.View({
regions: {
someRegion: 'div'
},
template: _.template('<div></div>')
this.view = new Marionette.View({ template: _.noop });
this.myApp = new this.MyApp();
this.myApp.start({
view: this.view
});
});
this.sinon.spy(myView, 'showChildView');
it('should pass the view to App#setView', function() {
expect(this.myApp.setView).to.be.calledOnce.and.calledWith(this.view);
});
});
this.myApp.showView(myView);
describe('when setting a view', function() {
beforeEach(function() {
this.myView = new Marionette.View({ template: _.noop });
this.view = new Marionette.View({ template: false });
const MyApp = this.MyApp.extend({
onBeforeStart: function() {
return this.getView();
}
});
this.myApp.showChildView('someRegion', this.view, 'foo');
this.spy = this.sinon.spy(MyApp.prototype, 'onBeforeStart');
this.myApp = new MyApp();
this.myApp.setView(this.myView);
});
it('should return the child view', function() {
expect(this.myApp.showChildView.returned(this.view)).to.be.true;
it('should attach the view to the app', function() {
expect(this.myApp.getView()).to.equal(this.myView);
});
it('should call showChildView on the app\'s view', function() {
expect(this.myApp.getView().showChildView).to.have.been.calledWith('someRegion', this.view, 'foo');
it('should make the view available during before:start', function() {
this.myApp.start();
expect(this.spy).to.have.returned(this.myView);
});
it('should return the set view', function() {
const view = new Marionette.View();
expect(this.myApp.setView(view)).to.equal(view);
});
});
});
describe('when the app region does not have a view', function() {
it('should return false', function() {
this.myApp.showChildView('someRegion', new Marionette.View());
expect(this.myApp.showChildView.returned(false)).to.be.true;
describe('#getView', function() {
beforeEach(function() {
this.view = new Marionette.View({ template: _.noop });
this.setView = new Marionette.View({ template: _.noop });
this.region = new Marionette.Region({ el: $('<div>')[0] });
this.myApp = new App();
this.myApp.start({ view: this.setView });
});
describe('when there is no app\'s region', function() {
it('should return the set view', function() {
expect(this.myApp.getView()).to.equal(this.setView);
});
});
describe('when a view is not shown in the app\'s region', function() {
it('should return the set view', function() {
this.myApp.setRegion(this.region);
expect(this.myApp.getView()).to.equal(this.setView);
});
});
describe('when a view is shown in the app\'s region', function() {
it('should return the shown view', function() {
this.myApp.setRegion(this.region);
this.myApp.showView(this.view);
expect(this.myApp.getView()).to.equal(this.view);
});
});
});
describe('#getChildView', function() {
describe('#showView', function() {
beforeEach(function() {
this.view = new Marionette.View({ template: _.noop });
this.setView = new Marionette.View({ template: _.noop });
this.region = new Marionette.Region({ el: $('<div>')[0] });
this.myApp = new App();
this.sinon.spy(this.region, 'show');
this.myApp.start({
view: this.setView,
region: this.region
});
});
describe('when no arguments are passed', function() {
it('should show the set view', function() {
this.myApp.showView();
expect(this.region.show).to.be.calledOnce.and.calledWith(this.setView);
});
it('should return the shown view', function() {
expect(this.myApp.showView()).to.equal(this.setView);
});
});
describe('when a view and arguments are passed', function() {
it('should show the view', function() {
this.myApp.showView(this.view, 'foo');
expect(this.region.show).to.be.calledOnce.and.calledWith(this.view, 'foo');
});
it('should return the shown view', function() {
expect(this.myApp.showView(this.view)).to.equal(this.view);
});
});
});
describe('#showChildView', function() {
beforeEach(function() {
const MyApp = App.extend();
this.sinon.spy(MyApp.prototype, 'getChildView');
this.sinon.spy(MyApp.prototype, 'showChildView');

@@ -145,41 +261,64 @@ this.myApp = new MyApp({

});
});
const myView = new Marionette.View({
regions: {
someRegion: 'div'
},
template: _.template('<div></div>')
});
describe('when the app region has a view', function() {
beforeEach(function() {
const myView = new Marionette.View({
regions: {
someRegion: 'div'
},
template: _.template('<div></div>')
});
this.sinon.spy(myView, 'showChildView');
this.sinon.spy(myView, 'getChildView');
this.myApp.showView(myView);
this.myApp.showView(myView);
this.view = new Marionette.View({ template: _.noop });
this.view = new Marionette.View({ template: false });
this.myApp.showChildView('someRegion', this.view, 'foo');
});
this.myApp.showChildView('someRegion', this.view);
it('should return the child view', function() {
expect(this.myApp.showChildView).to.have.returned(this.view);
});
this.myApp.getChildView('someRegion');
it('should call showChildView on the app\'s view', function() {
expect(this.myApp.getView().showChildView).to.have.been.calledWith('someRegion', this.view, 'foo');
});
});
describe('#getChildView', function() {
beforeEach(function() {
const MyApp = App.extend();
this.sinon.spy(MyApp.prototype, 'getChildView');
this.myApp = new MyApp({
region: new Marionette.Region({ el: $('<div>')[0] })
});
it('should return the child view', function() {
expect(this.myApp.getChildView.returned(this.view)).to.be.true;
const myView = new Marionette.View({
regions: {
someRegion: 'div'
},
template: _.template('<div></div>')
});
it('should call showChildView on the app\'s view', function() {
expect(this.myApp.getView().getChildView).to.have.been.calledWith('someRegion');
});
this.sinon.spy(myView, 'getChildView');
this.myApp.showView(myView);
this.view = new Marionette.View({ template: _.noop });
this.myApp.showChildView('someRegion', this.view);
this.myApp.getChildView('someRegion');
});
describe('when the app region does not have a view', function() {
it('should return false', function() {
this.myApp.getChildView('someRegion');
expect(this.myApp.getChildView.returned(false)).to.be.true;
});
it('should return the child view', function() {
expect(this.myApp.getChildView).to.have.returned(this.view);
});
it('should call showChildView on the app\'s view', function() {
expect(this.myApp.getView().getChildView).to.have.been.calledWith('someRegion');
});
});
});

@@ -192,2 +192,10 @@ describe('Marionette.Toolkit.Component', function() {

describe('when instantiating a component', function() {
it('should delegate state events', function() {
this.MyComponent = Marionette.Toolkit.Component.extend();
this.sinon.spy(this.MyComponent.prototype, 'delegateStateEvents');
this.myComponent = new this.MyComponent();
expect(this.myComponent.delegateStateEvents).to.be.calledOnce;
});
describe('with state options', function() {

@@ -207,23 +215,2 @@ it('should initialize stateModel with passed in state', function() {

describe('with a customized viewEventPrefix', function() {
it('should trigger the correct action as defined', function() {
this.MyComponent = Marionette.Toolkit.Component.extend({
viewEventPrefix: 'some:prefix',
viewOptions: {
template: false
},
testRender: false
});
this.myComponent = new this.MyComponent({});
this.myComponent.showIn(this.myRegion);
this.myComponent.on('some:prefix:render', function() {
this.testRender = true;
});
this.myComponent.currentView.render();
expect(this.myComponent.testRender).to.equal(true);
});
});
describe('with defined viewOptions', function() {

@@ -230,0 +217,0 @@ beforeEach(function() {

@@ -346,19 +346,19 @@ describe('ChildAppMixin', function() {

it('should start specified childApp', function() {
this.myApp.startChildApp('cA1');
this.myChildApp = this.myApp.startChildApp('cA1');
expect(this.myApp.getChildApp('cA1').isRunning()).to.be.true;
expect(this.myChildApp.isRunning()).to.be.true;
});
it('should start childApp with options', function() {
this.myApp.startChildApp('cA2', { region: 'regionName' });
this.myChildApp = this.myApp.startChildApp('cA2', { region: 'regionName' });
expect(this.myApp.getChildApp('cA2').getOption('region')).to.eq('regionName');
expect(this.myChildApp.getOption('region')).to.eq('regionName');
});
it('should return parentApp instance', function() {
it('should return childApp instance', function() {
const spy = sinon.spy(this.myApp, 'startChildApp');
this.myApp.startChildApp('cA1');
this.myChildApp = this.myApp.startChildApp('cA1');
expect(spy.returned(this.myApp)).to.be.true;
expect(spy.returned(this.myChildApp)).to.be.true;
});

@@ -376,3 +376,3 @@ });

this.myApp = new Marionette.Toolkit.App({ childApps: childApps });
this.myApp.startChildApp('cA1');
this.myChildApp = this.myApp.startChildApp('cA1');
});

@@ -383,3 +383,3 @@

expect(this.myApp.getChildApp('cA1').isRunning()).to.be.false;
expect(this.myChildApp.isRunning()).to.be.false;
});

@@ -392,5 +392,5 @@

expect(spy.returned(this.myApp)).to.be.true;
expect(spy.returned(this.myChildApp)).to.be.true;
});
});
});

@@ -135,2 +135,37 @@ import Marionette from 'backbone.marionette';

describe('when binding/unbinding state events', function() {
describe('when undelegateStateEvents is called', function() {
it('should call unbindEvents', function() {
this.sinon.spy(this.myStateClass, 'unbindEvents');
this.myStateClass.undelegateStateEvents();
expect(this.myStateClass.unbindEvents).to.have.been.calledOnce;
});
it('should return the _stateModel', function() {
expect(this.myStateClass.getState()).to.deep.equal(this.myStateClass._stateModel);
});
});
describe('when delegateStateEvents is called', function() {
it('should call undelegateStateEvents', function() {
this.sinon.spy(this.myStateClass, 'undelegateStateEvents');
this.myStateClass.delegateStateEvents();
expect(this.myStateClass.undelegateStateEvents).to.have.been.calledOnce;
});
it('should call bindEvents', function() {
this.sinon.spy(this.myStateClass, 'bindEvents');
this.myStateClass.delegateStateEvents();
expect(this.myStateClass.bindEvents).to.have.been.calledOnce;
});
it('should return the _stateModel', function() {
expect(this.myStateClass.getState()).to.deep.equal(this.myStateClass._stateModel);
});
});
});
// SETTING StateModel

@@ -231,2 +266,3 @@ describe('when defining StateModel as a function that returns a model class', function() {

this.sinon.spy(this.myStateClass, 'off');
this.sinon.spy(this.myStateClass, 'delegateStateEvents');

@@ -236,3 +272,4 @@ this.myStateClass.initState();

expect(orgStateModel.stopListening).to.have.been.calledOnce;
expect(this.myStateClass.unbindEvents).to.have.been.calledOnce;
expect(this.myStateClass.delegateStateEvents).to.have.been.calledOnce;
expect(this.myStateClass.unbindEvents).to.have.been.calledTwice;
expect(this.myStateClass.off).to.have.been.calledWith('destroy', this.myStateClass._destroyState);

@@ -239,0 +276,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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc