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
4
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 0.4.2 to 1.0.0

.babelrc

8

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

@@ -36,6 +36,6 @@ "main": "./dist/marionette.toolkit.js",

"dependencies": {
"backbone.marionette": "^2.1.0",
"backbone": "1.0.0 - 1.1.2",
"underscore": "1.4.4 - 1.8.2"
"backbone.marionette": "^2.4.5",
"backbone": "^1.3.2",
"underscore": "^1.8.3"
}
}

@@ -0,1 +1,35 @@

#### v1.0.0
* `StateMixin`:
* Replaces `StateClass` and is now a POJO instead of a `Marionette.object`
* Can now be mixed into any `Marionette.Object` or `Marionette.View`
* `stateDefaults` has been removed in favor of state being passed in `options` hash
* `initState` adds ability to reinitialize state at any point during the life-time of `Marionette.object` / `Marionette.View`
* `resetStateDefaults` adds ability to reset state defined in defaults
* `destroyState` has been replaced by `_destoryState` to privatize state deletion
* `ChildAppsMixin`:
* Now handles the adding and removing of childApps
* Adds functionality to share options with all children via `childAppsOptions`
* `EventListenerMixin` now handles `App` event-listener functionality
* `Marionette.Toolkit.MixinState` is a utiltiy function created to make it easy to mix `StateMixin` into any `Marionette.object` or `Marionette.View`
* `AbstractApp` has been removed and functionality moved into `App`
* `App`:
* Now extends Marionette.Object and not `StateClass` as `AbstractApp` previously did
* It now mixes in `StateMixin`, `ChildAppsMixin`, and `EventListenerMixin`
* An app can now be restarted and have it's state reinitialized via `restart`
* `_isDestroyed` is now the last action of `destroy` method to align with Marionette v3
* `Component`:
* Now extends a `Marionette.Object` and not `StateClass`
* It now mixes in `StateMixin`
* Stop passing entire `stateModel` to `currentView` in favor of passing only `attributes`
* Improve `ES6` usage
* Dependency:
* Change `dependencies` to `peerdependencies`
* Update to `Node` v4 and several other updates
* Build process updates:
* Remove `_buildPackages` and `NPM` deploy functionality as project will no longer publish packages individually
* Replace `Browserify` build process with `Rollup`
* Move from `JSHint` to `ESLint`
* Test and Documentation updates
#### v0.4.2

@@ -2,0 +36,0 @@

/**
* marionette.toolkit - A collection of opinionated Backbone.Marionette extensions for large scale application architecture.
* @version v0.4.1
* @version v1.0.0
* @link https://github.com/RoundingWellOS/marionette.toolkit
* @license MIT
*/
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory(require("backbone.marionette"), require("underscore"), require("backbone")) : typeof define === "function" && define.amd ? define(["backbone.marionette", "underscore", "backbone"], factory) : global.Marionette.Toolkit = factory(global.Marionette, global._, global.Backbone);
})(this, function (Marionette, _, Backbone) {
"use strict";
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('backbone.marionette'), require('underscore'), require('backbone')) :
typeof define === 'function' && define.amd ? define(['backbone.marionette', 'underscore', 'backbone'], factory) :
(global.Marionette = global.Marionette || {}, global.Marionette.Toolkit = factory(global.Marionette,global._,global.Backbone));
}(this, function (Marionette,_$1,Backbone) { 'use strict';
var StateClass = Marionette.Object.extend({
Marionette = 'default' in Marionette ? Marionette['default'] : Marionette;
_$1 = 'default' in _$1 ? _$1['default'] : _$1;
Backbone = 'default' in Backbone ? Backbone['default'] : Backbone;
var ClassOptions = ['StateModel', 'stateEvents'];
/**
* 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.
*
* @mixin
*/
var StateMixin = {
/**

@@ -23,393 +37,155 @@ * The model class for _stateModel.

* @public
* @constructs StateClass
* @param {Object} [options] - Settings for the stateClass.
* @param {Object} [options.stateEvents] - Event hash bound from _stateModel to stateClass.
* @method initState
* @param {Object} [options] - Settings for the StateMixin.
* @param {Object} [options.stateEvents] - Event hash bound from _stateModel to StateMixin.
* @param {Backbone.Model} [options.StateModel] - Model class for _stateModel.
*/
constructor: function constructor(options) {
options = _.extend({}, options);
initState: function initState() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
// Make defaults available to this
_.extend(this, _.pick(options, ["StateModel", "stateEvents", "stateDefaults"]));
this.mergeOptions(options, ClassOptions);
// Remove event handlers from previous state
this._removeEventHandlers();
var StateModel = this._getStateModel(options);
this._stateModel = new StateModel(_.result(this, "stateDefaults"));
this._stateModel = new StateModel(options.state);
// Bind events from the _stateModel defined in stateEvents hash
this.bindEntityEvents(this._stateModel, _.result(this, "stateEvents"));
this._setEventHandlers();
Marionette.Object.call(this, options);
return this;
},
/**
* Get the StateClass StateModel class.
* Checks if the `StateModel` is a model class (the common case)
* Then check if it's a function (which we assume that returns a model class)
* Unbind all entity events and remove any listeners on _stateModel
* Clean up destroy event handler
*
* @private
* @method _getStateModel
* @param {Object} [options] - Options that can be used to determine the StateModel.
* @memberOf StateClass
* @returns {Backbone.Model}
* @method _removeEventHandlers
*/
_getStateModel: function _getStateModel(options) {
var StateModel = this.getOption("StateModel");
_removeEventHandlers: function _removeEventHandlers() {
if (!this._stateModel) return;
if (StateModel.prototype instanceof Backbone.Model || StateModel === Backbone.Model) {
return StateModel;
} else if (_.isFunction(StateModel)) {
return StateModel.call(this, options);
} else {
throw new Marionette.Error({
name: "InvalidStateModelError",
message: "\"StateModel\" must be a model class or a function that returns a model class"
});
}
this.unbindEntityEvents(this._stateModel);
this._stateModel.stopListening();
this.off('destroy', this._destroyState);
},
/**
* Set a property on the _stateModel.
*
* @public
* @method setState
* @memberOf StateClass
* @param {String|Object} key - Attribute name or Hash of any number of key value pairs.
* @param {*} [value] - Attribute value if key is String, replaces options param otherwise.
* @param {Object} [options] - Backbone.Model options.
* @returns {Backbone.Model} - The _stateModel
*/
setState: function setState() {
return this._stateModel.set.apply(this._stateModel, arguments);
},
/**
* Get a property from the _stateModel, or return the _stateModel
* Bind events from the _stateModel defined in stateEvents hash
* Setup destroy event handle
*
* @public
* @method getState
* @memberOf StateClass
* @param {String} [attr] - Attribute name of stateModel.
* @returns {Backbone.Model|*} - The _stateModel or the attribute value of the _stateModel
* @private
* @method _setEventHandlers
*/
getState: function getState(attr) {
if (!attr) {
return this._stateModel;
}
_setEventHandlers: function _setEventHandlers() {
this.bindEntityEvents(this._stateModel, _$1.result(this, 'stateEvents'));
return this._stateModel.get.apply(this._stateModel, arguments);
this.on('destroy', this._destroyState);
},
/**
* Destroy the stateClass and clean up any listeners on the _stateModel.
*
* @public
* @method destroy
* @memberOf StateClass
*/
destroy: function destroy() {
this._stateModel.stopListening();
Marionette.Object.prototype.destroy.apply(this, arguments);
}
});
var state_class = StateClass;
var AbstractApp = state_class.extend({
/**
* Internal flag indiciate when `App` has started but has not yet stopped.
* Get the StateMixin StateModel class.
* Checks if the `StateModel` is a model class (the common case)
* Then check if it's a function (which we assume that returns a model class)
*
* @private
* @type {Boolean}
* @default false
* @method _getStateModel
* @param {Object} [options] - Options that can be used to determine the StateModel.
* @returns {Backbone.Model}
*/
_isRunning: false,
/**
* Internal flag indiciate when `App` has been destroyed
*
* @private
* @type {Boolean}
* @default false
*/
_isDestroyed: false,
/**
* Set to true if a parent `App` should not be able to destroy this `App`.
*
* @type {Boolean|Function}
* @default false
*/
preventDestroy: false,
/**
* Set to true if `App` should be started after it is initialized.
*
* @type {Boolean|Function}
* @default false
*/
startAfterInitialized: false,
/**
* Set to true if `App` should be started after its parent starts.
*
* @type {Boolean|Function}
* @default false
*/
startWithParent: false,
/**
* Set to false if `App` should not stop after its parent stops.
*
* @type {Boolean|Function}
* @default true
*/
stopWithParent: true,
/**
* @public
* @constructs AbstractApp
* @param {Object} [options] - Settings for the App.
* @param {Boolean} [options.startWithParent]
* @param {Boolean} [options.stopWithParent]
* @param {Boolean} [options.startAfterInitialized]
* @param {Boolean} [options.preventDestroy]
*/
constructor: function constructor(options) {
options = _.extend({}, options);
_.bindAll(this, "start", "stop");
var pickOptions = ["startWithParent", "stopWithParent", "startAfterInitialized", "preventDestroy"];
_.extend(this, _.pick(options, pickOptions));
// Will call initialize
state_class.call(this, options);
if (_.result(this, "startAfterInitialized")) {
this.start(options);
_getStateModel: function _getStateModel(options) {
if (this.StateModel.prototype instanceof Backbone.Model || this.StateModel === Backbone.Model) {
return this.StateModel;
} else if (_$1.isFunction(this.StateModel)) {
return this.StateModel.call(this, options);
}
},
/**
* Internal helper to verify if `App` has been destroyed
*
* @private
* @method _ensureAppIsIntact
* @memberOf AbstractApp
* @throws AppDestroyedError - Thrown if `App` has already been destroyed
*/
_ensureAppIsIntact: function _ensureAppIsIntact() {
if (this._isDestroyed) {
throw new Marionette.Error({
name: "AppDestroyedError",
message: "App has already been destroyed and cannot be used."
});
}
throw new Marionette.Error({
name: 'InvalidStateModelError',
message: '"StateModel" must be a model class or a function that returns a model class'
});
},
/**
* Gets the value of internal `_isRunning` flag
*
* @public
* @method isRunning
* @memberOf AbstractApp
* @returns {Boolean}
*/
isRunning: function isRunning() {
return this._isRunning;
},
/**
* Sets the app lifecycle to running.
* Set a property on the _stateModel.
*
* @public
* @method start
* @memberOf AbstractApp
* @param {Object} [options] - Settings for the App passed through to events
* @event AbstractApp#before:start - passes options
* @returns {AbstractApp}
* @method setState
* @param {String|Object} key - Attribute name or Hash of any number of key value pairs.
* @param {*} [value] - Attribute value if key is String, replaces options param otherwise.
* @param {Object} [options] - Backbone.Model options.
* @returns {Backbone.Model} - The _stateModel
*/
start: function start(options) {
this._ensureAppIsIntact();
if (this._isRunning) {
return this;
}
this.triggerMethod("before:start", options);
this._isRunning = true;
this.triggerStart(options);
return this;
setState: function setState() {
return this._stateModel.set.apply(this._stateModel, arguments);
},
/**
* Triggers start event.
* Override to introduce async start
*
* @public
* @method triggerStart
* @memberOf AbstractApp
* @param {Object} [options] - Settings for the App passed through to events
* @event AbstractApp#start - passes options
* @returns
*/
triggerStart: function triggerStart(options) {
this.triggerMethod("start", options);
},
/**
* Sets the app lifecycle to not running.
* Removes any listeners added during the running state
* Reset _stateModel to defined defaults
*
* @public
* @method stop
* @memberOf AbstractApp
* @param {Object} [options] - Settings for the App passed through to events
* @event AbstractApp#before:stop - passes options
* @event AbstractApp#stop - passes options
* @returns {AbstractApp}
* @method resetStateDefaults
* @param {Object} [newState] - Hash of any number of key value pairs.
* @returns {Backbone.Model|*} - The _stateModel or the attribute value of the _stateModel
*/
stop: function stop(options) {
if (!this._isRunning) {
return this;
}
resetStateDefaults: function resetStateDefaults() {
var defaults = _$1.result(this._stateModel, 'defaults');
this.triggerMethod("before:stop", options);
this._isRunning = false;
this.triggerMethod("stop", options);
this._stopRunningListeners();
this._stopRunningEvents();
return this;
return this._stateModel.set(defaults);
},
/**
* Gets the value of internal `_isDestroyed` flag
*
* @public
* @method isDestroyed
* @memberOf AbstractApp
* @returns {Boolean}
*/
isDestroyed: function isDestroyed() {
return this._isDestroyed;
},
/**
* Stops the `App` and sets it destroyed.
* Get a property from the _stateModel, or return the _stateModel
*
* @public
* @method destroy
* @memberOf AbstractApp
* @method getState
* @param {String} [attr] - Attribute name of stateModel.
* @returns {Backbone.Model|*} - The _stateModel or the attribute value of the _stateModel
*/
destroy: function destroy() {
if (this._isDestroyed) {
return;
getState: function getState(attr) {
if (!attr) {
return this._stateModel;
}
this._isDestroyed = true;
this.stop();
state_class.prototype.destroy.apply(this, arguments);
return this._stateModel.get.apply(this._stateModel, arguments);
},
/**
* Internal method to stop any registered events.
*
* @private
* @method _stopRunningEvents
* @memberOf AbstractApp
*/
_stopRunningEvents: function _stopRunningEvents() {
_.each(this._runningEvents, function (args) {
this.off.apply(this, args);
}, this);
},
/**
* Internal method to stop any registered listeners.
* Clean up any listeners on the _stateModel.
*
* @private
* @method _stopRunningListeners
* @memberOf AbstractApp
* @method _destroyState
*/
_stopRunningListeners: function _stopRunningListeners() {
_.each(this._runningListeningTo, function (args) {
this.stopListening.apply(this, args);
}, this);
},
/**
* Overrides `Backbone.Event.on()`
* If this `App` is running it will register the event for removal `onStop`
*
* @public
* @method on
* @memberOf AbstractApp
* @returns {AbstractApp}
*/
on: function on() {
if (this._isRunning) {
this._runningEvents = this._runningEvents || [];
this._runningEvents.push(arguments);
}
return state_class.prototype.on.apply(this, arguments);
},
/**
* Overrides `Backbone.Event.listenTo()`
* If this `App` is running it will register the listener for removal `onStop`
*
* @public
* @method listenTo
* @memberOf AbstractApp
* @returns {AbstractApp}
*/
listenTo: function listenTo() {
if (this._isRunning) {
this._runningListeningTo = this._runningListeningTo || [];
this._runningListeningTo.push(arguments);
}
return state_class.prototype.listenTo.apply(this, arguments);
},
/**
* Overrides `Backbone.Event.listenToOnce()`
* If this `App` is running it will register the listener for removal `onStop`
*
* @public
* @method listenToOnce
* @memberOf AbstractApp
* @returns {AbstractApp}
*/
listenToOnce: function listenToOnce() {
if (this._isRunning) {
this._runningListeningTo = this._runningListeningTo || [];
this._runningListeningTo.push(arguments);
}
return state_class.prototype.listenToOnce.apply(this, arguments);
_destroyState: function _destroyState() {
this._stateModel.stopListening();
}
});
};
var abstract_app = AbstractApp;
var ClassOptions$2 = ['childApps', 'childAppOptions'];
var App = abstract_app.extend({
/**
* This provides methods used for "App Manager" functionality - the adding and removing child `App`s. It's not meant to
* be used directly.
*
* @mixin
*/
var ChildAppsMixin = {
/**
* @public
* @constructs App
* @param {Object} [options] - Settings for the App.
* @private
* @method _initChildApps
* @constructs ChildApps
* @param {Object} [options] - Settings for the ChildApps.
* @param {Object} [options.childApps] - Hash for setting up child apps.
* @param {Object} [options.childAppOptions] - Hash of options passed to every child app.
*

@@ -427,35 +203,14 @@ * ```js

*/
constructor: function constructor(options) {
options = _.extend({}, options);
_initChildApps: function _initChildApps() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
this._childApps = {};
_.extend(this, _.pick(options, ["childApps"]));
this.mergeOptions(options, ClassOptions$2);
this._initChildApps(options);
// The child apps should be handled while the app is running;
// After start, before stop, and before destroy.
this.on({
start: this._startChildApps,
"before:stop": this._stopChildApps,
"before:destroy": this._destroyChildApps
});
abstract_app.call(this, options);
},
/**
* Initializes `childApps` option
*
* @private
* @method _initChildApps
* @memberOf App
*/
_initChildApps: function _initChildApps(options) {
var childApps = this.childApps;
if (childApps) {
if (_.isFunction(childApps)) {
if (_$1.isFunction(childApps)) {
childApps = childApps.call(this, options);

@@ -466,5 +221,24 @@ }

}
this._initListeners();
},
/**
* 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
});
},
/**
* Starts `childApps` if allowed by child

@@ -474,7 +248,6 @@ *

* @method _startChildApps
* @memberOf App
*/
_startChildApps: function _startChildApps() {
_.each(this._childApps, function (childApp) {
if (_.result(childApp, "startWithParent")) {
_$1.each(this._childApps, function (childApp) {
if (_$1.result(childApp, 'startWithParent')) {
childApp.start();

@@ -485,2 +258,3 @@ }

/**

@@ -491,7 +265,6 @@ * Stops `childApps` if allowed by child

* @method _stopChildApps
* @memberOf App
*/
_stopChildApps: function _stopChildApps() {
_.each(this._childApps, function (childApp) {
if (_.result(childApp, "stopWithParent")) {
_$1.each(this._childApps, function (childApp) {
if (_$1.result(childApp, 'stopWithParent')) {
childApp.stop();

@@ -502,2 +275,3 @@ }

/**

@@ -508,7 +282,6 @@ * Destroys `childApps` if allowed by child

* @method _destroyChildApps
* @memberOf App
*/
_destroyChildApps: function _destroyChildApps() {
_.each(this._childApps, function (childApp) {
if (!_.result(childApp, "preventDestroy")) {
_$1.each(this._childApps, function (childApp) {
if (!_$1.result(childApp, 'preventDestroy')) {
childApp.destroy();

@@ -519,2 +292,3 @@ }

/**

@@ -525,3 +299,2 @@ * Internal helper to instantiate and `App` from on `Object`

* @method _buildAppFromObject
* @memberOf App
* @param {Object} appConfig - `AppClass` and any other option for the `App`

@@ -532,3 +305,3 @@ * @returns {App}

var AppClass = appConfig.AppClass;
var options = _.omit(appConfig, "AppClass");
var options = _$1.omit(appConfig, 'AppClass');

@@ -538,2 +311,3 @@ return this.buildApp(AppClass, options);

/**

@@ -544,3 +318,2 @@ * Helper for building an App and return it

* @method _buildApp
* @memberOf App
* @param {App} AppClass - An App Class

@@ -552,6 +325,6 @@ * @param {Object} AppClass - Optionally passed as an appConfig Object

_buildApp: function _buildApp(AppClass, options) {
if (_.isFunction(AppClass)) {
if (_$1.isFunction(AppClass)) {
return this.buildApp(AppClass, options);
}
if (_.isObject(AppClass)) {
if (_$1.isObject(AppClass)) {
return this._buildAppFromObject(AppClass);

@@ -561,2 +334,3 @@ }

/**

@@ -568,3 +342,2 @@ * Build an App and return it

* @method buildApp
* @memberOf App
* @param {App} [AppClass] - An App Class

@@ -575,5 +348,9 @@ * @param {Object} [options] - options for the AppClass

buildApp: function buildApp(AppClass, options) {
// options on childApp definition supersede childAppOptions
options = _$1.extend({}, this.childAppOptions, options);
return new AppClass(options);
},
/**

@@ -584,3 +361,2 @@ * Internal helper to verify `appName` is unique and not in use

* @method _ensureAppIsUnique
* @memberOf App
* @param {String} appName - Name of app to test

@@ -592,4 +368,4 @@ * @throws DuplicateChildAppError - Thrown if `App` already has an `appName` registered

throw new Marionette.Error({
name: "DuplicateChildAppError",
message: "A child App with name \"" + appName + "\" has already been added."
name: 'DuplicateChildAppError',
message: 'A child App with name "' + appName + '" has already been added.'
});

@@ -599,2 +375,3 @@ }

/**

@@ -605,7 +382,6 @@ * Add child `App`s to this `App`

* @method addChildApps
* @memberOf App
* @param {Object} childApps - Hash of names and `AppClass` or `appConfig`
*/
addChildApps: function addChildApps(childApps) {
_.each(childApps, function (childApp, appName) {
_$1.each(childApps, function (childApp, appName) {
this.addChildApp(appName, childApp);

@@ -615,2 +391,3 @@ }, this);

/**

@@ -622,3 +399,2 @@ * Build's childApp and registers it with this App

* @method addChildApp
* @memberOf App
* @param {String} appName - Name of App to register

@@ -638,4 +414,4 @@ * @param {App} AppClass - An App Class

throw new Marionette.Error({
name: "AddChildAppError",
message: "App build failed. Incorrect configuration."
name: 'AddChildAppError',
message: 'App build failed. Incorrect configuration.'
});

@@ -649,5 +425,5 @@ }

// When the app is destroyed remove the cached app.
childApp.on("destroy", _.partial(this._removeChildApp, appName), this);
childApp.on('destroy', _$1.partial(this._removeChildApp, appName), this);
if (this.isRunning() && _.result(childApp, "startWithParent")) {
if (this.isRunning() && _$1.result(childApp, 'startWithParent')) {
childApp.start();

@@ -659,2 +435,3 @@ }

/**

@@ -665,3 +442,2 @@ * Returns registered child `App`s name

* @method getName
* @memberOf App
* @returns {String}

@@ -673,2 +449,3 @@ */

/**

@@ -679,9 +456,9 @@ * Returns registered child `App`s array

* @method getChildApps
* @memberOf App
* @returns {Array}
*/
getChildApps: function getChildApps() {
return _.clone(this._childApps);
return _$1.clone(this._childApps);
},
/**

@@ -692,3 +469,2 @@ * Returns registered child `App`

* @method getChildApp
* @memberOf App
* @param {String} appName - Name of App to retrieve

@@ -701,2 +477,3 @@ * @returns {App}

/**

@@ -707,3 +484,2 @@ * Internal helper. Unregisters child `App`

* @method _removeChildApp
* @memberOf App
* @param {String} appName - Name of App to unregister

@@ -717,2 +493,3 @@ * @returns {App}

/**

@@ -724,3 +501,2 @@ * Removes all childApps and returns them.

* @method removeChildApps
* @memberOf App
* @returns {Array}

@@ -731,3 +507,3 @@ */

_.each(this._childApps, function (childApp, appName) {
_$1.each(this._childApps, function (childApp, appName) {
this.removeChildApp(appName);

@@ -739,2 +515,3 @@ }, this);

/**

@@ -746,3 +523,2 @@ * Destroys or removes registered child `App` by name

* @method removeChildApp
* @memberOf App
* @param {String} appName - Name of App to destroy

@@ -753,3 +529,3 @@ * @param {Object} [options.preventDestroy] - Flag to remove but prevent App destroy

removeChildApp: function removeChildApp(appName, options) {
options = _.extend({}, options);
options = _$1.extend({}, options);

@@ -763,3 +539,3 @@ var childApp = this.getChildApp(appName);

// if preventDestroy simply unregister the child app
if (options.preventDestroy || _.result(childApp, "preventDestroy")) {
if (options.preventDestroy || _$1.result(childApp, 'preventDestroy')) {
this._removeChildApp(appName);

@@ -772,8 +548,350 @@ } else {

}
};
/**
* This provides methods used for registering events while App is running and cleans them up at `onStop`. It's not meant to
* be used directly.
*
* @mixin
*/
var EventListenersMixin = {
/**
* Internal method to stop any registered events.
*
* @private
* @method _stopRunningEvents
*/
_stopRunningEvents: function _stopRunningEvents() {
_$1.each(this._runningEvents, function (args) {
this.off.apply(this, args);
}, this);
},
/**
* Internal method to stop any registered listeners.
*
* @private
* @method _stopRunningListeners
*/
_stopRunningListeners: function _stopRunningListeners() {
_$1.each(this._runningListeningTo, function (args) {
this.stopListening.apply(this, args);
}, this);
},
/**
* Overrides `Backbone.Event.on()`
* If this `App` is running it will register the event for removal `onStop`
*
* @public
* @method on
* @returns {EventListeners}
*/
on: function on() {
if (this._isRunning) {
this._runningEvents = this._runningEvents || [];
this._runningEvents.push(arguments);
}
return Marionette.Object.prototype.on.apply(this, arguments);
},
/**
* Overrides `Backbone.Event.listenTo()`
* If this `App` is running it will register the listener for removal `onStop`
*
* @public
* @method listenTo
* @returns {EventListeners}
*/
listenTo: function listenTo() {
if (this._isRunning) {
this._runningListeningTo = this._runningListeningTo || [];
this._runningListeningTo.push(arguments);
}
return Marionette.Object.prototype.listenTo.apply(this, arguments);
},
/**
* Overrides `Backbone.Event.listenToOnce()`
* If this `App` is running it will register the listener for removal `onStop`
*
* @public
* @method listenToOnce
* @returns {EventListeners}
*/
listenToOnce: function listenToOnce() {
if (this._isRunning) {
this._runningListeningTo = this._runningListeningTo || [];
this._runningListeningTo.push(arguments);
}
return Marionette.Object.prototype.listenToOnce.apply(this, arguments);
}
};
var ClassOptions$1 = ['startWithParent', 'stopWithParent', 'startAfterInitialized', 'preventDestroy'];
/**
* Marionette.Object with an `initialize` / `start` / `stop` / `destroy` lifecycle.
*
* @public
* @class App
* @memberOf Toolkit
* @memberOf Marionette
*/
var App = Marionette.Object.extend({
/**
* Internal flag indiciate when `App` has started but has not yet stopped.
*
* @private
* @type {Boolean}
* @default false
*/
_isRunning: false,
/**
* Internal flag indiciate when `App` has been destroyed
*
* @private
* @type {Boolean}
* @default false
*/
_isDestroyed: false,
/**
* Set to true if a parent `App` should not be able to destroy this `App`.
*
* @type {Boolean|Function}
* @default false
*/
preventDestroy: false,
/**
* Set to true if `App` should be started after it is initialized.
*
* @type {Boolean|Function}
* @default false
*/
startAfterInitialized: false,
/**
* Set to true if `App` should be started after its parent starts.
*
* @type {Boolean|Function}
* @default false
*/
startWithParent: false,
/**
* Set to false if `App` should not stop after its parent stops.
*
* @type {Boolean|Function}
* @default true
*/
stopWithParent: true,
/**
* @public
* @constructs App
* @param {Object} [options] - Settings for the App.
* @param {Boolean} [options.startWithParent]
* @param {Boolean} [options.stopWithParent]
* @param {Boolean} [options.startAfterInitialized]
* @param {Boolean} [options.preventDestroy]
* @param {Object} [options.state] - Attributes to set on the state model.
*/
constructor: function constructor() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
_$1.bindAll(this, 'start', 'stop');
this.mergeOptions(options, ClassOptions$1);
this.initState(options);
this._initChildApps(options);
Marionette.Object.call(this, options);
if (_$1.result(this, 'startAfterInitialized')) {
this.start(options);
}
},
/**
* Internal helper to verify if `App` has been destroyed
*
* @private
* @method _ensureAppIsIntact
* @memberOf App
* @throws AppDestroyedError - Thrown if `App` has already been destroyed
*/
_ensureAppIsIntact: function _ensureAppIsIntact() {
if (this._isDestroyed) {
throw new Marionette.Error({
name: 'AppDestroyedError',
message: 'App has already been destroyed and cannot be used.'
});
}
},
/**
* Gets the value of internal `_isRunning` flag
*
* @public
* @method isRunning
* @memberOf App
* @returns {Boolean}
*/
isRunning: function isRunning() {
return this._isRunning;
},
/**
* Sets the app lifecycle to running.
*
* @public
* @method start
* @memberOf App
* @param {Object} [options] - Settings for the App passed through to events
* @event App#before:start - passes options
* @returns {App}
*/
start: function start(options) {
this._ensureAppIsIntact();
if (this._isRunning) {
return this;
}
this.triggerMethod('before:start', options);
this._isRunning = true;
this.triggerStart(options);
return this;
},
/**
* Triggers start event.
* Override to introduce async start
*
* @public
* @method triggerStart
* @memberOf App
* @param {Object} [options] - Settings for the App passed through to events
* @event App#start - passes options
* @returns
*/
triggerStart: function triggerStart(options) {
this.triggerMethod('start', options);
},
/**
* "Restarts the app" by first stoping app, reinitializing state, and then starting the app again
*
*
* @public
* @method restart
* @memberOf App
* @param {Object} [options] - Settings for the App passed through to events
* @returns {App}
*/
restart: function restart(options) {
this.stop(options);
this.initState(options);
this.start(options);
return this;
},
/**
* Sets the app lifecycle to not running.
* Removes any listeners added during the running state
*
* @public
* @method stop
* @memberOf App
* @param {Object} [options] - Settings for the App passed through to events
* @event App#before:stop - passes options
* @event App#stop - passes options
* @returns {App}
*/
stop: function stop(options) {
if (!this._isRunning) {
return this;
}
this.triggerMethod('before:stop', options);
this._isRunning = false;
this.triggerMethod('stop', options);
this._stopRunningListeners();
this._stopRunningEvents();
return this;
},
/**
* Gets the value of internal `_isDestroyed` flag
*
* @public
* @method isDestroyed
* @memberOf App
* @returns {Boolean}
*/
isDestroyed: function isDestroyed() {
return this._isDestroyed;
},
/**
* Stops the `App` and sets it destroyed.
*
* @public
* @method destroy
* @memberOf App
*/
destroy: function destroy() {
if (this._isDestroyed) {
return;
}
this.stop();
Marionette.Object.prototype.destroy.apply(this, arguments);
this._isDestroyed = true;
}
});
var app = App;
_$1.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin);
var Component = state_class.extend({
var ClassOpions = ['ViewClass', 'viewEventPrefix', 'viewOptions', 'region'];
/**
* Reusable Marionette.Object with View management boilerplate
*
* @public
* @class Component
* @memberOf Toolkit
* @memberOf Marionette
*/
var Component = Marionette.Object.extend({

@@ -793,3 +911,3 @@ /**

*/
viewEventPrefix: "view",
viewEventPrefix: 'view',

@@ -806,4 +924,4 @@ /**

* @constructs Component
* @param {Object} [stateAttrs] - Attributes to set on the state model.
* @param {Object} [options] - Settings for the component.
* @param {Object} [options.state] - Attributes to set on the state model.
* @param {Mn.ItemView|Mn.CollectionView|Mn.CompositeView|Mn.LayoutView=} [options.ViewClass]

@@ -816,13 +934,14 @@ * - The view class to be managed.

*/
constructor: function constructor(stateAttrs, options) {
options = _.extend({}, options);
constructor: function constructor() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
// Make defaults available to this
_.extend(this, _.pick(options, ["viewEventPrefix", "ViewClass", "viewOptions", "region"]));
this.mergeOptions(options, ClassOpions);
state_class.call(this, options);
this.initState(options);
this._setStateDefaults(stateAttrs);
Marionette.Object.call(this, options);
},
/**

@@ -839,15 +958,2 @@ * Internal flag to determine if the component should destroy.

/**
* Set the state model attributes to the initial
* passed in attributes or any defaults set
*
* @private
* @method _setStateDefaults
* @memberOf Component
* @param {Object} [stateAttrs] - Attributes to set on the state model
*/
_setStateDefaults: function _setStateDefaults(stateAttrs) {
this.setState(stateAttrs, { silent: true });
},
/**
* Set the Component's region and then show it.

@@ -870,2 +976,3 @@ *

/**

@@ -887,4 +994,4 @@ * Show the Component in its region.

throw new Marionette.Error({
name: "ComponentShowError",
message: "Component has already been shown in a region."
name: 'ComponentShowError',
message: 'Component has already been shown in a region.'
});

@@ -895,8 +1002,8 @@ }

throw new Marionette.Error({
name: "ComponentRegionError",
message: "Component has no defined region."
name: 'ComponentRegionError',
message: 'Component has no defined region.'
});
}
this.triggerMethod("before:show");
this.triggerMethod('before:show');

@@ -906,7 +1013,7 @@ this.renderView(viewOptions);

this.triggerMethod("show");
this.triggerMethod('show');
// Destroy the component if the region is emptied because
// it destroys the view
this.listenTo(this.region, "empty", this._destroy);
this.listenTo(this.region, 'empty', this._destroy);

@@ -916,2 +1023,3 @@ return this;

/**

@@ -928,19 +1036,20 @@ * Get the Component ViewClass class.

*/
_getViewClass: function _getViewClass(options) {
options = _.extend({}, options);
_getViewClass: function _getViewClass() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var ViewClass = this.getOption("ViewClass");
var ViewClass = this.ViewClass;
if (ViewClass.prototype instanceof Backbone.View || ViewClass === Backbone.View) {
return ViewClass;
} else if (_.isFunction(ViewClass)) {
} else if (_$1.isFunction(ViewClass)) {
return ViewClass.call(this, options);
} else {
throw new Marionette.Error({
name: "InvalidViewClassError",
message: "\"ViewClass\" must be a view class or a function that returns a view class"
});
}
throw new Marionette.Error({
name: 'InvalidViewClassError',
message: '"ViewClass" must be a view class or a function that returns a view class'
});
},
/**

@@ -969,3 +1078,3 @@ * Shows or re-shows a newly built view in the component's region

this.triggerMethod("before:render:view", view);
this.triggerMethod('before:render:view', view);

@@ -981,3 +1090,3 @@ // _shouldDestroy is flag that prevents the Component from being

this.triggerMethod("render:view", view);
this.triggerMethod('render:view', view);

@@ -987,2 +1096,3 @@ return this;

/**

@@ -1000,9 +1110,9 @@ * Proxies the ViewClass's viewEvents to the Component itself

_proxyViewEvents: function _proxyViewEvents(view) {
var prefix = this.getOption("viewEventPrefix");
var prefix = this.viewEventPrefix;
view.on("all", function () {
var args = _.toArray(arguments);
view.on('all', function () {
var args = _$1.toArray(arguments);
var rootEvent = args[0];
args[0] = prefix + ":" + rootEvent;
args[0] = prefix + ':' + rootEvent;
args.splice(1, 0, view);

@@ -1014,4 +1124,5 @@

/**
* Mixin stateModel from StateClass with any other viewOptions
* Mixin initial State with any other viewOptions
*

@@ -1026,7 +1137,8 @@ * @public

mixinOptions: function mixinOptions(options) {
var viewOptions = _.result(this, "viewOptions");
var viewOptions = _$1.result(this, 'viewOptions');
return _.extend({ stateModel: this.getState() }, viewOptions, options);
return _$1.extend({ state: this.getState().attributes }, viewOptions, options);
},
/**

@@ -1049,2 +1161,3 @@ * Builds the view class with options

/**

@@ -1059,6 +1172,7 @@ * Destroys Component.

if (this._shouldDestroy) {
state_class.prototype.destroy.apply(this, arguments);
Marionette.Object.prototype.destroy.apply(this, arguments);
}
},
/**

@@ -1074,3 +1188,3 @@ * Empties component's region.

if (this.region) {
this.stopListening(this.region, "empty");
this.stopListening(this.region, 'empty');
this.region.empty(options);

@@ -1080,2 +1194,3 @@ }

/**

@@ -1098,4 +1213,8 @@ * Empty the region and destroy the component.

var component = Component;
_$1.extend(Component.prototype, StateMixin);
/**
* @module Toolkit
*/
var previousToolkit = Marionette.Toolkit;

@@ -1110,14 +1229,24 @@

Toolkit.VERSION = "0.4.1";
Toolkit.MixinState = function (classDefinition) {
var _StateMixin = StateMixin;
Toolkit.StateClass = state_class;
if (classDefinition.prototype.StateModel) {
_StateMixin = _.omit(StateMixin, 'StateModel');
}
Toolkit.App = app;
_.extend(classDefinition.prototype, _StateMixin);
};
Toolkit.Component = component;
Toolkit.VERSION = '1.0.0';
var marionette_toolkit = Toolkit;
Toolkit.StateMixin = StateMixin;
return marionette_toolkit;
});
//# sourceMappingURL=./marionette.toolkit.js.map
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 v0.4.1
* @version v1.0.0
* @link https://github.com/RoundingWellOS/marionette.toolkit
* @license MIT
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("backbone.marionette"),require("underscore"),require("backbone")):"function"==typeof define&&define.amd?define(["backbone.marionette","underscore","backbone"],e):t.Marionette.Toolkit=e(t.Marionette,t._,t.Backbone)}(this,function(t,e,i){"use strict";var s=t.Object.extend({StateModel:i.Model,constructor:function(i){i=e.extend({},i),e.extend(this,e.pick(i,["StateModel","stateEvents","stateDefaults"]));var s=this._getStateModel(i);this._stateModel=new s(e.result(this,"stateDefaults")),this.bindEntityEvents(this._stateModel,e.result(this,"stateEvents")),t.Object.call(this,i)},_getStateModel:function(s){var n=this.getOption("StateModel");if(n.prototype instanceof i.Model||n===i.Model)return n;if(e.isFunction(n))return n.call(this,s);throw new t.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)},getState:function(t){return t?this._stateModel.get.apply(this._stateModel,arguments):this._stateModel},destroy:function(){this._stateModel.stopListening(),t.Object.prototype.destroy.apply(this,arguments)}}),n=s,r=n.extend({_isRunning:!1,_isDestroyed:!1,preventDestroy:!1,startAfterInitialized:!1,startWithParent:!1,stopWithParent:!0,constructor:function(t){t=e.extend({},t),e.bindAll(this,"start","stop");var i=["startWithParent","stopWithParent","startAfterInitialized","preventDestroy"];e.extend(this,e.pick(t,i)),n.call(this,t),e.result(this,"startAfterInitialized")&&this.start(t)},_ensureAppIsIntact:function(){if(this._isDestroyed)throw new t.Error({name:"AppDestroyedError",message:"App has already been destroyed and cannot be used."})},isRunning:function(){return this._isRunning},start:function(t){return this._ensureAppIsIntact(),this._isRunning?this:(this.triggerMethod("before:start",t),this._isRunning=!0,this.triggerStart(t),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},isDestroyed:function(){return this._isDestroyed},destroy:function(){this._isDestroyed||(this._isDestroyed=!0,this.stop(),n.prototype.destroy.apply(this,arguments))},_stopRunningEvents:function(){e.each(this._runningEvents,function(t){this.off.apply(this,t)},this)},_stopRunningListeners:function(){e.each(this._runningListeningTo,function(t){this.stopListening.apply(this,t)},this)},on:function(){return this._isRunning&&(this._runningEvents=this._runningEvents||[],this._runningEvents.push(arguments)),n.prototype.on.apply(this,arguments)},listenTo:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),n.prototype.listenTo.apply(this,arguments)},listenToOnce:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),n.prototype.listenToOnce.apply(this,arguments)}}),o=r,h=o.extend({constructor:function(t){t=e.extend({},t),this._childApps={},e.extend(this,e.pick(t,["childApps"])),this._initChildApps(t),this.on({start:this._startChildApps,"before:stop":this._stopChildApps,"before:destroy":this._destroyChildApps}),o.call(this,t)},_initChildApps:function(t){var i=this.childApps;i&&(e.isFunction(i)&&(i=i.call(this,t)),this.addChildApps(i))},_startChildApps:function(){e.each(this._childApps,function(t){e.result(t,"startWithParent")&&t.start()})},_stopChildApps:function(){e.each(this._childApps,function(t){e.result(t,"stopWithParent")&&t.stop()})},_destroyChildApps:function(){e.each(this._childApps,function(t){e.result(t,"preventDestroy")||t.destroy()})},_buildAppFromObject:function(t){var i=t.AppClass,s=e.omit(t,"AppClass");return this.buildApp(i,s)},_buildApp:function(t,i){return e.isFunction(t)?this.buildApp(t,i):e.isObject(t)?this._buildAppFromObject(t):void 0},buildApp:function(t,e){return new t(e)},_ensureAppIsUnique:function(e){if(this._childApps[e])throw new t.Error({name:"DuplicateChildAppError",message:'A child App with name "'+e+'" has already been added.'})},addChildApps:function(t){e.each(t,function(t,e){this.addChildApp(e,t)},this)},addChildApp:function(i,s,n){this._ensureAppIsUnique(i);var r=this._buildApp(s,n);if(!r)throw new t.Error({name:"AddChildAppError",message:"App build failed. Incorrect configuration."});return r._name=i,this._childApps[i]=r,r.on("destroy",e.partial(this._removeChildApp,i),this),this.isRunning()&&e.result(r,"startWithParent")&&r.start(),r},getName:function(){return this._name},getChildApps:function(){return e.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 e.each(this._childApps,function(t,e){this.removeChildApp(e)},this),t},removeChildApp:function(t,i){i=e.extend({},i);var s=this.getChildApp(t);if(s)return i.preventDestroy||e.result(s,"preventDestroy")?this._removeChildApp(t):s.destroy(),s}}),a=h,p=n.extend({ViewClass:t.ItemView,viewEventPrefix:"view",viewOptions:{},constructor:function(t,i){i=e.extend({},i),e.extend(this,e.pick(i,["viewEventPrefix","ViewClass","viewOptions","region"])),n.call(this,i),this._setStateDefaults(t)},_shouldDestroy:!0,_setStateDefaults:function(t){this.setState(t,{silent:!0})},showIn:function(t,e){return this.region=t,this.show(e),this},show:function(e){if(this._isShown)throw new t.Error({name:"ComponentShowError",message:"Component has already been shown in a region."});if(!this.region)throw new t.Error({name:"ComponentRegionError",message:"Component has no defined region."});return this.triggerMethod("before:show"),this.renderView(e),this._isShown=!0,this.triggerMethod("show"),this.listenTo(this.region,"empty",this._destroy),this},_getViewClass:function(s){s=e.extend({},s);var n=this.getOption("ViewClass");if(n.prototype instanceof i.View||n===i.View)return n;if(e.isFunction(n))return n.call(this,s);throw new t.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),s=this.buildView(e,i);return this.currentView=s,this._proxyViewEvents(s),this.triggerMethod("before:render:view",s),this._shouldDestroy=!1,this.region.show(s),this._shouldDestroy=!0,this.triggerMethod("render:view",s),this},_proxyViewEvents:function(t){var i=this.getOption("viewEventPrefix");t.on("all",function(){var s=e.toArray(arguments),n=s[0];s[0]=i+":"+n,s.splice(1,0,t),this.triggerMethod.apply(this,s)},this)},mixinOptions:function(t){var i=e.result(this,"viewOptions");return e.extend({stateModel:this.getState()},i,t)},buildView:function(t,e){return new t(e)},_destroy:function(){this._shouldDestroy&&n.prototype.destroy.apply(this,arguments)},_emptyRegion:function(t){this.region&&(this.stopListening(this.region,"empty"),this.region.empty(t))},destroy:function(t){this._emptyRegion(t),this._shouldDestroy=!0,this._destroy(t)}}),u=p,l=t.Toolkit,d=t.Toolkit={};d.noConflict=function(){return t.Toolkit=l,this},d.VERSION="0.4.1",d.StateClass=n,d.App=a,d.Component=u;var c=d;return c});
//# sourceMappingURL=marionette.toolkit.min.js.map
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("backbone.marionette"),require("underscore"),require("backbone")):"function"==typeof define&&define.amd?define(["backbone.marionette","underscore","backbone"],e):(t.Marionette=t.Marionette||{},t.Marionette.Toolkit=e(t.Marionette,t._,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.unbindEntityEvents(this._stateModel),this._stateModel.stopListening(),this.off("destroy",this._destroyState))},_setEventHandlers:function(){this.bindEntityEvents(this._stateModel,e.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(e.isFunction(this.StateModel))return this.StateModel.call(this,n);throw new t.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 t=e.result(this._stateModel,"defaults");return this._stateModel.set(t)},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 t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this._childApps={},this.mergeOptions(t,r);var i=this.childApps;i&&(e.isFunction(i)&&(i=i.call(this,t)),this.addChildApps(i)),this._initListeners()},_initListeners:function(){this.on({start:this._startChildApps,"before:stop":this._stopChildApps,"before:destroy":this._destroyChildApps})},_startChildApps:function(){e.each(this._childApps,function(t){e.result(t,"startWithParent")&&t.start()})},_stopChildApps:function(){e.each(this._childApps,function(t){e.result(t,"stopWithParent")&&t.stop()})},_destroyChildApps:function(){e.each(this._childApps,function(t){e.result(t,"preventDestroy")||t.destroy()})},_buildAppFromObject:function(t){var i=t.AppClass,n=e.omit(t,"AppClass");return this.buildApp(i,n)},_buildApp:function(t,i){return e.isFunction(t)?this.buildApp(t,i):e.isObject(t)?this._buildAppFromObject(t):void 0},buildApp:function(t,i){return i=e.extend({},this.childAppOptions,i),new t(i)},_ensureAppIsUnique:function(e){if(this._childApps[e])throw new t.Error({name:"DuplicateChildAppError",message:'A child App with name "'+e+'" has already been added.'})},addChildApps:function(t){e.each(t,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 t.Error({name:"AddChildAppError",message:"App build failed. Incorrect configuration."});return r._name=i,this._childApps[i]=r,r.on("destroy",e.partial(this._removeChildApp,i),this),this.isRunning()&&e.result(r,"startWithParent")&&r.start(),r},getName:function(){return this._name},getChildApps:function(){return e.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 e.each(this._childApps,function(t,e){this.removeChildApp(e)},this),t},removeChildApp:function(t,i){i=e.extend({},i);var n=this.getChildApp(t);if(n)return i.preventDestroy||e.result(n,"preventDestroy")?this._removeChildApp(t):n.destroy(),n}},h={_stopRunningEvents:function(){e.each(this._runningEvents,function(t){this.off.apply(this,t)},this)},_stopRunningListeners:function(){e.each(this._runningListeningTo,function(t){this.stopListening.apply(this,t)},this)},on:function(){return this._isRunning&&(this._runningEvents=this._runningEvents||[],this._runningEvents.push(arguments)),t.Object.prototype.on.apply(this,arguments)},listenTo:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),t.Object.prototype.listenTo.apply(this,arguments)},listenToOnce:function(){return this._isRunning&&(this._runningListeningTo=this._runningListeningTo||[],this._runningListeningTo.push(arguments)),t.Object.prototype.listenToOnce.apply(this,arguments)}},p=["startWithParent","stopWithParent","startAfterInitialized","preventDestroy"],a=t.Object.extend({_isRunning:!1,_isDestroyed:!1,preventDestroy:!1,startAfterInitialized:!1,startWithParent:!1,stopWithParent:!0,constructor:function(){var i=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];e.bindAll(this,"start","stop"),this.mergeOptions(i,p),this.initState(i),this._initChildApps(i),t.Object.call(this,i),e.result(this,"startAfterInitialized")&&this.start(i)},_ensureAppIsIntact:function(){if(this._isDestroyed)throw new t.Error({name:"AppDestroyedError",message:"App has already been destroyed and cannot be used."})},isRunning:function(){return this._isRunning},start:function(t){return this._ensureAppIsIntact(),this._isRunning?this:(this.triggerMethod("before:start",t),this._isRunning=!0,this.triggerStart(t),this)},triggerStart:function(t){this.triggerMethod("start",t)},restart:function(t){return this.stop(t),this.initState(t),this.start(t),this},stop:function(t){return this._isRunning?(this.triggerMethod("before:stop",t),this._isRunning=!1,this.triggerMethod("stop",t),this._stopRunningListeners(),this._stopRunningEvents(),this):this},isDestroyed:function(){return this._isDestroyed},destroy:function(){this._isDestroyed||(this.stop(),t.Object.prototype.destroy.apply(this,arguments),this._isDestroyed=!0)}});e.extend(a.prototype,s,o,h);var d=["ViewClass","viewEventPrefix","viewOptions","region"],u=t.Object.extend({ViewClass:t.ItemView,viewEventPrefix:"view",viewOptions:{},constructor:function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.mergeOptions(e,d),this.initState(e),t.Object.call(this,e)},_shouldDestroy:!0,showIn:function(t,e){return this.region=t,this.show(e),this},show:function(e){if(this._isShown)throw new t.Error({name:"ComponentShowError",message:"Component has already been shown in a region."});if(!this.region)throw new t.Error({name:"ComponentRegionError",message:"Component has no defined region."});return this.triggerMethod("before:show"),this.renderView(e),this._isShown=!0,this.triggerMethod("show"),this.listenTo(this.region,"empty",this._destroy),this},_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(e.isFunction(s))return s.call(this,n);throw new t.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.region.show(n),this._shouldDestroy=!0,this.triggerMethod("render:view",n),this},_proxyViewEvents:function(t){var i=this.viewEventPrefix;t.on("all",function(){var n=e.toArray(arguments),s=n[0];n[0]=i+":"+s,n.splice(1,0,t),this.triggerMethod.apply(this,n)},this)},mixinOptions:function(t){var i=e.result(this,"viewOptions");return e.extend({state:this.getState().attributes},i,t)},buildView:function(t,e){return new t(e)},_destroy:function(){this._shouldDestroy&&t.Object.prototype.destroy.apply(this,arguments)},_emptyRegion:function(t){this.region&&(this.stopListening(this.region,"empty"),this.region.empty(t))},destroy:function(t){this._emptyRegion(t),this._shouldDestroy=!0,this._destroy(t)}});e.extend(u.prototype,s);var l=t.Toolkit,c=t.Toolkit={};return c.noConflict=function(){return t.Toolkit=l,this},c.MixinState=function(t){var e=s;t.prototype.StateModel&&(e=_.omit(s,"StateModel")),_.extend(t.prototype,e)},c.VERSION="1.0.0",c.StateMixin=s,c.App=a,c.Component=u,c});
//# sourceMappingURL=marionette.toolkit.min.js.map
# Marionette.Toolkit.App
`Marionette.Toolkit.App` is an extension of [`Marionette.Toolkit.AbstractApp`](./abstract-app.md). An `AbstractApp`'s purpose is to provide an object with a `initialize`/`start`/`stop`/`destroy` lifecycle. All events bound to the `AbstractApp` while running (and only those) will be removed when stopped. `App` mixes in "App Manager" functionality so that child `App`s can be added or removed relating the child `App` lifecycle with the parent `App` lifecycle.
`Marionette.Toolkit.App` is an extension of `Marionette.Object`. Its purpose is to provide an object with a `initialize`/`start`/`stop`/`destroy` lifecycle. `App` has several mixins:
* [`StateMixin`](./mixins/state.md) to maintain application state.
* [`EventListernersMixin`](./mixins/event-listeners.md) to bind all events to an `App` while running (and only those) will be remove when stopped.
* [`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.
## Documentation Index
* [App's Lifecycle Settings](#apps-lifecycle-settings)
* [App's `childApps`](#apps-childapps)
* [App API](#app-api)
* [App `buildApp`](#app-buildapp)
* [App `addChildApp`](#app-addchildapp)
* [App `addChildApps`](#app-addchildapps)
* [App `getName`](#app-getName)
* [App `getChildApp`](#app-getchildapp)
* [App `getChildApps`](#app-getchildapps)
* [App `removeChildApp`](#app-removechildapp)
* [App `removeChildApps`](#app-removechildapps)
* [App `removeChildApp`](#app-removechildapp)
* [Lifecycle Settings](#lifecycle-settings)
* [App's `startAfterInitialized`](#apps-startafterinitialized)
* [App's `preventDestroy`](#apps-preventdestroy)
* [App's `startWithParent`](#apps-startwithparent)
* [App's `stopWithParent`](#apps-stopwithparent)
* [Lifecycle API](#lifecycle-api)
* [App `start`](#app-start)
* [App `restart`](#app-restart)
* [App `stop`](#app-stop)
* [App `isRunning`](#app-isrunning)
* [App `destroy`](#app-destroy)
* [App `isDestroyed`](#app-isdestroyed)
* [Lifecycle Events](#lifecycle-events)
* ["before:start" / "start" events](#beforestart--start-events)
* ["before:stop" / "stop" events](#beforestop--stop-events)
## App's Lifecycle Settings
## Lifecycle Settings
`childApp` lifecycles may be determined by the settings applied to a `childApp` itself. For more information read [AbstractApp Lifecycle Settings](./abstract-app.md#lifecycle-settings)
### App's `startAfterInitialized`
### App's `childApps`
`childApps` is an object literal or a function that returns an object literal.
The object literal must contain app names as keys and app definitions as values.
`childApps` can be passed to an `App` at instantiation or defined on the definition.
If defined as a function it will receive the `options` passed to the `constructor`.
Call `start` immediately after `initialize` if `true`. Default value is `false`.
Can be added as an option when instantiated or defined on the `App` definition.
It can also be defined as a function returning a boolean value.
```js
var MyApp = Marionette.Toolkit.App.extend({
childApps: function(options){
return {
childName: MyChildApp,
otherName: {
AppClass: MyOtherApp,
preventDestroy: true,
fooOption: 'bar'
}
};
}
initialize: function(){
this.isRunning() === false;
},
startAfterInitialized: true
});
var myApp = new MyApp();
myApp.isRunning() === true;
```
### App's `preventDestroy`
If set `true` this `App` will not be destroyed when its parent `App` is destroyed.
Default value is `false`.
Can be added as an option when instantiated or defined on the `App` definition.
It can also be defined as a function returning a boolean value.
```js
var myApp = new Marionette.Toolkit.App({
childApps: {
childName: MyChildApp,
otherName: {
AppClass: MyOtherApp,
preventDestroy: true,
fooOption: 'bar'
}
}
var myApp = new Marionette.Toolkit.App();
var myChildApp = myApp.addChildApp('myChildApp', {
AppClass: Marionette.Toolkit.App,
preventDestroy: false
});
var myPreventDestroyApp = myApp.addChildApp('myPreventDestroyApp', {
AppClass: Marionette.Toolkit.App,
preventDestroy: true
});
myApp.destroy();
// logs true
console.log(myChildApp.isDestroyed());
// logs false
console.log(myPreventDestroyApp.isDestroyed());
```
## App API
### App's `startWithParent`
### App `buildApp`
Child instances are built through this function.
Override it if a parent app has additional concerns when building its children.
If set `true` this `App` will start when its parent `App` starts.
Default value is `false`.
Can be added as an option when instantiated or defined on the `App` definition.
It can also be defined as a function returning a boolean value.
```js
buildApp: function(AppClass, options) {
return new AppClass(options);
}
var myApp = new Marionette.Toolkit.App();
var myChildApp = myApp.addChildApp('myChildApp', {
AppClass: Marionette.Toolkit.App,
startWithParent: false
});
var myStartWithParentApp = myApp.addChildApp('myStartWithParentApp', {
AppClass: Marionette.Toolkit.App,
startWithParent: true
});
myApp.start();
// logs false
console.log(myChildApp.isRunning());
// logs true
console.log(myStartWithParentApp.isRunning());
```
### App `addChildApp`
### App's `stopWithParent`
`App`s can be added as children of an `App` individually using
the `addChildApp` method. This method takes three parameters: the app name,
the app definition and options to pass to the app when built.
The returned value is the add childApp instance.
If set `true` this `App` will stop when its parent `App` stops.
Default value is `true`.
Can be added as an option when instantiated or defined on the `App` definition.
It can also be defined as a function returning a boolean value.

@@ -79,57 +117,89 @@ ```js

var myChildApp = myApp.addChildApp('foo', Marionette.Toolkit.App, { fooOption: true });
var myChildApp = myApp.addChildApp('myChildApp', {
AppClass: Marionette.Toolkit.App,
stopWithParent: false
});
myChildApp.getOption('fooOption'); // => true
var myStopWithParentApp = myApp.addChildApp('myStopWithParentApp', {
AppClass: Marionette.Toolkit.App,
stopWithParent: true
});
myApp.start();
myChildApp.start();
myStopWithParentApp.start();
myApp.stop();
// logs true
console.log(myChildApp.isRunning());
// logs false
console.log(myStopWithParentApp.isRunning());
```
In this example, a child app named "foo" will be added
to the myApp instance.
## Lifecycle API
There are a lot of other ways to define an app,
including object literals with various options and
a function returning an object literal. For more information
on this, see [App's `childApps`](#apps-childapps).
### App `start`
### App `addChildApps`
This method sets the `App` to its running state.
Events added after `start` are registered for removal `onStop`.
This triggers ["before:start" / "start" events](#beforestart--start-events).
`App`s can also be added en-masse through the use
of the `addChildApps` method. This method takes an object
literal or a function that returns an object literal.
The object literal must contain app names as keys
and app definitions as values.
```js
var myApp = new Marionette.Toolkit.App();
myApp.on('start', function(options){
console.log('My App Started!');
options.foo === true;
});
// false
myApp.isRunning();
// "My App Started!" logged
myApp.start({
foo: true
});
// true
myApp.isRunning();
// Nothing is logged
myApp.start();
```
### App `restart`
This method stops the `App`'s running state.
The `App`'s state is then reinitialized.
Finally the `App`'s `start` method is triggered.
Among other options that `restart` accepts, it also takes state settings.
```js
var myApp = new Marionette.Toolkit.App();
// With an object literal
myApp.addChildApps({
main: Marionette.Toolkit.App,
navigation: {
fooOption: true,
startWithParent: true,
AppClass: MyNavApp
myApp.start();
//Pass state argument
myApp.restart({
state: {
foo: 'bar'
}
});
// With a function
myApp.addChildApps(function() {
return {
footer: Marionette.Toolkit.App
};
});
// true
myApp.isRunning();
myApp.getChildApp('main'); //=> 'main' app instance
var navApp = myApp.getChildApp('navigation'); //=> 'navigation' app instance
navApp.getOption('fooOption'); //=> true
myApp.getChildApp('footer'); //=> 'footer' app instance
// bar
this.getState('foo');
```
### App `getName`
### App `stop`
An App's name can be retrieved from the App
instance calling the `getName` method on the
instance. If the app is a childApp then the
app name will be returned, however if an app
is not a childApp or is a parentApp `undefined`
will be returned.
This method stops the `App`'s running state.
Events added after `start` are registered for removal `onStop`.
This triggers ["before:stop" / "stop" events](#beforestop--stop-events).

@@ -139,89 +209,143 @@ ```js

myApp.addChildApp('bar', Marionette.Toolkit.App);
var barAppName = myApp.getChildApp('bar').getName();
myApp.on('stop', function(options){
console.log('My App Stopped!');
options.foo === true;
});
// logs bar
console.log(barAppName);
// Nothing is logged
myApp.stop();
var myAppName = myApp.getName();
// logs undefined
console.log(myAppName);
myApp.start();
// true
myApp.isRunning();
// "My App Stopped!" logged
myApp.stop({
foo: true
});
// false
myApp.isRunning();
// Nothing is logged
myApp.stop();
```
### App `getChildApp`
### App `isRunning`
A childApp instance can be retrieved from the
App instance using the `getChildApp` method and
passing in the name of the childApp.
Returns a Boolean indicating whether or not the `App` is running.
```js
var myApp = new Marionette.Toolkit.App();
myApp.addChildApp('foo', Marionette.Toolkit.App);
var fooApp = myApp.getChildApp('foo');
myApp.start();
myApp.isRunning() === true;
myApp.stop();
myApp.isRunning() === false;
```
### App `getChildApps`
### App `destroy`
Get all the childApps from the app.
Returns an object literal with named childApps
as attributes.
This method stops the `App` if running and sets the `App`'s state to destroyed.
```js
var myApp = new Marionette.Toolkit.App();
myApp.addChildApp('foo', Marionette.Toolkit.App);
myApp.addChildApp('bar', Marionette.Toolkit.App);
var childApps = myApp.getChildApps();
myApp.start();
childApps.foo; //=> foo childApp
childApps.bar; //=> bar childApp
myApp.isRunning() === true;
myApp.isDestroyed() === false;
myApp.destroy();
myApp.isRunning() === false;
myApp.isDestroyed() === true;
```
### App `removeChildApp`
### App `isDestroyed`
An app can be removed by calling the `removeChildApp`
method and passing in the name of the app.
Returns a Boolean indicating whether or not the `App` is destroyed. Destroyed `App`s cannot be started or used.
```js
var myApp = new Marionette.Toolkit.App();
myApp.addChildApp('foo', Marionette.Toolkit.App);
myApp.addChildApp('bar', {
AppClass: Marionette.Toolkit.App,
preventDestroy: true
myApp.isDestroyed() === false;
myApp.destroy();
myApp.isDestroyed() === true;
```
## Lifecycle Events
### `before:start` / `start` events
The "before:start" event and corresponding `onBeforeStart`
method are triggered just before the `App` `isRunning` is set `true`.
The "start" event and corresponding `onStart`
method are triggered after the `App` `isRunning` is set `true`.
```js
var MyApp = Marionette.Toolkit.App.extend({
// ...
onBeforeStart: function(options){
// ...
},
onStart: function(options){
// ...
}
});
var fooApp = myApp.removeChildApp('foo');
var myApp = new MyApp({...});
var barApp = myApp.removeChildApp('bar');
myApp.on('before:start', function(options){
// ...
});
// logs true
console.log(fooApp.isDestroyed());
// logs false
console.log(barApp.isDestroyed());
myApp.on('start', function(options){
// ...
});
```
The removed app is destroyed unless that app has its
[preventDestroy](./abstract-app.md#apps-preventdestroy) setting set to true.
### `before:stop` / `stop` events
### App `removeChildApps`
The "before:stop" event and corresponding `onBeforeStop`
method are triggered just before the `App` `isRunning` is set `false`.
You can quickly remove all childApps from an
App instance by calling the `removeChildApps`
method.
The "stop" event and corresponding `onStop`
method are triggered after the `App` `isRunning` is set `false`.
```js
var myApp = new Marionette.Toolkit.App();
myApp.addChildApps({
foo: Marionette.Toolkit.App,
bar: Marionette.Toolkit.App,
baz: Marionette.Toolkit.App
var MyApp = Marionette.Toolkit.App.extend({
// ...
onBeforeStop: function(options){
// ...
},
onStop: function(options){
// ...
}
});
myApp.removeChildApps();
var myApp = new MyApp({...});
myApp.on('before:stop', function(options){
// ...
});
myApp.on('stop', function(options){
// ...
});
```
This will destroy all childApps (that don't have preventDestroy set to true), and remove them.
# Marionette.Toolkit.Component
`Marionette.Toolkit.Component` is heavily influenced by **@jfairbank**'s [Marionette.Component](https://github.com/jfairbank/marionette.component).
It is an extension of [`StateClass`](./state-class.md) that manages a view (or views) whose lifecycle is tied to the region it is shown in.
It mixes in [`StateMixin`](./mixins/state.md) that manages a view (or views) whose lifecycle is tied to the region it is shown in.
The Component provides a consistent interface for which to package state-view-logic.

@@ -29,4 +29,3 @@

The component is built to work out of the box.
When instantiating a component it takes two optional parameters, the initial state and options.
If there is no initial state to set, simply pass `null`, or `{}`.
When instantiating a component you can pass various options including `ViewClass` or initial component `state`.

@@ -38,12 +37,11 @@ ```js

var initialState = {
fooState: 'bar'
};
var options = {
fooOption: 'baz',
ViewClass: MyComponentView
ViewClass: MyComponentView,
state: {
fooState: 'bar'
}
};
var myComponent = new Marionette.Toolkit.Component(initialState, options);
var myComponent = new Marionette.Toolkit.Component(options);

@@ -93,2 +91,16 @@ myComponent.getState('fooState') === 'bar';

You can also manage the state of the ViewClass by mixing in the [`StateMixin`](./mixins/state.md) into your view.
This can be done by using the `Marionette.Toolkit.MixinState` Utility.
```js
var MyViewClass = Marionette.ItemView.extend({});
Marionette.Toolkit.MixinState(MyViewClass);
Marionette.Toolkit.Component.extend({
ViewClass: MyViewClass
});
```
### Component's `viewEventPrefix`

@@ -357,3 +369,3 @@

Mixes options passed to the method with the Component's [`viewOptions`](#components-viewoptions) and the `stateModel`
Mixes options passed to the method with the Component's [`viewOptions`](#components-viewoptions) and the current component `state`.
This function is used internally by [`renderView`](#component-renderview)

@@ -366,3 +378,3 @@ however you can override this function if you need to dynamically build the view options hash.

return _.extend({ stateModel: this.getState() }, viewOptions, options);
return _.extend({ state: this.getState().attributes }, viewOptions, options);
}

@@ -387,3 +399,2 @@ ```

Calling `destroy` will empty the `Component`'s `region` and destroy the `Component`.
Destroying the `Component` calls `destroy` on the [`StateClass`](./state-class.md#destroying-a-stateclass)
A destroyed `Component` instance should not be reused.

@@ -1,19 +0,16 @@

var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
const fs = require('fs');
const gulp = require('gulp');
const $ = require('gulp-load-plugins')();
const del = require('del');
const glob = require('glob');
const path = require('path');
const mkdirp = require('mkdirp');
const babelify = require('babelify');
const isparta = require('isparta');
const esperanto = require('esperanto');
const browserify = require('browserify');
const runSequence = require('run-sequence');
const source = require('vinyl-source-stream');
const Promise = require('bluebird');
const _ = require('underscore');
const rollup = require('rollup').rollup;
const multiEntry = require('rollup-plugin-multi-entry');
const nodeResolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const babel = require('rollup-plugin-babel');
const json = require('rollup-plugin-json');
const preset = require('babel-preset-es2015-rollup');
const manifest = require('./package.json');
const config = manifest.babelBoilerplateOptions;
const mochaGlobals = require('./test/.globals.json').globals;
const mainFile = manifest.main;

@@ -33,12 +30,10 @@ const destinationFolder = path.dirname(mainFile);

// Send a notification when JSHint fails,
// so that you know your changes didn't build
function jshintNotify(file) {
if (!file.jshint) { return; }
return file.jshint.success ? false : 'JSHint failed';
}
const onError = $.notify.onError('Error: <%= error.message %>');
function jscsNotify(file) {
if (!file.jscs) { return; }
return file.jscs.success ? false : 'JSRC failed';
function lint(files) {
return gulp.src(files)
.pipe($.plumber(onError))
.pipe($.eslint())
.pipe($.eslint.format())
.pipe($.eslint.failOnError());
}

@@ -48,9 +43,3 @@

gulp.task('lint-src', function() {
return gulp.src(['src/**/*.js'])
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'))
.pipe($.notify(jshintNotify))
.pipe($.jscs())
.pipe($.notify(jscsNotify))
.pipe($.jshint.reporter('fail'));
return lint(['src/**/*.js']);
});

@@ -60,67 +49,68 @@

gulp.task('lint-test', function() {
return gulp.src(['test/**/*.js'])
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'))
.pipe($.notify(jshintNotify))
.pipe($.jscs())
.pipe($.notify(jscsNotify))
.pipe($.jshint.reporter('fail'));
return lint(['test/**/*.js']);
});
function getBanner() {
var banner = ['/**',
' * <%= name %> - <%= description %>',
' * @version v<%= version %>',
' * @link <%= homepage %>',
' * @license <%= license %>',
const banner = ['/**',
` * ${ manifest.name } - ${ manifest.description }`,
` * @version v${ manifest.version }`,
` * @link ${ manifest.homepage }`,
` * @license ${ manifest.license }`,
' */',
''].join('\n');
return _.template(banner)(manifest);
return banner;
}
function _build(entryFileName, destFolder, expFileName, expVarName, umd){
return esperanto.bundle({
base: 'src',
entry: entryFileName,
transform: function(source) {
var js_source = _.template(source)(manifest);
function _generate(bundle, expVarName) {
const intro = getBanner();
// Poor way of modifying dependency for modular build
if(!umd){
return js_source.replace('./state-class', 'marionette.toolkit.state-class');
}
return bundle.generate({
format: 'umd',
moduleName: expVarName,
sourceMap: true,
banner: intro,
globals: {
'backbone': 'Backbone',
'underscore': '_',
'backbone.marionette': 'Marionette'
}
});
}
return js_source;
}
function bundleCode(entryFileName, expVarName) {
return rollup({
entry: `src/${ entryFileName }.js`,
external: ['underscore', 'backbone', 'backbone.marionette'],
plugins: [
babel({
sourceMaps: true,
presets: [preset],
babelrc: false
})
]
}).then(function(bundle) {
var banner = getBanner();
return _generate(bundle, expVarName);
}).then(gen => {
gen.code += `\n//# sourceMappingURL=${ gen.map.toUrl() }`;
return gen;
});
}
var bundleMethod = umd? 'toUmd' : 'toCjs';
function _buildLib(entryFileName, destFolder, expFileName, expVarName) {
return bundleCode(entryFileName, expVarName).then(function(gen) {
gen.code = gen.code.replace('<%VERSION%>', manifest.version);
var res = bundle[bundleMethod]({
banner: banner,
sourceMap: true,
sourceMapSource: entryFileName + '.js',
sourceMapFile: expFileName + '.js',
name: expVarName
});
// Write the generated sourcemap
mkdirp.sync(destFolder);
fs.writeFileSync(path.join(destFolder, expFileName + '.js'), res.map.toString());
$.file(expFileName + '.js', res.code, { src: true })
return $.file(`${ expFileName }.js`, gen.code, { src: true })
.pipe($.plumber())
.pipe($.sourcemaps.init({ loadMaps: true }))
.pipe($.babel({ blacklist: ['useStrict'] }))
.pipe($.sourcemaps.write('./', {addComment: false}))
.pipe($.sourcemaps.write('./'))
.pipe(gulp.dest(destFolder))
.pipe($.filter(['*', '!**/*.js.map']))
.pipe($.rename(expFileName + '.min.js'))
.pipe($.uglifyjs({
outSourceMap: true,
inSourceMap: destFolder + '/' + expFileName + '.js.map',
.pipe($.rename(`${ expFileName }.min.js`))
.pipe($.sourcemaps.init({ loadMaps: true }))
.pipe($.uglify({
preserveComments: 'license'
}))
.pipe($.header(banner))
.pipe($.sourcemaps.write('./'))
.pipe(gulp.dest(destFolder));

@@ -130,69 +120,49 @@ });

// Build two versions of the library
gulp.task('build-lib', ['lint-src', 'clean'], function() {
return _build(config.entryFileName, destinationFolder, exportFileName, config.exportVarName, 'umd');
return _buildLib(config.entryFileName, destinationFolder, exportFileName, config.exportVarName);
});
function _buildPackage(destFolder, entryName, exportName){
var data = {
version: manifest.version,
exportVarName: exportName,
entryName: entryName,
dependencies: JSON.stringify(manifest.dependencies, null, 4)
};
function bundleTest() {
return rollup({
entry: ['./test/setup/browser.js', './test/unit/**/*.js'],
plugins: [
multiEntry.default(),
nodeResolve({ main: true }),
commonjs(),
json(),
babel({
sourceMaps: true,
presets: [preset],
babelrc: false,
exclude: 'node_modules/**'
})
]
}).then(function(bundle) {
return bundle.write({
format: 'iife',
sourceMap: true,
moduleName: 'ToolkitTests',
dest: './tmp/__spec-build.js'
});
}).then($.livereload.changed('./tmp/__spec-build.js'));
}
gulp.src('./packages/LICENSE')
.pipe(gulp.dest(destFolder));
function browserWatch() {
$.livereload.listen({ port: 35729, host: 'localhost', start: true });
gulp.watch(['src/**/*.js', 'test/**/*.js'], ['browser-bundle']);
}
gulp.src('./packages/README.md')
.pipe($.template(data))
.pipe(gulp.dest(destFolder));
gulp.src('./packages/package.json.template')
.pipe($.template(data))
.pipe($.rename('package.json'))
.pipe(gulp.dest(destFolder));
function _registerBabel() {
require('babel-register');
}
gulp.task('build-packages', ['lint-src', 'clean'], function() {
var tasks = _.map(config.exportPackageNames, function(entryName, exportName){
var destFolder = './packages/' + exportName + '/';
var exportVarName = 'Marionette.Toolkit.' + exportName;
return _build(entryName, destFolder, exportName, exportVarName)
.then(_.partial(_buildPackage, destFolder, entryName, exportName));
});
return Promise.all(tasks);
});
// Bundle our app for our unit tests
gulp.task('browserify', function() {
var testFiles = glob.sync('./test/unit/**/*');
var allFiles = ['./test/setup/browserify.js'].concat(testFiles);
var bundler = browserify(allFiles);
bundler.transform(babelify.configure({
sourceMapRelative: __dirname + '/src',
blacklist: ['useStrict']
}));
var bundleStream = bundler.bundle();
return bundleStream
.on('error', function(err){
console.log(err.message);
this.emit('end');
})
.pipe($.plumber())
.pipe(source('./tmp/__spec-build.js'))
.pipe(gulp.dest(''))
.pipe($.livereload());
});
gulp.task('coverage', ['lint-src', 'lint-test'], function(done) {
require('babel/register')({ modules: 'common' });
gulp.src(['src/*.js'])
.pipe($.istanbul({ instrumenter: isparta.Instrumenter }))
.pipe($.istanbul.hookRequire())
_registerBabel();
gulp.src(['src/**/*.js'])
.pipe($.babelIstanbul())
.pipe($.babelIstanbul.hookRequire())
.on('finish', function() {
return test()
.pipe($.istanbul.writeReports())
.on('end', done);
.pipe($.babelIstanbul.writeReports())
.on('end', done);
});

@@ -202,18 +172,12 @@ });

function test() {
return gulp.src(['test/setup/node.js', 'test/unit/**/*.js'], {read: false})
.pipe($.mocha({reporter: 'dot', globals: config.mochaGlobals}));
};
return gulp.src(['test/setup/node.js', 'test/unit/**/*.js'], { read: false })
.pipe($.mocha({ reporter: 'dot', globals: mochaGlobals }));
}
// Lint and run our tests
gulp.task('test', ['lint-src', 'lint-test'], function() {
require('babel/register')({ modules: 'common' });
_registerBabel();
return test();
});
// Ensure that linting occurs before browserify runs. This prevents
// the build from breaking due to poorly formatted code.
gulp.task('build-in-sequence', function(callback) {
runSequence(['lint-src', 'lint-test'], 'browserify', callback);
});
// Run the headless unit tests as you make changes.

@@ -224,11 +188,9 @@ gulp.task('watch', function() {

// Set up a livereload environment for our spec runner
gulp.task('test-browser', ['build-in-sequence'], function() {
$.livereload.listen({port: 35729, host: 'localhost', start: true});
return gulp.watch(['src/**/*.js', 'test/**/*', '.jshintrc', 'test/.jshintrc'], ['build-in-sequence']);
});
gulp.task('browser-bundle', ['lint-src', 'lint-test'], bundleTest);
gulp.task('build', ['build-lib', 'build-packages']);
gulp.task('test-browser', ['browser-bundle'], browserWatch);
gulp.task('build', ['build-lib']);
// An alias of test
gulp.task('default', ['test']);
{
"name": "marionette.toolkit",
"version": "0.4.2",
"version": "1.0.0",
"description": "A collection of opinionated Backbone.Marionette extensions for large scale application architecture.",

@@ -10,4 +10,3 @@ "main": "./dist/marionette.toolkit.js",

"build": "gulp build",
"coverage": "gulp coverage",
"deploy": "./ship.sh"
"coverage": "gulp coverage"
},

@@ -37,18 +36,18 @@ "repository": {

"devDependencies": {
"babel": "^5.8.19",
"babelify": "^6.3.0",
"bluebird": "^2.9.27",
"browserify": "^8.1.1",
"babel-core": "^6.7.7",
"babel-eslint": "^6.0.3",
"babel-polyfill": "^6.7.4",
"babel-preset-es2015": "^6.6.0",
"babel-preset-es2015-rollup": "^1.1.1",
"babel-register": "^6.7.2",
"backbone": "^1.0.0",
"backbone.marionette": "^2.1.0",
"chai": "^2.0.0",
"del": "^1.1.1",
"esperanto": "^0.6.7",
"glob": "^4.3.5",
"eslint": "^2.8.0",
"gulp": "^3.8.10",
"gulp-babel": "^4.0.0",
"gulp-eslint": "^2.0.0",
"gulp-file": "^0.2.0",
"gulp-filter": "^2.0.0",
"gulp-header": "^1.2.2",
"gulp-istanbul": "^0.6.0",
"gulp-jscs": "^1.4.0",
"gulp-jshint": "^1.9.0",
"gulp-babel-istanbul": "^1.1.0",
"gulp-livereload": "^3.4.0",

@@ -61,38 +60,25 @@ "gulp-load-plugins": "^0.8.0",

"gulp-sourcemaps": "^1.3.0",
"gulp-template": "^3.0.0",
"gulp-uglifyjs": "^0.6.0",
"isparta": "^2.2.0",
"gulp-uglify": "^1.5.3",
"jquery": "~2.1.1",
"jsdom": "^2.0.0",
"jshint-stylish": "^1.0.0",
"mkdirp": "^0.5.0",
"jsdom": "^8.0.0",
"mocha": "^2.1.0",
"run-sequence": "^1.0.2",
"rollup": "^0.25.8",
"rollup-plugin-babel": "^2.4.0",
"rollup-plugin-commonjs": "^2.2.1",
"rollup-plugin-json": "^2.0.0",
"rollup-plugin-multi-entry": "^1.2.0",
"rollup-plugin-node-resolve": "^1.5.0",
"sinon": "^1.12.2",
"sinon-chai": "^2.7.0",
"underscore": "^1.8.3",
"vinyl-source-stream": "^1.0.0"
"underscore": "^1.8.3"
},
"babelBoilerplateOptions": {
"entryFileName": "marionette.toolkit",
"exportVarName": "Marionette.Toolkit",
"exportPackageNames": {
"App": "app",
"Component": "component",
"StateClass": "state-class"
},
"mochaGlobals": [
"stub",
"spy",
"expect",
"_",
"Backbone",
"Marionette"
]
"exportVarName": "Marionette.Toolkit"
},
"dependencies": {
"backbone.marionette": "^2.1.0",
"backbone": "^1.0.0",
"underscore": "^1.4.4"
"peerDependencies": {
"backbone.marionette": "^2.4.5",
"backbone": "^1.3.2",
"underscore": "^1.8.3"
}
}

@@ -15,13 +15,15 @@ Marionette.Toolkit

## Documentation
[StateClass](./docs/state-class.md) - `Marionette.Object` with a `Backbone.Model` for keeping state.
[App](./docs/app.md) - An extension of `Marionette.Object`. Its purpose is to provide an object with a `initialize`/`start`/`stop`/`destroy` lifecycle.
[Component](./docs/component.md) - Reusable `StateClass` with `View` management boilerplate
[Component](./docs/component.md) - Provides a consistent interface for which to package state-view-logic.
[AbstractApp](./docs/abstract-app.md) - Object including a start/stop lifecycle and running event registry.
[Asnyc App start](./docs/async-app-start.md) - How to easily start apps asynchronously.
[App](./docs/app.md) - `AbstractApp` with mixed in "App Manager" for adding and removing child `App`s
[StateMixin](./docs/mixins/state.md) - JavaScript Object with a `Backbone.Model` for keeping state.
[Asnyc App start](./docs/async-app-start.md) - How to easily start apps asynchronously
[ChildAppsMixin](./docs/mixins/child-apps.md) - Functionality to add or remove child Apps to a parent App, start apps asynchronously, and connect the child App lifecycle with the parent App lifecycle.
[EventListernersMixin](./docs/mixins/event-listeners.md) - Binds events to the `App` while running and removed (and only those) when the `App` is stopped.
## Getting Help

@@ -28,0 +30,0 @@

import _ from 'underscore';
import Marionette from 'backbone.marionette';
import AbstractApp from './abstract-app';
import StateMixin from './mixins/state';
import ChildAppsMixin from './mixins/child-apps';
import EventListenersMixin from './mixins/event-listeners';
const ClassOptions = [
'startWithParent',
'stopWithParent',
'startAfterInitialized',
'preventDestroy'
];
/**
* AbstractApp with an "App Manager" functionality mixed in for adding and removing child `App`s.
* Marionette.Object with an `initialize` / `start` / `stop` / `destroy` lifecycle.
*

@@ -13,171 +22,92 @@ * @public

*/
var App = AbstractApp.extend({
const App = Marionette.Object.extend({
/**
* @public
* @constructs App
* @param {Object} [options] - Settings for the App.
* @param {Object} [options.childApps] - Hash for setting up child apps.
* Internal flag indiciate when `App` has started but has not yet stopped.
*
* ```js
* childApps: {
* appName: {
* AppClass: MyChildAppClass,
* fooOption: true,
* startWithParent: true
* },
* barApp: MyOtherChildAppClass
* }
* ```
*/
constructor: function(options) {
options = _.extend({}, options);
this._childApps = {};
_.extend(this, _.pick(options, ['childApps']));
this._initChildApps(options);
// The child apps should be handled while the app is running;
// After start, before stop, and before destroy.
this.on({
'start' : this._startChildApps,
'before:stop' : this._stopChildApps,
'before:destroy' : this._destroyChildApps
});
AbstractApp.call(this, options);
},
/**
* Initializes `childApps` option
*
* @private
* @method _initChildApps
* @memberOf App
* @type {Boolean}
* @default false
*/
_initChildApps: function(options) {
var childApps = this.childApps;
_isRunning: false,
if(childApps) {
if(_.isFunction(childApps)) {
childApps = childApps.call(this, options);
}
this.addChildApps(childApps);
}
},
/**
* Starts `childApps` if allowed by child
* Internal flag indiciate when `App` has been destroyed
*
* @private
* @method _startChildApps
* @memberOf App
* @type {Boolean}
* @default false
*/
_startChildApps: function() {
_.each(this._childApps, function(childApp) {
if(_.result(childApp, 'startWithParent')) {
childApp.start();
}
});
},
_isDestroyed: false,
/**
* Stops `childApps` if allowed by child
* Set to true if a parent `App` should not be able to destroy this `App`.
*
* @private
* @method _stopChildApps
* @memberOf App
* @type {Boolean|Function}
* @default false
*/
_stopChildApps: function() {
_.each(this._childApps, function(childApp) {
if(_.result(childApp, 'stopWithParent')) {
childApp.stop();
}
});
},
preventDestroy: false,
/**
* Destroys `childApps` if allowed by child
* Set to true if `App` should be started after it is initialized.
*
* @private
* @method _destroyChildApps
* @memberOf App
* @type {Boolean|Function}
* @default false
*/
_destroyChildApps: function() {
_.each(this._childApps, function(childApp) {
if(!_.result(childApp, 'preventDestroy')) {
childApp.destroy();
}
});
},
startAfterInitialized: false,
/**
* Internal helper to instantiate and `App` from on `Object`
* Set to true if `App` should be started after its parent starts.
*
* @private
* @method _buildAppFromObject
* @memberOf App
* @param {Object} appConfig - `AppClass` and any other option for the `App`
* @returns {App}
* @type {Boolean|Function}
* @default false
*/
_buildAppFromObject: function(appConfig) {
var AppClass = appConfig.AppClass;
var options = _.omit(appConfig, 'AppClass');
startWithParent: false,
return this.buildApp(AppClass, options);
},
/**
* Helper for building an App and return it
* Set to false if `App` should not stop after its parent stops.
*
* @private
* @method _buildApp
* @memberOf App
* @param {App} AppClass - An App Class
* @param {Object} AppClass - Optionally passed as an appConfig Object
* @param {Object} [options] - options for the AppClass
* @returns {App}
* @type {Boolean|Function}
* @default true
*/
_buildApp: function(AppClass, options) {
if(_.isFunction(AppClass)) {
return this.buildApp(AppClass, options);
}
if(_.isObject(AppClass)) {
return this._buildAppFromObject(AppClass);
}
},
stopWithParent: true,
/**
* Build an App and return it
* Override for dynamic App building
*
* @public
* @method buildApp
* @memberOf App
* @param {App} [AppClass] - An App Class
* @param {Object} [options] - options for the AppClass
* @returns {App}
* @constructs App
* @param {Object} [options] - Settings for the App.
* @param {Boolean} [options.startWithParent]
* @param {Boolean} [options.stopWithParent]
* @param {Boolean} [options.startAfterInitialized]
* @param {Boolean} [options.preventDestroy]
* @param {Object} [options.state] - Attributes to set on the state model.
*/
buildApp: function(AppClass, options) {
return new AppClass(options);
constructor(options = {}) {
_.bindAll(this, 'start', 'stop');
this.mergeOptions(options, ClassOptions);
this.initState(options);
this._initChildApps(options);
Marionette.Object.call(this, options);
if(_.result(this, 'startAfterInitialized')) {
this.start(options);
}
},
/**
* Internal helper to verify `appName` is unique and not in use
* Internal helper to verify if `App` has been destroyed
*
* @private
* @method _ensureAppIsUnique
* @method _ensureAppIsIntact
* @memberOf App
* @param {String} appName - Name of app to test
* @throws DuplicateChildAppError - Thrown if `App` already has an `appName` registered
* @throws AppDestroyedError - Thrown if `App` has already been destroyed
*/
_ensureAppIsUnique: function(appName) {
if(this._childApps[appName]) {
_ensureAppIsIntact() {
if(this._isDestroyed) {
throw new Marionette.Error({
name: 'DuplicateChildAppError',
message: 'A child App with name "' + appName + '" has already been added.'
name: 'AppDestroyedError',
message: 'App has already been destroyed and cannot be used.'
});

@@ -188,158 +118,135 @@ }

/**
* Add child `App`s to this `App`
* Gets the value of internal `_isRunning` flag
*
* @public
* @method addChildApps
* @method isRunning
* @memberOf App
* @param {Object} childApps - Hash of names and `AppClass` or `appConfig`
* @returns {Boolean}
*/
addChildApps: function(childApps) {
_.each(childApps, function(childApp, appName) {
this.addChildApp(appName, childApp);
}, this);
isRunning() {
return this._isRunning;
},
/**
* Build's childApp and registers it with this App
* Starts the childApp, if this app is running and child is `startWithParent`
* Sets the app lifecycle to running.
*
* @public
* @method addChildApp
* @method start
* @memberOf App
* @param {String} appName - Name of App to register
* @param {App} AppClass - An App Class
* @param {Object} AppClass - Optionally passed as an appConfig Object
* @param {Object} [options] - options for the AppClass
* @throws AddChildAppError - Thrown if no childApp could be built from params
* @param {Object} [options] - Settings for the App passed through to events
* @event App#before:start - passes options
* @returns {App}
*/
addChildApp: function(appName, AppClass, options) {
this._ensureAppIsUnique(appName);
start(options) {
this._ensureAppIsIntact();
var childApp = this._buildApp(AppClass, options);
if(!childApp){
throw new Marionette.Error({
name: 'AddChildAppError',
message: 'App build failed. Incorrect configuration.'
});
if(this._isRunning) {
return this;
}
childApp._name = appName;
this.triggerMethod('before:start', options);
this._childApps[appName] = childApp;
this._isRunning = true;
// When the app is destroyed remove the cached app.
childApp.on('destroy', _.partial(this._removeChildApp, appName), this);
this.triggerStart(options);
if(this.isRunning() && _.result(childApp, 'startWithParent')) {
childApp.start();
}
return childApp;
return this;
},
/**
* Returns registered child `App`s name
* Triggers start event.
* Override to introduce async start
*
* @public
* @method getName
* @method triggerStart
* @memberOf App
* @returns {String}
* @param {Object} [options] - Settings for the App passed through to events
* @event App#start - passes options
* @returns
*/
getName: function() {
return this._name;
triggerStart(options) {
this.triggerMethod('start', options);
},
/**
* Returns registered child `App`s array
* "Restarts the app" by first stoping app, reinitializing state, and then starting the app again
*
*
* @public
* @method getChildApps
* @method restart
* @memberOf App
* @returns {Array}
* @param {Object} [options] - Settings for the App passed through to events
* @returns {App}
*/
getChildApps: function(){
return _.clone(this._childApps);
restart(options) {
this.stop(options);
this.initState(options);
this.start(options);
return this;
},
/**
* Returns registered child `App`
* Sets the app lifecycle to not running.
* Removes any listeners added during the running state
*
* @public
* @method getChildApp
* @method stop
* @memberOf App
* @param {String} appName - Name of App to retrieve
* @param {Object} [options] - Settings for the App passed through to events
* @event App#before:stop - passes options
* @event App#stop - passes options
* @returns {App}
*/
getChildApp: function(appName) {
return this._childApps[appName];
},
stop(options) {
if(!this._isRunning) {
return this;
}
/**
* Internal helper. Unregisters child `App`
*
* @private
* @method _removeChildApp
* @memberOf App
* @param {String} appName - Name of App to unregister
* @returns {App}
*/
_removeChildApp: function(appName) {
delete this._childApps[appName]._name;
delete this._childApps[appName];
this.triggerMethod('before:stop', options);
this._isRunning = false;
this.triggerMethod('stop', options);
this._stopRunningListeners();
this._stopRunningEvents();
return this;
},
/**
* Removes all childApps and returns them.
* The return is useful if any app is using `preventDestroy`
* Gets the value of internal `_isDestroyed` flag
*
* @public
* @method removeChildApps
* @method isDestroyed
* @memberOf App
* @returns {Array}
* @returns {Boolean}
*/
removeChildApps: function(){
var childApps = this.getChildApps();
_.each(this._childApps, function(childApp, appName) {
this.removeChildApp(appName);
}, this);
return childApps;
isDestroyed() {
return this._isDestroyed;
},
/**
* Destroys or removes registered child `App` by name
* depending on `preventDestroy`
* Stops the `App` and sets it destroyed.
*
* @public
* @method removeChildApp
* @method destroy
* @memberOf App
* @param {String} appName - Name of App to destroy
* @param {Object} [options.preventDestroy] - Flag to remove but prevent App destroy
* @returns {App}
*/
removeChildApp: function(appName, options) {
options = _.extend({}, options);
var childApp = this.getChildApp(appName);
if(!childApp) {
destroy() {
if(this._isDestroyed) {
return;
}
// if preventDestroy simply unregister the child app
if(options.preventDestroy || _.result(childApp, 'preventDestroy')) {
this._removeChildApp(appName);
} else {
childApp.destroy();
}
this.stop();
return childApp;
Marionette.Object.prototype.destroy.apply(this, arguments);
this._isDestroyed = true;
}
});
_.extend(App.prototype, StateMixin, ChildAppsMixin, EventListenersMixin);
export default App;
import _ from 'underscore';
import Backbone from 'backbone';
import Marionette from 'backbone.marionette';
import StateClass from './state-class';
import StateMixin from './mixins/state';
const ClassOpions = [
'ViewClass',
'viewEventPrefix',
'viewOptions',
'region'
];
/**
* Reusable StateClass with View management boilerplate
* Reusable Marionette.Object with View management boilerplate
*

@@ -14,3 +20,3 @@ * @public

*/
var Component = StateClass.extend({
const Component = Marionette.Object.extend({

@@ -42,4 +48,4 @@ /**

* @constructs Component
* @param {Object} [stateAttrs] - Attributes to set on the state model.
* @param {Object} [options] - Settings for the component.
* @param {Object} [options.state] - Attributes to set on the state model.
* @param {Mn.ItemView|Mn.CollectionView|Mn.CompositeView|Mn.LayoutView=} [options.ViewClass]

@@ -52,11 +58,9 @@ * - The view class to be managed.

*/
constructor: function(stateAttrs, options){
options = _.extend({}, options);
constructor(options = {}) {
// Make defaults available to this
_.extend(this, _.pick(options, ['viewEventPrefix', 'ViewClass', 'viewOptions', 'region']));
this.mergeOptions(options, ClassOpions);
StateClass.call(this, options);
this.initState(options);
this._setStateDefaults(stateAttrs);
Marionette.Object.call(this, options);
},

@@ -75,15 +79,2 @@

/**
* Set the state model attributes to the initial
* passed in attributes or any defaults set
*
* @private
* @method _setStateDefaults
* @memberOf Component
* @param {Object} [stateAttrs] - Attributes to set on the state model
*/
_setStateDefaults: function(stateAttrs){
this.setState(stateAttrs, { silent: true });
},
/**
* Set the Component's region and then show it.

@@ -98,3 +89,3 @@ *

*/
showIn: function(region, viewOptions) {
showIn(region, viewOptions) {
this.region = region;

@@ -120,3 +111,3 @@

*/
show: function(viewOptions){
show(viewOptions) {
if(this._isShown) {

@@ -161,17 +152,15 @@ throw new Marionette.Error({

*/
_getViewClass: function(options) {
options = _.extend({}, options);
_getViewClass(options = {}) {
const ViewClass = this.ViewClass;
var ViewClass = this.getOption('ViewClass');
if (ViewClass.prototype instanceof Backbone.View || ViewClass === Backbone.View) {
if(ViewClass.prototype instanceof Backbone.View || ViewClass === Backbone.View) {
return ViewClass;
} else if (_.isFunction(ViewClass)) {
} else if(_.isFunction(ViewClass)) {
return ViewClass.call(this, options);
} else {
throw new Marionette.Error({
name: 'InvalidViewClassError',
message: '"ViewClass" must be a view class or a function that returns a view class'
});
}
throw new Marionette.Error({
name: 'InvalidViewClassError',
message: '"ViewClass" must be a view class or a function that returns a view class'
});
},

@@ -190,8 +179,8 @@

*/
renderView: function(options){
var ViewClass = this._getViewClass(options);
renderView(options) {
const ViewClass = this._getViewClass(options);
var viewOptions = this.mixinOptions(options);
const viewOptions = this.mixinOptions(options);
var view = this.buildView(ViewClass, viewOptions);
const view = this.buildView(ViewClass, viewOptions);

@@ -230,10 +219,10 @@ // Attach current built view to component

*/
_proxyViewEvents: function(view){
var prefix = this.getOption('viewEventPrefix');
_proxyViewEvents(view) {
const prefix = this.viewEventPrefix;
view.on('all', function() {
var args = _.toArray(arguments);
var rootEvent = args[0];
const args = _.toArray(arguments);
const rootEvent = args[0];
args[0] = prefix + ':' + rootEvent;
args[0] = `${ prefix }:${ rootEvent }`;
args.splice(1, 0, view);

@@ -246,3 +235,3 @@

/**
* Mixin stateModel from StateClass with any other viewOptions
* Mixin initial State with any other viewOptions
*

@@ -256,6 +245,6 @@ * @public

*/
mixinOptions: function(options){
var viewOptions = _.result(this, 'viewOptions');
mixinOptions(options) {
const viewOptions = _.result(this, 'viewOptions');
return _.extend({ stateModel: this.getState() }, viewOptions, options);
return _.extend({ state: this.getState().attributes }, viewOptions, options);
},

@@ -276,3 +265,3 @@

*/
buildView: function(ViewClass, viewOptions) {
buildView(ViewClass, viewOptions) {
return new ViewClass(viewOptions);

@@ -288,5 +277,5 @@ },

*/
_destroy: function(){
_destroy() {
if(this._shouldDestroy) {
StateClass.prototype.destroy.apply(this, arguments);
Marionette.Object.prototype.destroy.apply(this, arguments);
}

@@ -303,7 +292,7 @@ },

*/
_emptyRegion: function(options){
if(this.region) {
this.stopListening(this.region, 'empty');
this.region.empty(options);
}
_emptyRegion(options) {
if(this.region) {
this.stopListening(this.region, 'empty');
this.region.empty(options);
}
},

@@ -319,11 +308,13 @@

*/
destroy: function(options){
this._emptyRegion(options);
destroy(options) {
this._emptyRegion(options);
this._shouldDestroy = true;
this._shouldDestroy = true;
this._destroy(options);
this._destroy(options);
}
});
_.extend(Component.prototype, StateMixin);
export default Component;
import Marionette from 'backbone.marionette';
import StateClass from './state-class';
import StateMixin from './mixins/state';
import App from './app';

@@ -11,5 +11,5 @@ import Component from './component';

var previousToolkit = Marionette.Toolkit;
const previousToolkit = Marionette.Toolkit;
var Toolkit = Marionette.Toolkit = {};
const Toolkit = Marionette.Toolkit = {};

@@ -21,6 +21,16 @@ Toolkit.noConflict = function() {

Toolkit.VERSION = '<%= version %>';
Toolkit.MixinState = function(classDefinition) {
let _StateMixin = StateMixin;
Toolkit.StateClass = StateClass;
if(classDefinition.prototype.StateModel) {
_StateMixin = _.omit(StateMixin, 'StateModel');
}
_.extend(classDefinition.prototype, _StateMixin);
};
Toolkit.VERSION = '<%VERSION%>';
Toolkit.StateMixin = StateMixin;
Toolkit.App = App;

@@ -27,0 +37,0 @@

@@ -1,15 +0,13 @@

if (!global.document || !global.window) {
if(!global.document || !global.window) {
const jsdom = require('jsdom').jsdom;
var jsdom = require('jsdom').jsdom;
global.document = jsdom('<html><head><script></script></head><body><div id="testDiv"></div></body></html>', null, {
FetchExternalResources : ['script'],
ProcessExternalResources : ['script'],
MutationEvents : '2.0',
QuerySelector : false
global.document = jsdom('<html><head><script></script></head><body><div id="testDiv"></div></body></html>', {
FetchExternalResources: ['script'],
ProcessExternalResources: ['script'],
MutationEvents: '2.0',
QuerySelector: false
});
global.window = document.parentWindow;
global.window = document.defaultView;
global.navigator = global.window.navigator;
global.location = global.window.location;
}

@@ -23,3 +21,3 @@

require('babel/register');
require('babel-register');
require('./setup')();
module.exports = function() {
var _ = require('underscore');
var Backbone = require('backbone');
var $ = require('jquery');
const _ = require('underscore');
const Backbone = require('backbone');
const $ = require('jquery');
Backbone.$ = $;
var Marionette = require('backbone.marionette');
const Marionette = require('backbone.marionette');
require('../../src/marionette.toolkit');
// Set up test div
var $testDiv = $('#testDiv');
const $testDiv = $('#testDiv');
var setFixtures = function () {
_.each(arguments, function (content) {
const setFixtures = function() {
_.each(arguments, function(content) {
$testDiv.append(content);

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

var clearFixtures = function () {
const clearFixtures = function() {
$testDiv.empty();

@@ -33,4 +33,4 @@ };

global.stub = this.sinon.stub.bind(this.sinon);
global.spy = this.sinon.spy.bind(this.sinon);
this.setFixtures = setFixtures;
global.spy = this.sinon.spy.bind(this.sinon);
this.setFixtures = setFixtures;
this.clearFixtures = clearFixtures;

@@ -37,0 +37,0 @@ });

function createNewApp(startWParent, stopWParent, prevDestroy) {
return new Marionette.Toolkit.App({
childApps : {
myChildAppOne : {
AppClass : Marionette.Toolkit.App,
startWithParent : startWParent,
stopWithParent : stopWParent,
preventDestroy : prevDestroy
childApps: {
myChildAppOne: {
AppClass: Marionette.Toolkit.App,
startWithParent: startWParent,
stopWithParent: stopWParent,
preventDestroy: prevDestroy
}

@@ -14,96 +14,99 @@ }

describe('app-lifecycle-options', function () {
describe('when starting the application', function () {
it('should start automatically with startAfterInitialized set to true', function() {
this.autoStartApp = new Marionette.Toolkit.App({
'startAfterInitialized' : true
});
expect(this.autoStartApp._isRunning).to.equal(true);
});
describe('app-lifecycle-options', function() {
describe('when starting the application', function() {
it('should start automatically with startAfterInitialized set to true', function() {
this.autoStartApp = new Marionette.Toolkit.App({
'startAfterInitialized': true
});
expect(this.autoStartApp._isRunning).to.equal(true);
});
describe('and adding a childApp with startWithParent = true after parent has started', function () {
it('should start the childApp', function () {
describe('and adding a childApp with startWithParent = true after parent has started', function() {
it('should start the childApp', function() {
this.myApp = new Marionette.Toolkit.App();
this.myApp.start();
this.myApp.addChildApp('myAddedChild', Marionette.Toolkit.App, {
startWithParent : true
startWithParent: true
});
var test = this.myApp.getChildApp('myAddedChild');
const test = this.myApp.getChildApp('myAddedChild');
expect(test.isRunning()).to.equal(true);
});
});
});
});
describe('when starting the application with child apps', function () {
describe('and startWithParent = false', function () {
it('should not start the childApp on parent start', function () {
this.myApp = createNewApp(false, false, false);
this.myApp.start();
var test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(false);
});
});
describe('when starting the application with child apps', function() {
describe('and startWithParent = false', function() {
it('should not start the childApp on parent start', function() {
this.myApp = createNewApp(false, false, false);
this.myApp.start();
const test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(false);
});
});
describe('and startWithParent = true', function () {
it('should start the childApp on parent start', function () {
this.myApp = createNewApp(true, false, false);
this.myApp.start();
var test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(true);
});
});
});
describe('and startWithParent = true', function() {
it('should start the childApp on parent start', function() {
this.myApp = createNewApp(true, false, false);
this.myApp.start();
describe('when stopping the application with child apps', function () {
describe('and stopWithParent = false', function () {
it('should not stop the childApp on parent stop', function () {
this.myApp = createNewApp(true, false, false);
this.myApp.start();
this.myApp.stop();
var test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(true);
});
});
const test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(true);
});
});
});
describe('and stopWithParent = true', function () {
it('should stop the childApp on parent stop', function () {
this.myApp = createNewApp(true, true, false);
this.myApp.start();
this.myApp.stop();
var test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(false);
});
});
});
describe('when stopping the application with child apps', function() {
describe('and stopWithParent = false', function() {
it('should not stop the childApp on parent stop', function() {
this.myApp = createNewApp(true, false, false);
this.myApp.start();
this.myApp.stop();
describe('when destroying the application with child apps', function () {
describe('and preventDestroy = false', function () {
it('should not destroy the childApp on parent destroy', function () {
this.myApp = createNewApp(true, false, false);
var test = this.myApp.getChildApp('myChildAppOne');
this.myApp.destroy();
expect(test.isDestroyed()).to.equal(true);
});
});
const test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(true);
});
});
describe('and preventDestroy = true', function () {
it('should destroy the childApp on parent destroy', function () {
this.myApp = createNewApp(true, false, true);
var test = this.myApp.getChildApp('myChildAppOne');
this.myApp.destroy();
expect(test.isDestroyed()).to.equal(false);
});
});
});
describe('and stopWithParent = true', function() {
it('should stop the childApp on parent stop', function() {
this.myApp = createNewApp(true, true, false);
this.myApp.start();
this.myApp.stop();
const test = this.myApp.getChildApp('myChildAppOne').isRunning();
expect(test).to.equal(false);
});
});
});
describe('when removing a child app from the application, the child app', function () {
it('should not be destroyed if preventDestroy = true', function () {
describe('when destroying the application with child apps', function() {
describe('and preventDestroy = false', function() {
it('should not destroy the childApp on parent destroy', function() {
this.myApp = createNewApp(true, false, false);
const test = this.myApp.getChildApp('myChildAppOne');
this.myApp.destroy();
expect(test.isDestroyed()).to.equal(true);
});
});
describe('and preventDestroy = true', function() {
it('should destroy the childApp on parent destroy', function() {
this.myApp = createNewApp(true, false, true);
const test = this.myApp.getChildApp('myChildAppOne');
this.myApp.destroy();
expect(test.isDestroyed()).to.equal(false);
});
});
});
describe('when removing a child app from the application, the child app', function() {
it('should not be destroyed if preventDestroy = true', function() {
this.myApp = createNewApp(true, false, true);
var test = this.myApp.removeChildApp('myChildAppOne');
const test = this.myApp.removeChildApp('myChildAppOne');
expect(test.isDestroyed()).to.equal(false);
});
it('should be destroyed if preventDestroy = false', function () {
it('should be destroyed if preventDestroy = false', function() {
this.myApp = createNewApp(true, false, false);
var test = this.myApp.removeChildApp('myChildAppOne');
const test = this.myApp.removeChildApp('myChildAppOne');
expect(test.isDestroyed()).to.equal(true);

@@ -110,0 +113,0 @@ });

@@ -1,5 +0,5 @@

import AbstractApp from '../../src/abstract-app';
import App from '../../src/app';
describe('App-Lifecycle', function () {
beforeEach(function () {
describe('App-Lifecycle', function() {
beforeEach(function() {
this.beforeStartStub = this.sinon.stub();

@@ -10,3 +10,3 @@ this.startStub = this.sinon.stub();

this.destroyStub = this.sinon.stub();
this.myApp = new AbstractApp();
this.myApp = new App();
this.myApp.on('before:start', this.beforeStartStub);

@@ -19,21 +19,21 @@ this.myApp.on('start', this.startStub);

describe('when starting the application', function () {
beforeEach(function () {
describe('when starting the application', function() {
beforeEach(function() {
this.myApp.start();
});
it('should trigger before:start event', function () {
it('should trigger before:start event', function() {
expect(this.beforeStartStub).to.have.been.calledOnce;
});
it('should trigger start event', function () {
it('should trigger start event', function() {
expect(this.startStub).to.have.been.calledOnce;
});
it('should be successfully started', function () {
it('should be successfully started', function() {
expect(this.myApp._isRunning).to.equal(true);
});
describe('when the application has already been started', function () {
it('should not start the app again and not trigger before:start twice', function () {
describe('when the application has already been started', function() {
it('should not start the app again and not trigger before:start twice', function() {
this.myApp.start();

@@ -43,3 +43,3 @@ expect(this.beforeStartStub).to.have.not.been.calledTwice;

it('should not start the app again and not trigger start twice', function () {
it('should not start the app again and not trigger start twice', function() {
this.myApp.start();

@@ -50,3 +50,3 @@ expect(this.startStub).to.have.not.been.calledTwice;

describe('and stopping the application', function () {
describe('and stopping the application', function() {
beforeEach(function() {

@@ -56,15 +56,15 @@ this.myApp.stop();

it('should stop the application', function () {
it('should stop the application', function() {
expect(this.myApp._isRunning).to.equal(false);
});
it('should call before:stop', function () {
it('should call before:stop', function() {
expect(this.beforeStopStub).to.have.been.calledOnce;
});
it('should call stop', function () {
it('should call stop', function() {
expect(this.stopStub).to.have.been.calledOnce;
});
it('should NOT stop the application if the application has already been stopped and not call before:stop twice', function () {
it('should NOT stop the application if the application has already been stopped and not call before:stop twice', function() {
this.myApp.stop();

@@ -74,10 +74,43 @@ expect(this.beforeStopStub).to.have.not.been.calledTwice;

it('should NOT stop the application if the application has already been stopped and not call stop twice', function () {
it('should NOT stop the application if the application has already been stopped and not call stop twice', function() {
expect(this.stopStub).to.have.not.been.calledTwice;
});
});
describe('and restarting the application', function() {
beforeEach(function() {
this.sinon.spy(this.myApp, 'initState');
this.myApp.restart();
});
it('should call stop', function() {
expect(this.stopStub).to.have.been.calledOnce;
});
it('should reinitialize state', function() {
expect(this.myApp.initState).to.have.been.calledOnce;
});
it('should trigger start event', function() {
expect(this.startStub).to.have.been.calledTwice;
});
describe('passing state argument', function() {
beforeEach(function() {
this.myApp.restart({
state: {
foo: 'bar'
}
});
});
it('should have reinitialize state with passed in state', function() {
expect(this.myApp.getState('foo')).to.equal('bar');
});
});
});
});
describe('when an application is yet to be destroyed', function () {
it('should have isDestroyed() to return false', function () {
describe('when an application is yet to be destroyed', function() {
it('should have isDestroyed() to return false', function() {
expect(this.myApp.isDestroyed()).to.equal(false);

@@ -87,4 +120,4 @@ });

describe('when destroying an application', function () {
beforeEach(function () {
describe('when destroying an application', function() {
beforeEach(function() {
this.myApp.start();

@@ -94,13 +127,13 @@ this.myApp.destroy();

it('should be stopped', function () {
it('should be stopped', function() {
expect(this.stopStub).to.have.been.calledOnce;
});
it('should successfully be destroyed', function () {
it('should successfully be destroyed', function() {
expect(this.myApp.isDestroyed()).to.equal(true);
});
describe('and restarting the destroyed application', function () {
it('should throw an error', function () {
expect(_.bind(function(){
describe('and restarting the destroyed application', function() {
it('should throw an error', function() {
expect(_.bind(function() {
this.myApp.start();

@@ -111,14 +144,12 @@ }, this)).to.throw('App has already been destroyed and cannot be used.');

describe('and destroying it again', function () {
beforeEach(function () {
describe('and destroying it again', function() {
beforeEach(function() {
this.myApp.destroy();
});
it('should not destroy', function () {
it('should not destroy', function() {
expect(this.destroyStub).to.have.not.been.calledTwice;
});
});
});
});

@@ -1,7 +0,7 @@

describe('Marionette.Toolkit.Component', function () {
beforeEach(function () {
describe('Marionette.Toolkit.Component', function() {
beforeEach(function() {
this.setFixtures('<div id="testRegion"></div>');
this.el = Backbone.$('#testRegion');
this.myRegion = new Backbone.Marionette.Region({
el:this.el
el: this.el
});

@@ -11,4 +11,4 @@ });

// SHOWING A VIEW
describe('when showing a component', function () {
beforeEach(function () {
describe('when showing a component', function() {
beforeEach(function() {
this.beforeShowStub = this.sinon.stub();

@@ -28,4 +28,4 @@ this.showStub = this.sinon.stub();

describe('in a specified region "showIn()"', function () {
it('should show the component', function () {
describe('in a specified region "showIn()"', function() {
it('should show the component', function() {
this.myComponent.showIn(this.myRegion);

@@ -35,3 +35,3 @@ expect(this.myComponent._isShown).to.equal(true);

it('should fire the before:show methods', function () {
it('should fire the before:show methods', function() {
this.myComponent.showIn(this.myRegion);

@@ -41,3 +41,3 @@ expect(this.beforeShowStub).to.have.been.calledOnce;

it('should fire the show methods', function () {
it('should fire the show methods', function() {
this.myComponent.showIn(this.myRegion);

@@ -47,3 +47,3 @@ expect(this.showStub).to.have.been.calledOnce;

it('should throw a "no defined region" error when no region is defined', function () {
it('should throw a "no defined region" error when no region is defined', function() {
expect(_.bind(function() {

@@ -54,3 +54,3 @@ this.myComponent.showIn();

it('should throw a "already been shown" error if component has been shown', function () {
it('should throw a "already been shown" error if component has been shown', function() {
this.myComponent.showIn(this.myRegion);

@@ -63,4 +63,4 @@ expect(_.bind(function() {

describe('in a region on the component definition "show()"', function () {
it('should show the component', function () {
describe('in a region on the component definition "show()"', function() {
it('should show the component', function() {
this.ShowComponent = this.MyComponent.extend({

@@ -79,4 +79,4 @@ region: this.myRegion

// RENDERING A VIEW WITH renderView()
describe('when rendering a view', function () {
beforeEach(function () {
describe('when rendering a view', function() {
beforeEach(function() {
this.beforeRenderStub = this.sinon.stub();

@@ -97,3 +97,3 @@ this.renderStub = this.sinon.stub();

it('should fire the before:render:view methods', function () {
it('should fire the before:render:view methods', function() {
this.myComponent.renderView({

@@ -105,3 +105,3 @@ className: 'other-component-class'

it('should fire the render:view methods', function () {
it('should fire the render:view methods', function() {
this.myComponent.renderView({

@@ -113,4 +113,4 @@ className: 'other-component-class'

describe('and checking the currentView', function () {
it('should have the correct className on currentView', function () {
describe('and checking the currentView', function() {
it('should have the correct className on currentView', function() {
this.myComponent.renderView({

@@ -123,6 +123,6 @@ className: 'my-component-class'

describe('with a defined ViewClass', function () {
it('should return the correct ViewClass', function () {
describe('with a defined ViewClass', function() {
it('should return the correct ViewClass', function() {
this.myComponent.renderView();
var test = this.myComponent.currentView;
const test = this.myComponent.currentView;
expect(test).to.be.instanceof(this.MyViewClass);

@@ -133,4 +133,4 @@ });

// The test for mixinOptions()
describe('with additional options passed in', function () {
it('should put the options on currentView', function () {
describe('with additional options passed in', function() {
it('should put the options on currentView', function() {
this.myComponent.renderView({

@@ -149,4 +149,4 @@ foo: 'bar'

region: this.myRegion,
ViewClass: function(options){
if(options.foo){
ViewClass: function(options) {
if(options.foo) {
return Marionette.ItemView.extend({

@@ -185,5 +185,19 @@ customViewOption: 'bar',

// INSTANTIATING A COMPONENT WITH OPTIONS
describe('when instantiating a component', function () {
describe('with a customized viewEventPrefix', function () {
it('should trigger the correct action as defined', function () {
describe('when instantiating a component', function() {
describe('with state options', function() {
it('should initialize stateModel with passed in state', function() {
this.MyComponent = Marionette.Toolkit.Component.extend();
this.myComponent = new this.MyComponent({
state: {
foo: 'bar'
}
});
expect(this.myComponent.getState('foo')).to.equal('bar');
});
});
describe('with a customized viewEventPrefix', function() {
it('should trigger the correct action as defined', function() {
this.MyComponent = Marionette.Toolkit.Component.extend({

@@ -199,3 +213,3 @@ viewEventPrefix: 'some:prefix',

this.myComponent.showIn(this.myRegion);
this.myComponent.on('some:prefix:render', function(){
this.myComponent.on('some:prefix:render', function() {
this.testRender = true;

@@ -209,3 +223,3 @@ });

describe('with defined viewOptions', function () {
describe('with defined viewOptions', function() {
beforeEach(function() {

@@ -219,4 +233,4 @@ this.MyView = Marionette.ItemView.extend({

describe('on the view instance', function () {
it('should do what...', function () {
describe('on the view instance', function() {
it('should do what...', function() {
this.MyComponent = Marionette.Toolkit.Component.extend({

@@ -236,15 +250,15 @@ ViewClass: this.MyView,

describe('as specified as a function', function () {
it('should do what...', function () {
describe('as specified as a function', function() {
it('should do what...', function() {
this.MyComponent = Marionette.Toolkit.Component.extend({
ViewClass: this.MyView,
region: this.myRegion,
viewOptions: {
foo: 'bar2',
template: false
}
});
this.myComponent = new this.MyComponent();
this.myComponent.renderView();
expect(this.myComponent.currentView.test).to.equal('bar2');
ViewClass: this.MyView,
region: this.myRegion,
viewOptions: {
foo: 'bar2',
template: false
}
});
this.myComponent = new this.MyComponent();
this.myComponent.renderView();
expect(this.myComponent.currentView.test).to.equal('bar2');
});

@@ -256,5 +270,5 @@ });

// DESTROYING COMPONENTS
describe('when destroying a component', function () {
describe('with a defined region', function () {
it('should be destroyed and region emptied', function () {
describe('when destroying a component', function() {
describe('with a defined region', function() {
it('should be destroyed and region emptied', function() {
this.MyComponent = Marionette.Toolkit.Component.extend({

@@ -268,4 +282,4 @@ region: this.myRegion

describe('without a defined region', function () {
it('should be destroyed', function () {
describe('without a defined region', function() {
it('should be destroyed', function() {
this.myComponent = new Marionette.Toolkit.Component();

@@ -276,4 +290,4 @@ this.myComponent.destroy();

describe('by showing a new view in the region', function () {
beforeEach(function () {
describe('by showing a new view in the region', function() {
beforeEach(function() {
this.destroyEvent = this.sinon.stub();

@@ -290,3 +304,3 @@ this.MyComponent = Marionette.Toolkit.Component.extend({

it('should trigger a destroy event on the component', function () {
it('should trigger a destroy event on the component', function() {
this.myRegion.show(new Marionette.ItemView({

@@ -298,3 +312,3 @@ template: false

it('should not trigger a destroy event on rendering a view after show', function () {
it('should not trigger a destroy event on rendering a view after show', function() {
this.myComponent.renderView();

@@ -301,0 +315,0 @@ expect(this.destroyEvent).to.have.not.been.called;

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

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