Socket
Book a DemoInstallSign in
Socket

muscles

Package Overview
Dependencies
Maintainers
2
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

muscles - npm Package Compare versions

Comparing version

to
0.2.0

1960

muscles.js
/*!
* Muscles.js v0.1.0 Building muscle mass for your Backbone application!
* Muscles.js v0.2.0 Building muscle mass for your Backbone application!
* http://musclesjs.org

@@ -28,153 +28,9 @@ *

// Make Backbone accessible via the Muscles namespace.
Muscles.Backbone = Backbone;
// Current version of the library. Keep in sync with `package.json` and
// `bower.json`.
Muscles.VERSION = '0.1.0';
Muscles.VERSION = '0.2.0';
/**
* Create a new bundle.
*
* @class Muscles.Bundle
* @constructor
* @module Muscles
*/
var Bundle = Muscles.Bundle = function() {};
// Attach all inheritable methods to the bundle prototype.
_.extend(Bundle.prototype, {
/**
* Extend the context with a set of extensions.
*
* @example
* var bundle = Muscles.Bundle.extend({
* extend: function(context) {
* context.install([
* BoatExtension,
* TreasureExtension,
* EyepatchExtension
* ]);
* }
* });
*
* var context = new Muscles.Context();
* context.install(bundle);
* @method extend
* @param {Muscles.Context} context The context where this bundle is
* being installed to.
*/
extend: function(context) {}
});
// Extend bundle with the ability to bind an trigger custom named events.
_.extend(Bundle.prototype, Backbone.Events);
// Set up inheritance for extension.
Bundle.extend = Backbone.Model.extend;
/**
* Create a new context.
*
* @class Muscles.Context
* @constructor
* @module Muscles
*/
var Context = Muscles.Context = function() {
// Create new injector instance.
this.injector = new Backbone.Muscles.Injector();
// Map this context instance and injector.
this.injector.map('context').toValue(this);
this.injector.map('injector').toValue(this.injector);
};
// Attach all inheritable methods to the context prototype.
_.extend(Context.prototype, {
/**
* @property injector
* @type Muscles.Injector
*/
injector: null,
/**
* Install a single or multiple bundles and extensions to extend your
* application logic.
*
* @chainable
* @method install
* @param {Array|Muscles.Bundle|Muscles.Extension} extensions The
* extensions or bundles to install, which also may be an array.
* @return {Muscles.Context} The context instance.
*/
install: function(extensions) {
// Make sure extensions is an array.
if (!_.isArray(extensions)) {
extensions = [extensions];
}
// Loop over dependencies and install in order.
_.each(extensions, function(clazz) {
// Create extension instance and inject dependencies.
var extension = this.injector.instantiate(clazz);
// Validate extension.
if (!(extension instanceof Backbone.Muscles.Bundle) && !(extension instanceof Backbone.Muscles.Extension)) {
throw 'Extension is not of type Muscles.Bundle or Muscles.Extension';
}
// Extend the context instance
extension.extend(this);
}, this);
return this;
}
});
// Extend context with the ability to bind an trigger custom named events.
_.extend(Context.prototype, Backbone.Events);
// Set up inheritance for Context.
Context.extend = Backbone.Model.extend;
/**
* Create a new extension.
*
* @class Muscles.Extension
* @constructor
* @module Muscles
*/
var Extension = Muscles.Extension = function() {};
// Attach all inheritable methods to the Extension.Context prototype.
_.extend(Extension.prototype, {
/**
* Extend the context with additional functionality. This class can be
* used as an abstract base class where you can override the extend
* method.
*
* @example
* var extension = Backbone.Muscles.Extension.extend({
* extend: function(context) {
* context.injector.map('purpose').toValue('Sink the ship!');
* }
* });
*
* var context = new Backbone.Muscles.Context();
* context.install(extension);
*
* var purpose = context.injector.get('purpose');
* @method extend
* @param {Muscles.Context} context The context where this extension is
* being installed to.
*/
extend: function(context) {}
});
// Extend extension with the ability to bind an trigger custom named
// events.
_.extend(Extension.prototype, Backbone.Events);
// Set up inheritance for extension.
Extension.extend = Backbone.Model.extend;
/**
* Create a new mapping.
*
* @class Muscles.InjectorMapping

@@ -184,11 +40,9 @@ * @constructor

*/
var Mapping = Muscles.InjectorMapping = function() {};
var InjectorMapping = Muscles.InjectorMapping = function() {};
// Attach all inheritable methods to the Muscles.InjectorMapping prototype.
_.extend(Mapping.prototype, {
// Attach all inheritable methods to the InjectorMapping prototype.
_.extend(InjectorMapping.prototype, {
/**
* Determine whether a request should be fulfilled with a singleton
* instance or not.
*
* @property isSingleton
* @public
* @type Boolean

@@ -199,5 +53,4 @@ */

/**
* The factory type.
*
* @property type
* @public
* @type Function

@@ -208,6 +61,5 @@ */

/**
* The value to return.
*
* @property value
* @type Mixed
* @public
* @type *
*/

@@ -217,19 +69,9 @@ value: null,

/**
* Map a type as a singleton factory.
*
* @chainable
* @example
* injector.map('model').toSingleton(Backbone.Model);
*
* // get the model instance (or create one if not exists)
* var model = injector.get('model');
* model.set('job', 'Pirate');
* @method toSingleton
* @param {Function} value The type to map.
* @return {Muscles.InjectorMapping} The InjectorMapping instance.
* @param {Function} type
* @public
* @return {Muscles.InjectorMapping}
*/
toSingleton: function(type) {
this.isSingleton = true;
this.type = type;
// reset value if rule is overridden with another factory type

@@ -239,21 +81,15 @@ if (this.type !== type) {

}
this.isSingleton = true;
this.type = type;
return this;
},
/**
* Map a type as a factory.
*
* @chainable
* @example
* injector.map('view').toSingleton(Backbone.View);
*
* // create a new view instance
* var header = injector.get('view');
* header.render();
*
* // create a second new view instance
* var footer = injector.get('view');
* footer.render();
* @method toType
* @param {Function} value The type to map.
* @return {Muscles.InjectorMapping} The InjectorMapping instance.
* @param {Function} type
* @public
* @return {Muscles.InjectorMapping}
*/

@@ -264,18 +100,17 @@ toType: function(type) {

this.value = null;
return this;
},
/**
* Map a value.
*
* @chainable
* @example
* injector.map('name').toValue('Pantin Jack Cutty');
*
* var name = injector.get('name');
* @method toValue
* @param {Mixed} value The value to map.
* @return {Muscles.InjectorMapping} The InjectorMapping instance.
* @param {*} value
* @public
* @return {Muscles.InjectorMapping}
*/
toValue: function(value) {
this.value = value;
return this;
}

@@ -285,11 +120,6 @@ });

/**
* Create a new injector.
*
* @class Muscles.Injector
* @constructor
* @module Muscles
* @param {Muscles.Injector} parentInjector Optional parent injector
* instance to fulfill parent injections when mappings are not present in
* a child injector instance (see
* {{#crossLink "Muscles.Injector/getChildInjector:method"}}{{/crossLink}}).
* @param {Muscles.Injector} parentInjector
*/

@@ -301,28 +131,19 @@ var Injector = Muscles.Injector = function(parentInjector) {

if (parentInjector !== undefined) {
this.parentInjector = parentInjector;
this._parentInjector = parentInjector;
}
};
// Attach all inheritable methods to the Muscles.Injector prototype.
// Attach all inheritable methods to the Injector prototype.
_.extend(Injector.prototype, {
/**
* The parent injector instance if the injector is created via
* {{#crossLink "Muscles.Injector/getChildInjector:method"}}{{/crossLink}}.
*
* @property parentInjector
* @private
* @property _parentInjector
* @type Muscles.Injector
*/
parentInjector: null,
_parentInjector: null,
/**
* Get a mapped instance or value. If no mapping can be found the
* <code>parentInjector</code> tries to fulfill the request.
*
* @example
* injector.map('name').toValue('Pantin Jack Cutty');
*
* var name = injector.get('name');
* @method get
* @return {Mixed|null} The mapped instance, value or null if no mapping
* can be found.
* @public
* @return {*|null}
*/

@@ -334,12 +155,5 @@ get: function(key) {

/**
* Create a new injector with this injector as his parent. If there are
* no mappings found in the child injector, it will check if the parent
* injector can fulfill them.
*
* @example
* var childInjector = injector.getChildInjector();
* childInjector.map('model').toType(Backbone.Model);
*
* @method getChildInjector
* @return {Muscles.Injector} The created injector instance with this injector as his parent.
* @public
* @return {Muscles.Injector}
*/

@@ -351,8 +165,6 @@ getChildInjector: function() {

/**
* Get the keys of a target which should be fulfilled for injection.
*
* @method getInjectionPoints
* @param {String} target The target to get the injection points from.
* @return {Array|null} The retrieved keys from the target or null if
* no injection points are found.
* @param {String} target
* @public
* @return {Array|null}
*/

@@ -362,44 +174,60 @@ getInjectionPoints: function(target) {

// convert inject config to usable injection points config
if (target.inject !== undefined) {
// retrieve injection points from function
if (_.isFunction(target.inject)) {
injectionPoints = target.inject.apply(target);
// Convert inject config to usable injection points config.
if (target.injectConfig !== undefined) {
// Retrieve injection points from function.
if (_.isFunction(target.injectConfig)) {
injectionPoints = target.injectConfig.apply(target);
}
// retrieve injection points from array
else if (_.isArray(target.inject)) {
injectionPoints = target.inject;
// Retrieve injection points from array.
else if (_.isArray(target.injectConfig)) {
injectionPoints = target.injectConfig;
}
// incorrect inject config
// Retrieve injection points from properties.
else {
throw 'Invalid injection config for target ' + target;
throw 'Invalid inject config for target ' + target;
}
}
// compute output or return null if no injection config found
// Always get injection points from properties.
for (var prop in target) {
if (_.isString(target[prop]) && target[prop].indexOf('inject') === 0) {
injectionPoints.push({
key: this._getInjectionKey(target[prop], prop),
property: prop
});
}
}
// Compute output or return null if no injection config found.
return injectionPoints.length ? _.map(injectionPoints, function(value) {
return {
return _.isString(value) ? {
key: value,
property: value
};
}) : null;
} : value;
}, this) : null;
},
/**
* Get the created mapping instance, null if not found.
*
* @method getMapping
* @param {String} key The key under which the mapping should exists.
* @param {String} key
* @public
* @return {Muscles.InjectorMapping|null}
*/
getMapping: function(key) {
return this._mappings[key] || (this.parentInjector !== null ? this.parentInjector.getMapping(key) : null);
return this._mappings[key] || (this.getParentInjector() !== null ? this.getParentInjector().getMapping(key) : null);
},
/**
* Check if a mapping exists, but don't check parent injectors, for a
* given key.
*
* @method getParentInjector
* @public
* @return {Muscles.Injector|null}
*/
getParentInjector: function() {
return this._parentInjector;
},
/**
* @method hasDirectMapping
* @param {String} key The key under which the mapping should exists.
* @param {String} key
* @public
* @return {Boolean}

@@ -412,6 +240,5 @@ */

/**
* Check if a mapping exists for a given key.
*
* @method hasMapping
* @param {String} key The key under which the mapping should exists.
* @param {String} key
* @public
* @return {Boolean}

@@ -424,45 +251,29 @@ */

/**
* Check if a mapping exists on the parent injector.
*
* @method hasParentMapping
* @param {String} key The key under which the mapping should exists.
* @param {String} key
* @public
* @return {Boolean}
*/
hasParentMapping: function(key) {
return this.parentInjector !== null ? this.parentInjector.hasMapping(key) : false;
var parentInjector = this.getParentInjector();
return parentInjector !== null ? parentInjector.hasMapping(key) : false;
},
/**
* Fulfills injections for a specific target.
*
* @example
* var boatViewClass = Backbone.View.extend({
* inject: ['boatModel']
* });
*
* var boatModelClass = Backbone.Model.extend({
* defaults: {
* hasSunk: false
* }
* });
*
* // define mappings
* var injector = new Backbone.Muscles.Injector();
* injector.map('boatView').toType(boatViewClass);
* injector.map('boatModel').toType(boatModelClass);
*
* // create an injected instance of boatViewClass
* var boatView = injector.get('boatView');
*
* // now let's set some values on the view's injected model
* boatView.boatModel.set('hasSunk', true);
* @method injectInto
* @param {Object} target The target to apply the injections on.
* @return {Object} The injected target.
* @param {Object} target
* @public
* @return {Object}
*/
injectInto: function(target) {
// get injections points of the target to inject
// Get injections points of the target to inject.
var injectionPoints = this.getInjectionPoints(target);
// apply injections
// Call preInject method.
if (_.isFunction(target.preInject)) {
target.preInject.apply(target, []);
}
// Apply injections.
if (injectionPoints !== null) {

@@ -474,2 +285,7 @@ _.each(injectionPoints, function(config) {

// Call postInject method.
if (_.isFunction(target.postInject)) {
target.postInject.apply(target, []);
}
return target;

@@ -479,10 +295,6 @@ },

/**
* Create an instance of factory type <code>clazz</code> and inject
* it's depedencies.
*
* @example
* var view = injector.instantiate(Backbone.View);
* @method instantiate
* @param {Function} Clazz The factory type to create an instance from.
* @return {Object} The created instance with it's injected depedencies.
* @param {Function} Clazz
* @public
* @return {Object}
*/

@@ -496,13 +308,12 @@ instantiate: function(Clazz) {

/**
* Create a mapping for a given key.
*
* @method map
* @param {String} key The key under which the mapping will be applied.
* @return {Muscles.InjectorMapping} The created mapping instance.
* @param {String} key
* @public
* @return {Muscles.InjectorMapping}
*/
map: function(key) {
// get or create mapping
var mapping = this._mappings[key] || new Backbone.Muscles.InjectorMapping(key);
// Get or create mapping.
var mapping = this._mappings[key] || new Muscles.InjectorMapping(key);
// register mapping under key
// Register mapping under key.
this._mappings[key] = mapping;

@@ -514,11 +325,10 @@

/**
* Remove a mapping for a given key.
*
* @chainable
* @method unmap
* @param {String} key The key under which the mapping exists.
* @return {Muscles.InjectorMapping} The injector instance.
* @chainable
* @param {String} key
* @public
* @return {Muscles.Injector}
*/
unmap: function(key) {
// remove key from mappings
// Remove key from mappings.
delete this._mappings[key];

@@ -530,10 +340,17 @@

/**
* Get a mapped instance or value from the cache or create one if is
* doesn't exists in the mapping instance.
*
* @method _getInjectionKey
* @param {String} key
* @param {String} value
* @private
* @return {String}
*/
_getInjectionKey: function(key, value) {
return key.indexOf('inject:') === 0 ? key.replace('inject:', '') : value;
},
/**
* @method _getFromCacheOrCreate
* @param {String} key The key under which the mapping should exists.
* @param {String} key
* @private
* @return {Object|null} The (cached) instance or <code>null</code> if
* not mapped.
* @return {Object|null}
*/

@@ -543,20 +360,20 @@ _getFromCacheOrCreate: function(key) {

// if a mapping is found fulfill the request and return some output
// If a mapping is found fulfill the request and return some output.
if (mapping !== null) {
// return value is set (which also can be created by a
// singleton request)
// Return value is set (which also can be created by a singleton
// singleton request).
if (mapping.value !== null) {
return mapping.value;
}
// return singleton instance
// Return singleton instance.
else if (mapping.isSingleton && mapping.type !== null) {
mapping.value = this.injectInto(new mapping.type());
mapping.value = this.instantiate(mapping.type);
return mapping.value;
}
// return new instance
// Return new instance.
else if (!mapping.isSingleton && mapping.type !== null) {
return this.injectInto(new mapping.type());
return this.instantiate(mapping.type);
}
// no rules set for mapping
// No rules set for mapping.
else {

@@ -566,3 +383,3 @@ throw 'No rules set for mapping ' + key;

}
// no mapping found
// No mapping found.
else {

@@ -574,5 +391,1500 @@ throw 'No mapping found for ' + key;

// Set up inheritance for mapping and injector.
Injector.extend = Mapping.extend = Backbone.Model.extend;
/**
* @class Muscles.Bundle
* @constructor
* @module Muscles
*/
var Bundle = Muscles.Bundle = function() {};
// Attach all inheritable methods to the Bundle prototype.
_.extend(Bundle.prototype, {
/**
* @method extend
* @param {Muscles.Context} context
* @public
*/
extend: function(context) {}
});
// Set up inheritance for Bundle.
Bundle.extend = Backbone.Model.extend;
/**
* @class Muscles.Config
* @constructor
* @module Muscles
*/
var Config = Muscles.Config = function() {};
// Attach all inheritable methods to the Config prototype.
_.extend(Config.prototype, {
/**
* @method extend
* @param {Muscles.Context} context
* @public
*/
extend: function(context) {}
});
// Set up inheritance for Config.
Config.extend = Backbone.Model.extend;
/**
* @class Muscles.Extension
* @constructor
* @module Muscles
*/
var Extension = Muscles.Extension = function() {};
// Attach all inheritable methods to the Extension prototype.
_.extend(Extension.prototype, {
/**
* @method extend
* @param {Muscles.Context} context
* @public
*/
extend: function(context) {}
});
// Set up inheritance for Extension.
Extension.extend = Backbone.Model.extend;
/**
* @class Muscles.Context
* @constructor
* @module Muscles
*/
var Context = Muscles.Context = function() {
// Create new injector instance.
this._injector = new Backbone.Muscles.Injector();
// Map this context instance and injector.
this.getInjector().map('context').toValue(this);
this.getInjector().map('injector').toValue(this._injector);
// Install required extensions.
this.install([
Muscles.CommandMapExtension,
Muscles.EventMapExtension,
Muscles.MediatorMapExtension
]);
// Get instance of commandMap.
this._commandMap = this.getInjector().get('commandMap');
// Get instance of eventMap.
this._eventMap = this.getInjector().get('eventMap');
// Get instance of mediatorMap.
this._mediatorMap = this.getInjector().get('mediatorMap');
this.initialize.apply(this, arguments);
};
// Attach all inheritable methods to the Context prototype.
_.extend(Context.prototype, {
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: null,
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: null,
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: null,
/**
* @private
* @property _mediatorMap
* @type Muscles.MediatorMap
*/
_mediatorMap: null,
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
},
/**
* @method getMediatorMap
* @public
* @return {Muscles.MediatorMap}
*/
getMediatorMap: function() {
return this._mediatorMap;
},
/**
* @method initialize
* @public
*/
initialize: function() {},
/**
* @chainable
* @method install
* @param {Array|Muscles.Config|Muscles.Bundle|Muscles.Extension} extensions
* @public
* @return {Muscles.Context}
*/
install: function(extensions) {
// Make sure extensions is an array.
if (!_.isArray(extensions)) {
extensions = [extensions];
}
// Loop over dependencies and install in order.
_.each(extensions, function(clazz) {
// Create extension instance and inject dependencies.
var extension = this.getInjector().instantiate(clazz);
// Validate extension.
if (!(extension instanceof Backbone.Muscles.Config) && !(extension instanceof Backbone.Muscles.Bundle) && !(extension instanceof Backbone.Muscles.Extension)) {
throw 'Extension is not of type Muscles.Config, Muscles.Bundle or Muscles.Extension';
}
// Extend the context instance.
extension.extend(this);
}, this);
return this;
},
/**
* @method shutdown
* @public
*/
shutdown: function() {},
/**
* @method startup
* @public
*/
startup: function() {}
});
// Set up inheritance for Context.
Context.extend = Backbone.Model.extend;
/**
* @class Muscles.View
* @constructor
* @extends Backbone.View
* @module Muscles
*/
var View = Muscles.View = Backbone.View.extend({
/**
* @method getSerializedData
* @public
* @return {Object}
*/
getSerializedData: function() {
var data = {};
if (this.model) {
data = this.model.toJSON();
} else if (this.collection) {
data = {
models: this.collection.toJSON()
};
}
return data;
},
/**
* @chainable
* @method remove
* @public
* @return {Muscles.View}
*/
remove: function() {
this.trigger('preRemove', this);
Backbone.View.prototype.remove.apply(this, arguments);
this.trigger('postRemove', this);
return this;
},
/**
* @chainable
* @method render
* @public
* @return {Muscles.View}
*/
render: function() {
var serializedData = this.getSerializedData();
if (this.willRender(serializedData)) {
this.trigger('preRender', this, serializedData);
this.$el.html(Muscles.renderTemplate(this.template, serializedData));
this.trigger('postRender', this);
}
return this;
},
/**
* @chainable
* @method setElement
* @param {HTMLElement} element
* @param {Boolean} delegate
* @public
* @return {Muscles.View}
*/
setElement: function(element, delegate) {
this.trigger('preElementSet', this, element);
Backbone.View.prototype.setElement.apply(this, [element, delegate]);
this.trigger('postElementSet', this, element);
return this;
},
/**
* @method template
* @param {Object} context
* @public
* @return {String}
*/
template: function(context) {
return '';
},
/**
* @method willRender
* @property {Object} serializedData
* @public
* @return {Boolean}
*/
willRender: function(serializedData) {
return true;
}
});
/**
* @class Muscles.ModelView
* @constructor
* @extends Muscles.View
* @module Muscles
*/
var ModelView = Muscles.ModelView = Muscles.View.extend({
/**
* @property modelEvents
* @protected
* @type {Object}
*/
modelEvents: {},
/**
* @extends Muscles.View.postInject
*/
postInject: function() {
if (this.model !== undefined && this.model !== null) {
Muscles.mapEvents(this, this.model, this.modelEvents);
}
},
/**
* @extends Muscles.View.willRender
*/
willRender: function(serializedData) {
return this.model !== undefined && this.model !== null;
}
});
/**
* @class Muscles.ASyncCommand
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var ASyncCommand = Muscles.ASyncCommand = function() {
this._onComplete = null;
this._onCompleteContext = null;
};
// Attach all inheritable methods to the ASyncCommand prototype.
_.extend(ASyncCommand.prototype, {
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @private
* @property _context
* @type Muscles.Context
*/
_context: 'inject:context',
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: 'inject:eventMap',
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: 'inject:injector',
/**
* @method commandComplete
* @public
*/
commandComplete: function() {
this._onComplete.apply(this._onCompleteContext, []);
this._onComplete = null;
this._onCompleteContext = null;
},
/**
* @method execute
* @public
*/
execute: function() {},
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getContext
* @public
* @return {Muscles.Context}
*/
getContext: function() {
return this._context;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
},
/**
* @method setOnComplete
* @param {Function} fn
* @param {Muscles.ASyncMacroCommand} context
* @public
*/
setOnComplete: function(fn, context) {
this._onComplete = fn;
this._onCompleteContext = context;
}
});
// Set up inheritance for ASyncCommand.
ASyncCommand.extend = Backbone.Model.extend;
/**
* @class Muscles.ASyncMacroCommand
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var ASyncMacroCommand = Muscles.ASyncMacroCommand = function() {
this._arguments = null;
this._onComplete = null;
this._onCompleteContext = null;
this._subCommands = [];
};
// Attach all inheritable methods to the ASyncMacroCommand prototype.
_.extend(ASyncMacroCommand.prototype, {
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @private
* @property _context
* @type Muscles.Context
*/
_context: 'inject:context',
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: 'inject:eventMap',
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: 'inject:injector',
/**
* @method initialize
* @public
*/
initialize: function() {},
/**
* @chainable
* @method addSubCommand
* @param {Muscles.Command|Muscles.MacroCommand|Muscles.ASyncCommand|Muscles.ASyncMacroCommand} type
* @public
* @return {Muscles.ASyncMacroCommand}
*/
addSubCommand: function(type) {
this._subCommands.push(type);
return this;
},
/**
* @method commandComplete
* @public
*/
commandComplete: function() {
this._onComplete.apply(this._onCompleteContext, []);
this._onComplete = null;
this._onCompleteContext = null;
},
/**
* @method execute
* @public
*/
execute: function() {
// Cache arguments.
this._arguments = arguments;
// Execute first subcommand in queue.
this.executeNextCommand(this._arguments);
},
/**
* @method executeNextCommand
* @public
*/
executeNextCommand: function() {
if (this._subCommands.length) {
var command = this.getInjector().instantiate(this._subCommands.shift());
var commandIsASync = _.isFunction(command.commandComplete);
// Set async command callback.
if (commandIsASync) {
command.setOnComplete(this.executeNextCommand, this);
// Bind command context, to make async callbacks directly
// callable with the correct context.
_.bindAll(command, 'commandComplete');
}
// Execute the command with the same paypload.
command.execute.apply(command, this._arguments);
// Execute next command if no async callback is set.
if (!commandIsASync) {
this.executeNextCommand();
}
} else if (this._onComplete !== null && _.isFunction(this.commandComplete)) {
this.commandComplete();
}
},
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getContext
* @public
* @return {Muscles.Context}
*/
getContext: function() {
return this._context;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
},
/**
* @method postInject
* @public
*/
postInject: function() {
this.initialize.apply(this, []);
},
/**
* @method setOnComplete
* @param {Function} fn
* @param {Muscles.ASyncMacroCommand} context
* @public
*/
setOnComplete: function(fn, context) {
this._onComplete = fn;
this._onCompleteContext = context;
}
});
// Set up inheritance for ASyncMacroCommand.
ASyncMacroCommand.extend = Backbone.Model.extend;
/**
* @class Muscles.Command
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var Command = Muscles.Command = function() {};
// Attach all inheritable methods to the Command prototype.
_.extend(Command.prototype, {
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @private
* @property _context
* @type Muscles.Context
*/
_context: 'inject:context',
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: 'inject:eventMap',
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: 'inject:injector',
/**
* @method execute
* @public
*/
execute: function() {},
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getContext
* @public
* @return {Muscles.Context}
*/
getContext: function() {
return this._context;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
}
});
// Set up inheritance for Command.
Command.extend = Backbone.Model.extend;
/**
* @class Muscles.CommandMap
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var CommandMap = Muscles.CommandMap = function() {
this._mappings = {};
};
// Attach all inheritable methods to the CommandMap prototype.
_.extend(CommandMap.prototype, {
/**
* @property context
* @public
* @type Muscles.Context
*/
context: 'inject',
/**
* @method execute
* @param {String} key
* @public
*/
execute: function(key) {
var mapping = this.getMapping(key);
// Throw error no mapping instance exists.
if (mapping === null) {
throw new Error('Mapping not exists under key ' + key);
} else {
var Clazz = mapping.getType();
// Throw error if no command is set on mapping instance.
if (Clazz === null) {
throw new Error('Mapping does not have a command set');
} else {
var command = this.context.getInjector().instantiate(Clazz);
// Throw error if command is not of type Muscles.Command,
// Muscles.MacroCommand or Muscles.ASyncMacroCommand.
if (!(command instanceof Muscles.Command) && !(command instanceof Muscles.MacroCommand) && !(command instanceof Muscles.ASyncMacroCommand)) {
throw new Error('Command is not of type Muscles.Command or Muscles.MacroCommand');
} else {
var args = Array.prototype.slice.call(arguments, 1);
// Execute the command with paypload.
command.execute.apply(command, args);
}
}
}
},
/**
* @method getMapping
* @param {String} key
* @public
* @return {Muscles.CommandMapMapping|null}
*/
getMapping: function(key) {
return this._mappings[key] || null;
},
/**
* @method hasMapping
* @param {String} key
* @public
* @return {Boolean}
*/
hasMapping: function(key) {
return this._mappings.hasOwnProperty(key);
},
/**
* @method map
* @param {String} key
* @public
* @return {Muscles.CommandMapMapping}
*/
map: function(key) {
// Throw error if mapping already exists.
if (this.hasMapping(key)) {
throw new Error('Mapping already exists under key ' + key);
} else {
// Create mapping.
var mapping = new Muscles.CommandMapMapping(key);
// Register mapping under key.
this._mappings[key] = mapping;
return mapping;
}
},
/**
* @chainable
* @method unmap
* @param {String} key
* @public
* @return {Muscles.CommandMap}
*/
unmap: function(key) {
// Throw error if mapping not exists.
if (!this.hasMapping(key)) {
throw new Error('Mapping not exists under key ' + key);
} else {
// Remove key from mappings.
delete this._mappings[key];
return this;
}
}
});
/**
* @class Muscles.CommandMapExtension
* @constructor
* @extends Muscles.Extension
* @submodule Muscles.CommandMapExtension
*/
var CommandMapExtension = Muscles.CommandMapExtension = Muscles.Extension.extend({
/**
* @extends Muscles.Extension.extend
*/
extend: function(context) {
context.getInjector().map('commandMap').toSingleton(Muscles.CommandMap);
}
});
/**
* @class Muscles.CommandMapMapping
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var CommandMapMapping = Muscles.CommandMapMapping = function() {};
// Attach all inheritable methods to the CommandMapMapping prototype.
_.extend(CommandMapMapping.prototype, {
/**
* @private
* @property _type
* @type {Muscles.Command|Muscles.MacroCommand|Muscles.ASyncMacroCommand|null}
*/
_type: null,
/**
* @method getType
* @public
* @return {Muscles.Command|Muscles.MacroCommand|Muscles.ASyncMacroCommand|null}
*/
getType: function() {
return this._type;
},
/**
* @chainable
* @method toCommand
* @param {Muscles.Command|Muscles.MacroCommand|Muscles.ASyncMacroCommand} type
* @public
* @return {Muscles.CommandMapMapping}
*/
toCommand: function(type) {
// Throw error if type already exists.
if (this._type !== null) {
throw new Error('Type already exists');
} else {
this._type = type;
}
return this;
}
});
/**
* @class Muscles.CommandRouter
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var CommandRouter = Muscles.CommandRouter = Backbone.Router.extend({
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @chainable
* @method route
* @param {RegExp|String} route
* @param {String} key
* @public
* @return {Muscles.CommandRouter}
*/
route: function(route, key) {
var fn = function() {
var arg, args = Array.prototype.slice.call(arguments);
var argsArray = [key];
while ((arg = args.shift()) !== null) {
argsArray.push(arg);
}
this.getCommandMap().execute.apply(this.getCommandMap(), argsArray);
};
return Backbone.Router.prototype.route.apply(this, [route, key, fn]);
}
});
/**
* @class Muscles.MacroCommand
* @constructor
* @submodule Muscles.CommandMapExtension
*/
var MacroCommand = Muscles.MacroCommand = function() {
this._subCommands = [];
};
// Attach all inheritable methods to the MacroCommand prototype.
_.extend(MacroCommand.prototype, {
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @private
* @property _context
* @type Muscles.Context
*/
_context: 'inject:context',
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: 'inject:eventMap',
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: 'inject:injector',
/**
* @method initialize
* @public
*/
initialize: function() {},
/**
* @chainable
* @method addSubCommand
* @param {Muscles.Command|Muscles.MacroCommand} type
* @public
* @return {Muscles.MacroCommand}
*/
addSubCommand: function(type) {
this._subCommands.push(type);
},
/**
* @method execute
* @public
*/
execute: function() {
while (this._subCommands.length) {
var command = this.getInjector().instantiate(this._subCommands.shift());
// Execute the command with the same paypload.
command.execute.apply(command, arguments);
}
},
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getContext
* @public
* @return {Muscles.Context}
*/
getContext: function() {
return this._context;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
},
/**
* @method postInject
* @public
*/
postInject: function() {
this.initialize.apply(this, []);
}
});
// Set up inheritance for MacroCommand.
MacroCommand.extend = Backbone.Model.extend;
/**
* @class Muscles.EventMapExtension
* @constructor
* @extends Muscles.Extension
* @submodule Muscles.EventMapExtension
*/
var EventMapExtension = Muscles.EventMapExtension = Muscles.Extension.extend({
/**
* @extends Muscles.Extension.extend
*/
extend: function(context) {
context.getInjector().map('eventMap').toValue(_.clone(Backbone.Events));
}
});
/**
* @class Muscles.Mediator
* @constructor
* @module Muscles.MediatorMapExtension
*/
var Mediator = Muscles.Mediator = function() {};
// Extend the Mediator prototype with Backbone events.
_.extend(Mediator.prototype, Backbone.Events);
// Attach all inheritable methods to the Mediator prototype.
_.extend(Mediator.prototype, {
/**
* @property contextEvents
* @protected
* @type {Object}
*/
contextEvents: {},
/**
* @property viewEvents
* @protected
* @type {Object}
*/
viewEvents: {},
/**
* @private
* @property _commandMap
* @type Muscles.CommandMap
*/
_commandMap: 'inject:commandMap',
/**
* @private
* @property _eventMap
* @type Muscles.EventMap
*/
_eventMap: 'inject:eventMap',
/**
* @private
* @property _injector
* @type Muscles.Injector
*/
_injector: 'inject:injector',
/**
* @property _view
* @private
* @type {Muscles.View}
*/
_view: 'inject:view',
/**
* @method getCommandMap
* @public
* @return {Muscles.CommandMap}
*/
getCommandMap: function() {
return this._commandMap;
},
/**
* @method getEventMap
* @public
* @return {Backbone.Events}
*/
getEventMap: function() {
return this._eventMap;
},
/**
* @method getInjector
* @public
* @return {Muscles.Injector}
*/
getInjector: function() {
return this._injector;
},
/**
* @method getView
* @public
* @return {Muscles.View}
*/
getView: function() {
return this._view;
},
/**
* @method initialize
* @public
*/
initialize: function() {},
/**
* @method postInject
* @public
*/
postInject: function() {
Muscles.mapEvents(this, this.getEventMap(), this.contextEvents);
Muscles.mapEvents(this, this.getView(), this.viewEvents);
this.initialize.apply(this, []);
},
/**
* @method remove
* @public
*/
remove: function() {
Muscles.unmapEvents(this, this.getEventMap(), this.contextEvents);
Muscles.unmapEvents(this, this.getView(), this.viewEvents);
this._view = null;
}
});
// Set up inheritance for Mediator.
Mediator.extend = Backbone.Model.extend;
/**
* @class Muscles.MediatorMap
* @constructor
* @submodule Muscles.MediatorMapExtension
*/
var MediatorMap = Muscles.MediatorMap = function() {
this._mappings = [];
this._mediators = [];
};
// Attach all inheritable methods to the MediatorMap prototype.
_.extend(MediatorMap.prototype, {
/**
* @property context
* @public
* @type Muscles.Context
*/
context: 'inject',
/**
* @method getMapping
* @param {Function} type
* @public
* @return {Muscles.MediatorMapMapping|null}
*/
getMapping: function(type) {
var mapping = _.find(this._mappings, function(mapping) {
return mapping.getType() === type;
});
return mapping || null;
},
/**
* @method getMappingForView
* @param {Muscles.View} view
* @public
* @return {Muscles.MediatorMapMapping|null}
*/
getMappingForView: function(view) {
var mapping = _.find(this._mappings, function(mapping) {
return view instanceof mapping.getType();
});
return mapping || null;
},
/**
* @method getMediator
* @param {Muscles.View} view
* @public
* @return {Muscles.Mediator|null}
*/
getMediator: function(view) {
var mediator = _.find(this._mediators, function(mediator) {
return mediator.getView() === view;
});
return mediator || null;
},
/**
* @method hasMapping
* @param {Function} type
* @public
* @return {Boolean}
*/
hasMapping: function(type) {
return _.find(this._mappings, function(mapping) {
return mapping.getType() === type;
}) !== undefined;
},
/**
* @method hasMappingForView
* @param {Muscles.View} view
* @public
* @return {Boolean}
*/
hasMappingForView: function(view) {
return _.find(this._mappings, function(mapping) {
return view instanceof mapping.getType();
}) !== undefined;
},
/**
* @method hasMediator
* @param {Muscles.View} view
* @public
* @return {Boolean}
*/
hasMediator: function(view) {
return _.find(this._mediators, function(mediator) {
return mediator.getView() === view;
}) !== undefined;
},
/**
* @method map
* @param {Function} type
* @public
* @return {Muscles.MediatorMapMapping}
*/
map: function(type) {
// Throw error if mapping already exists.
if (this.hasMapping(type)) {
throw new Error('Mapping already exists for ' + type);
} else {
// Create mapping.
var mapping = new Muscles.MediatorMapMapping(type);
// Register mapping.
this._mappings.push(mapping);
return mapping;
}
},
/**
* @method mediate
* @param {Muscles.View} view
* @public
* @return {Muscles.Mediator}
*/
mediate: function(view) {
var mapping = this.getMappingForView(view);
// If a mapping is found return the instantiated mediator.
if (mapping !== null) {
if (mapping.getMediator() !== null) {
// Get child injector and map the view.
var childInjector = this.context.getInjector().getChildInjector();
childInjector.map('view').toValue(view);
// Create mediator.
var mediator = childInjector.instantiate(mapping.getMediator());
// Register mediator.
this._mediators.push(mediator);
// Return mediator.
return mediator;
}
// No rules set for mapping.
else {
throw 'No rules set for mapping of ' + view;
}
}
// No mapping found.
else {
throw 'No mapping found for ' + view;
}
},
/**
* @chainable
* @method unmap
* @param {Function} type
* @public
* @return {Muscles.MediatorMap}
*/
unmap: function(type) {
// Throw error if mapping not exists.
if (!this.hasMapping(type)) {
throw new Error('Mapping not exists for ' + type);
} else {
// Remove type from mappings.
this._mappings = _.filter(this._mappings, function(mapping) {
return mapping.getType() !== type;
});
return this;
}
},
/**
* @method unmediate
* @param {Muscles.View} view
* @public
*/
unmediate: function(view) {
var mediator = this.getMediator(view);
// Remove mediator.
if (mediator !== null) {
mediator.remove.apply(mediator, []);
this._mediators = _.filter(this._mediators, function(entry) {
return entry !== mediator;
});
}
}
});
/**
* @class Muscles.MediatorMapMapping
* @constructor
* @submodule Muscles.MediatorMapExtension
* @param {Function} type
*/
var MediatorMapMapping = Muscles.MediatorMapMapping = function(type) {
if (type !== undefined) {
this._type = type;
}
};
// Attach all inheritable methods to the MediatorMapMapping prototype.
_.extend(MediatorMapMapping.prototype, {
/**
* @private
* @property _mediator
* @type {Function|null}
*/
_mediator: null,
/**
* @private
* @property _type
* @type {Function|null}
*/
_type: null,
/**
* @method getMediator
* @public
* @return {Function|null}
*/
getMediator: function() {
return this._mediator;
},
/**
* @method getType
* @public
* @return {Function|null}
*/
getType: function() {
return this._type;
},
/**
* @chainable
* @method toMediator
* @param {Function} mediator
* @public
* @return {Muscles.MediatorMapMapping}
*/
toMediator: function(mediator) {
// Throw error if type already exists.
if (this._mediator !== null) {
throw new Error('Mediator already exists');
} else {
this._mediator = mediator;
}
return this;
}
});
/**
* @class Muscles.MediatorMapExtension
* @constructor
* @extends Muscles.Extension
* @submodule Muscles.MediatorMapExtension
*/
var MediatorMapExtension = Muscles.MediatorMapExtension = Muscles.Extension.extend({
/**
* @extends Muscles.Extension.extend
*/
extend: function(context) {
context.getInjector().map('mediatorMap').toSingleton(Muscles.MediatorMap);
}
});
/**
* @method mapEvents
* @param {Object} target
* @param {Backbone.Events} eventMap
* @param {Object} events
* @static
*/
var mapEvents = Muscles.mapEvents = function(target, eventMap, events) {
_.each(events, function(value, key) {
var callback = target[value];
if (!_.isFunction(callback)) {
throw value + ' on target' + target + ' is not a function';
} else {
target.listenTo(eventMap, key, callback);
}
});
};
/**
* @method renderTemplate
* @param {Function} template
* @param {Object} context
* @return {String}
* @static
*/
var renderTemplate = Muscles.renderTemplate = function(template, context) {
if (!_.isFunction(template)) {
throw 'No pre-compiled template method found';
}
return template(context);
};
/**
* @method unmapEvents
* @param {Object} target
* @param {Backbone.Events} eventMap
* @param {Object} events
* @static
*/
var unmapEvents = Muscles.unmapEvents = function(target, eventMap, events) {
_.each(events, function(value, key) {
target.stopListening(eventMap, key);
});
};
return Muscles;
}));

32

package.json

@@ -10,15 +10,24 @@ {

"devDependencies": {
"bower": "~1.2.8",
"grunt": "~0.4.2",
"grunt-cli": "~0.1.11",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-jasmine": "~0.6.1",
"grunt-contrib-jshint": "~0.8.0",
"grunt-contrib-uglify": "~0.3.2",
"grunt-contrib-yuidoc": "~0.5.0"
"bower": "1.3.3",
"grunt": "0.4.5",
"grunt-cli": "0.1.13",
"grunt-contrib-concat": "0.4.0",
"grunt-contrib-jshint": "0.10.0",
"grunt-contrib-uglify": "0.4.0",
"grunt-contrib-yuidoc": "0.5.2",
"grunt-coveralls": "0.3.0",
"grunt-karma": "0.8.3",
"install": "^0.1.7",
"karma": "0.12.16",
"karma-browserstack-launcher": "0.0.8",
"karma-coverage": "0.2.1",
"karma-jasmine": "0.2.2",
"karma-junit-reporter": "0.2.2",
"karma-phantomjs-launcher": "0.1.4",
"requirejs": "2.1.11"
},
"engineStrict": false,
"engines": {
"node": ">=0.10.23",
"npm": ">=1.3.17"
"node": ">=0.10.25",
"npm": ">=1.4.3"
},

@@ -45,6 +54,5 @@ "files": [

"postinstall": "bower install",
"prepublish": "grunt build",
"test": "grunt test"
},
"version": "0.1.0"
"version": "0.2.0"
}

@@ -1,12 +0,26 @@

# Muscles.js [![Build Status](https://secure.travis-ci.org/muscles/muscles.js.png?branch=develop)](http://travis-ci.org/muscles/muscles.js) [![GitHub version](https://badge.fury.io/gh/muscles%2Fmuscles.js.png)](http://badge.fury.io/gh/muscles%2Fmuscles.js) [![Dependency Status](https://gemnasium.com/muscles/muscles.js.png)](https://gemnasium.com/muscles/muscles.js)
# Muscles.js [![Build Status](https://travis-ci.org/muscles/muscles.js.svg)](http://travis-ci.org/muscles/muscles.js) [![Coverage Status](https://coveralls.io/repos/muscles/muscles.js/badge.png?branch=develop)](https://coveralls.io/r/muscles/muscles.js?branch=develop) [![NPM version](https://badge.fury.io/js/muscles.svg)](http://badge.fury.io/js/muscles) [![Dependency Status](https://gemnasium.com/muscles/muscles.js.png)](https://gemnasium.com/muscles/muscles.js)
> Building muscle mass for your Backbone application!
## Downloads
Using [Bower](http://bower.io/):
```shell
bower install muscles --save
```
Using [NPM](https://www.npmjs.org/):
```shell
npm install muscles --save
```
## Getting started
This library requires Backbone ``1.1.1``.
Read our getting started guide at [musclesjs.org](http://musclesjs.org).
## Status
This project is currently in development.
Release ``0.2.0`` is now available for public and stable for development.

@@ -13,0 +27,0 @@ ## Changelog