Comparing version
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; | ||
})); |
@@ -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 [](http://travis-ci.org/muscles/muscles.js) [](http://badge.fury.io/gh/muscles%2Fmuscles.js) [](https://gemnasium.com/muscles/muscles.js) | ||
# Muscles.js [](http://travis-ci.org/muscles/muscles.js) [](https://coveralls.io/r/muscles/muscles.js?branch=develop) [](http://badge.fury.io/js/muscles) [](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 |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
45992
143.61%1626
222.62%30
87.5%17
112.5%2
Infinity%