mvc-pack
Advanced tools
Comparing version 0.11.0 to 0.12.0
@@ -5,4 +5,4 @@ (function (root, factory) { | ||
// AMD. Register as an anonymous module unless amdModuleId is set | ||
define(["class-wrapper","lodash"], function (a0,b1) { | ||
return (root['mvc-pack'] = factory(a0,b1)); | ||
define(["class-wrapper","@valerii-zinchenko/observer","lodash"], function (a0,b1,c2) { | ||
return (root['mvc-pack'] = factory(a0,b1,c2)); | ||
}); | ||
@@ -13,11 +13,11 @@ } else if (typeof module === 'object' && module.exports) { | ||
// like Node. | ||
module.exports = factory(require("class-wrapper"),require("lodash")); | ||
module.exports = factory(require("class-wrapper"),require("@valerii-zinchenko/observer"),require("lodash")); | ||
} else { | ||
root['mvc-pack'] = factory(root["class-wrapper"],root["_"]); | ||
root['mvc-pack'] = factory(root["class-wrapper"],root["Observer"],root["_"]); | ||
} | ||
}(this, function (ClassWrapper, _) { | ||
}(this, function (ClassWrapper, Observer, _) { | ||
// Package of combined patterns to build rich MVC modules | ||
// v0.11.0 | ||
// Copyright (c) 2016-2017 Valerii Zinchenko | ||
// v0.12.0 | ||
// Copyright (c) 2016-2019 Valerii Zinchenko | ||
// Licensed under MIT (http://valerii-zinchenko.github.io/mvc-pack/blob/master/LICENSE.txt) | ||
@@ -44,3 +44,2 @@ // All source files are available at https://github.com/valerii-zinchenko/mvc-pack | ||
var Class = ClassWrapper.Class; | ||
var SingletonClass = ClassWrapper.SingletonClass; | ||
@@ -115,3 +114,3 @@ /* | ||
* | ||
* @version 3.0.1 | ||
* @version 3.1.0 | ||
*/ | ||
@@ -133,3 +132,10 @@ | ||
} | ||
// Automatically bind all defined function to "this" and store them into "this" under the same name | ||
Object.keys(this._bind2this).forEach(function(name) { | ||
this[name] = this._bind2this[name].bind(this); | ||
}, this); | ||
}, /** @lends AModeComponent.prototype */{ | ||
__name: 'AModeComponent', | ||
/** | ||
@@ -150,2 +156,11 @@ * Reference to the model. | ||
/** | ||
* Set of function, which will be bound automatically to "this" by constructing an instance. | ||
* This is most useful for event handlers, which in most cases should have the context of current class. | ||
* Be careful, as that can overwrite other properties/methods defined in the class. | ||
* | ||
* @type {Object} | ||
*/ | ||
_bind2this: {}, | ||
/** | ||
* Connect view component to the module. | ||
@@ -200,3 +215,3 @@ * This is called after constructor and setting of the model and control components. | ||
* | ||
* @version 2.0.0 | ||
* @version 3.0.0 | ||
*/ | ||
@@ -212,30 +227,16 @@ | ||
* @extends AModeComponent | ||
* @mixes Observer | ||
*/ | ||
var AControl = Class(AModeComponent, null, /** @lends AControl.prototype */{ | ||
/** | ||
* Reference to the view. | ||
*/ | ||
view: null, | ||
__name: 'AControl', | ||
Encapsulate: [Observer], | ||
/** | ||
* Control destructor. | ||
* Control destructor | ||
*/ | ||
destruct: function() { | ||
this.view = null; | ||
this._events = {}; | ||
AModeComponent.prototype.destruct.call(this); | ||
}, | ||
/** | ||
* Set view | ||
* | ||
* @param {Object} view - View | ||
* | ||
* @throws {Error} Incorrect type of view component | ||
*/ | ||
setView: function(view) { | ||
if (!(view instanceof AView)) { | ||
throw new Error('Incorrect type of view component'); | ||
} | ||
this.view = view; | ||
} | ||
@@ -278,2 +279,4 @@ }); | ||
var AView = Class(AModeComponent, null, /** @lends AView.prototype */{ | ||
__name: 'AView', | ||
/** | ||
@@ -323,2 +326,33 @@ * Reference to the control. | ||
/** | ||
* Insert additionl module into current view | ||
* | ||
* @param {DOMElement} container - Container element, where the module is going to be inserted. This should be empty, because it will be emptied before insertion | ||
* @param {AView} view - View to insert | ||
*/ | ||
insertView: function(container, view) { | ||
// cleanup the container | ||
while (container.firstChild) { | ||
container.removeChild(container.firstChild); | ||
} | ||
this.appendView(container, view); | ||
}, | ||
/** | ||
* Appends the view's elements into the container | ||
* | ||
* @param {DOMElement} container - Container element, where the module is going to be inserted. This should be empty, because it will be emptied before insertion | ||
* @param {AView} view - View to insert | ||
*/ | ||
appendView: function(container, view) { | ||
// insert view's elements | ||
for (var n = 0, N = view.elements.length; n < N; n++) { | ||
container.appendChild(view.elements[n]); | ||
} | ||
view.update(); | ||
}, | ||
/** | ||
* Initialize required for view elements. | ||
@@ -360,3 +394,3 @@ */ | ||
* Static view. | ||
* It implements parent's [render()]{@link render} method to initalize required elements and attach eventh. | ||
* It implements parent's [render()]{@link render} method to initialize required elements and attach events. | ||
* | ||
@@ -366,5 +400,7 @@ * @class | ||
*/ | ||
var StaticView = SingletonClass(AView, function() { | ||
var StaticView = Class(AView, function() { | ||
this.element = document.querySelector(this.selector); | ||
}, /** @lends StaticView.prototype */{ | ||
__name: 'StaticView', | ||
/** | ||
@@ -447,2 +483,4 @@ * Selector of a static element from a DOM | ||
var DynamicView = Class(AView, null, /** @lends DynamicView.prototype */{ | ||
__name: 'DynamicView', | ||
/** | ||
@@ -534,2 +572,4 @@ * Main template. | ||
var ADecorator = Class(DynamicView, null, /** @lends ADecorator.prototype */{ | ||
__name: 'ADecorator', | ||
template: '<div data-mvc-container></div>', | ||
@@ -620,10 +660,11 @@ | ||
* | ||
* @see {@link AFMode} | ||
* @see {@link AModeComponent} | ||
* @see {@link AView} | ||
* @see {@link AControl} | ||
* @see {@link AFMode} | ||
* @see {@link ADecorator} | ||
* | ||
* @author Valerii Zinchenko | ||
* | ||
* @version 5.0.0 | ||
* @version 6.0.0 | ||
*/ | ||
@@ -637,13 +678,17 @@ | ||
* @class | ||
* @param {Object} properties - Mode's properties. | ||
* @param {Object} properties.model - Model. | ||
* @param {AView} properties.view - Main mode's view. | ||
* @param {AControl} [properties.control] - Mode's control. | ||
* @param {Object} [properties.decorators] - Object of decorators for a view, where the key is a decorator name and value is a decorator. | ||
* | ||
* @throws {Error} Incorrect type of the mode's properties. Expected: Object | ||
* @throws {Error} Incorrect type of the model. Expected: Object | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* @throws {Error} Incorrect type of a view's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of a control's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor | ||
* @throws {Error} Incorrect type of a decorator's constructor "{name}". Expected: Function | ||
* @throws {Error} Control should be inherited from AControl | ||
* | ||
* @param {Object} properties - Mode's properties. | ||
* @param {Object} properties.model - Model. | ||
* @param {Function} properties.View - Constructor for the view component. | ||
* @param {Function} [properties.Control] - Constructor for the control component. | ||
* @param {Object} [properties.Decorators] - Map of decorators' names and decorators' construstors for a new mode. | ||
* @param {Object} [properties.config] - Configurations for control, view and decorator components. | ||
*/ | ||
@@ -654,37 +699,50 @@ var Mode = Class(function(properties) { | ||
} | ||
if (!utils.is(properties.model, 'Object')) { | ||
if (utils.is(properties.model, 'Object')) { | ||
this.model = properties.model; | ||
} else { | ||
throw new Error('Incorrect type of the model. Expected: Object'); | ||
} | ||
if (!(properties.view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
if (utils.is(properties.View, 'Function')) { | ||
this.View = properties.View; | ||
} else { | ||
throw new Error('Incorrect type of a view\'s constructor. Expected: Function'); | ||
} | ||
this.model = properties.model; | ||
this._view = properties.view; | ||
if (properties.Decorators) { | ||
if (!utils.is(properties.Decorators, 'Object')) { | ||
throw new Error('Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor'); | ||
} | ||
if (properties.decorators && utils.is(properties.decorators, 'Object')) { | ||
for (var key in properties.decorators) { | ||
if ( !(properties.decorators[key] instanceof ADecorator) ) { | ||
throw new Error('Decorator "' + key + '" should be inherited from ADecorator'); | ||
for (var key in properties.Decorators) { | ||
if (!utils.is(properties.Decorators[key], 'Function')) { | ||
throw new Error('Incorrect type of a decorator\'s constructor "' + key + '". Expected: Function'); | ||
} | ||
} | ||
this._decorators[key] = properties.decorators[key]; | ||
} | ||
this.Decorators = properties.Decorators; | ||
} | ||
if (properties.control) { | ||
if ( !(properties.control instanceof AControl) ) { | ||
this.config = properties.config; | ||
if (properties.Control) { | ||
if (!utils.is(properties.Control, 'Function')) { | ||
throw new Error('Incorrect type of a control\'s constructor. Expected: Function'); | ||
} | ||
var control = new properties.Control(this.config); | ||
if ( !(control instanceof AControl) ) { | ||
throw new Error('Control should be inherited from AControl'); | ||
} | ||
this.control = properties.control; | ||
this.control = control; | ||
this.control.setModel(this.model); | ||
this.control.connect(); | ||
} | ||
}, /** @lends Mode.prototype */{ | ||
__name: 'Mode', | ||
this.view = this._view; | ||
this.connect(); | ||
}, /** @lends Mode.prototype */{ | ||
/** | ||
* Reference to the model. | ||
* | ||
* @type {Objcet} | ||
@@ -695,18 +753,15 @@ */ | ||
/** | ||
* Reference to the main view. | ||
* | ||
* @type {AView} | ||
* View constructor | ||
* @type {Function} | ||
*/ | ||
_view: null, | ||
View: null, | ||
/** | ||
* Current view. This can be an original or decorated view. | ||
* | ||
* @type {DynamicView} | ||
* Map of decorators' names and decorators' construstors for a new mode | ||
* @type {Object} | ||
*/ | ||
view: null, | ||
Decorators: {}, | ||
/** | ||
* Reference to the control. | ||
* | ||
* @type {AControl} | ||
@@ -717,74 +772,91 @@ */ | ||
/** | ||
* Object of decorators, where the key is a name of a deocorator and value is a decorator object. | ||
* | ||
* Map of usages and created views for it | ||
* @type {Object} | ||
*/ | ||
_decorators: {}, | ||
_usages: {}, | ||
/** | ||
* This indicates if a mode components are already connected or not. | ||
* Get an instance of the view for a specific usage. | ||
* | ||
* @type {Boolean} | ||
* For each usage a new instance of the view will be created and, if needed, decorated also with new instances of decorators. | ||
* This allows to use the same view in different places with or without different decorators. | ||
* | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* | ||
* @param {String} [usage = 'default'] - Define for how/where he view is going to be used. For different usages different instances of a view and decorators will be created. | ||
* @param {String[] | String} [decorators] - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
*/ | ||
_isConnected: false, | ||
getView: function(usage, decorations) { | ||
if (!usage) { | ||
usage = 'default'; | ||
} | ||
if (this._usages[usage]) { | ||
return this._usages[usage]; | ||
} | ||
/** | ||
* Connect mode components | ||
*/ | ||
connect: function() { | ||
if (this._isConnected) { | ||
return; | ||
var view = new this.View(this.config); | ||
if (!(view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
} | ||
this._view.setModel(this.model); | ||
view.setModel(this.model); | ||
if (this.control) { | ||
this._view.setControl(this.control); | ||
this.control.setModel(this.model); | ||
this.control.setView(this._view); | ||
this.control.connect(); | ||
view.setControl(this.control); | ||
} | ||
this._view.connect(); | ||
this._view.render(); | ||
view.connect(); | ||
view.render(); | ||
view = this.decorate(view, decorations); | ||
var decorator; | ||
for (var key in this._decorators) { | ||
decorator = this._decorators[key]; | ||
decorator.setModel(this.model); | ||
decorator.render(); | ||
} | ||
this._usages[usage] = view; | ||
this._isConnected = true; | ||
return view; | ||
}, | ||
/** | ||
* Apply decorators to the original view. | ||
* Apply decorators to a view. | ||
* | ||
* @param {String[] | String } decorators - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Constructor for decorator "{name}" is not defined | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* | ||
* @param {AView} view - View, which should be decorated. | ||
* @param {String[] | String} decorators - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
* @return {Aview} | ||
*/ | ||
decorateWith: function(decorators){ | ||
if (!decorators) { | ||
return; | ||
decorate: function(view, decorators){ | ||
if (!(view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
} | ||
if (this.Decorators.length === 0 || !decorators) { | ||
return view; | ||
} | ||
if (utils.is(decorators, 'String')){ | ||
this.decorateWith([decorators]); | ||
return; | ||
decorators = [decorators]; | ||
} | ||
var v = this._view; | ||
var v = view; | ||
var decorator; | ||
var Constructor; | ||
for (var n = 0, N = decorators.length; n < N; n++) { | ||
Constructor = this.Decorators[decorators[n]]; | ||
if (!Constructor) { | ||
throw new Error('Constructor for decorator "' + decorators[n] + '" is not defined'); | ||
} | ||
if (this._decorators && decorators.length > 0) { | ||
var decorator; | ||
for (var n = 0, N = decorators.length; n < N; n++) { | ||
decorator = this._decorators[decorators[n]]; | ||
if (decorator) { | ||
decorator.setComponent(v); | ||
decorator = new Constructor(this.config); | ||
v = decorator; | ||
} | ||
if ( !(decorator instanceof ADecorator) ) { | ||
throw new Error('Decorator "' + decorators[n] + '" should be inherited from ADecorator'); | ||
} | ||
decorator.setComponent(v); | ||
decorator.setModel(this.model); | ||
decorator.render(); | ||
v = decorator; | ||
} | ||
this.view = v; | ||
return v; | ||
} | ||
@@ -804,5 +876,5 @@ }); | ||
/** | ||
* @file It contains the implementation of [abstract mode factory]{@link AFMode} that returns [mode builder]{@link BMode}. | ||
* @file It contains the implementation of [abstract mode factory]{@link AFMode} that returns [mode builder]{@link FMode}. | ||
* | ||
* @see {@link BMode} | ||
* @see {@link FMode} | ||
* @see {@link Mode} | ||
@@ -812,3 +884,3 @@ * | ||
* | ||
* @version 3.0.0 | ||
* @version 4.0.0 | ||
*/ | ||
@@ -823,6 +895,2 @@ | ||
* @throws {Error} Incorrect type of the constructors. Object expected. | ||
* @throws {Error} Incorrect type of a view's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of a control's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor | ||
* @throws {Error} Incorrect type of a decorator's constructor "{name}". Expected: Function | ||
* | ||
@@ -832,4 +900,4 @@ * @param {Object} Constructors - Collection of constructors for a mode. | ||
* @param {Function} [Constructors.Control] - Constructor for the control component. | ||
* @param {Object} [Constructors.Decorators] - Map of decorators' names and decorators' construstors for a new mode. If some constructor is not a function, then it will be skipped in order to avoid NPE | ||
* @return {BMode} Mode builder. | ||
* @param {Object} [Constructors.Decorators] - Map of decorators' names and decorators' constructors for a new mode. If some constructor is not a function, then it will be skipped in order to avoid NPE | ||
* @return {FMode} Mode factory. | ||
*/ | ||
@@ -840,24 +908,7 @@ function AFMode(Constructors) { | ||
} | ||
if (!utils.is(Constructors.View, 'Function')) { | ||
throw new Error('Incorrect type of a view\'s constructor. Expected: Function'); | ||
} | ||
if (Constructors.Control && !utils.is(Constructors.Control, 'Function')) { | ||
throw new Error('Incorrect type of a control\'s constructor. Expected: Function'); | ||
} | ||
if (Constructors.Decorators) { | ||
if (!utils.is(Constructors.Decorators, 'Object')) { | ||
throw new Error('Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor'); | ||
} | ||
for (var key in Constructors.Decorators) { | ||
if (!utils.is(Constructors.Decorators[key], 'Function')) { | ||
throw new Error('Incorrect type of a decorator\'s constructor "' + key + '". Expected: Function'); | ||
} | ||
} | ||
} | ||
/** | ||
* Mode builder. | ||
* Mode factory. | ||
* | ||
* @name BMode | ||
* @name FMode | ||
* @type {Function} | ||
@@ -870,22 +921,7 @@ * | ||
return function(model, config) { | ||
var view = new Constructors.View(config); | ||
var decorators; | ||
if (Constructors.Decorators) { | ||
decorators = {}; | ||
for (var key in Constructors.Decorators) { | ||
decorators[key] = new Constructors.Decorators[key](config); | ||
} | ||
} | ||
var control; | ||
if (Constructors.Control) { | ||
control = new Constructors.Control(config); | ||
} | ||
return new Mode({ | ||
model: model, | ||
view: view, | ||
decorators: decorators, | ||
control: control, | ||
View: Constructors.View, | ||
Control: Constructors.Control, | ||
Decorators: Constructors.Decorators, | ||
config: config | ||
@@ -940,2 +976,4 @@ }); | ||
}, /** @lends MVCModule.prototype */{ | ||
__name: 'MVCModule', | ||
/** | ||
@@ -967,13 +1005,6 @@ * Module's model | ||
* @param {String} modeName - Mode name. | ||
* @param {String | String[]} [decorators] - Mode's decorators. | ||
* @return {Mode | Null} | ||
*/ | ||
getMode: function(modeName, decorators) { | ||
var mode = this.modes[modeName]; | ||
if (mode && decorators) { | ||
mode.decorateWith(decorators); | ||
} | ||
return mode || null; | ||
getMode: function(modeName) { | ||
return this.modes[modeName] || null; | ||
}, | ||
@@ -985,7 +1016,6 @@ | ||
* @param {String} env - Target environment. | ||
* @param {String | String[]} [decorators] - Mode's decorators. | ||
* @return {Mode | Null} | ||
*/ | ||
getModeFor: function(env, decorators){ | ||
return this.getMode(this.envModeMap[env], decorators); | ||
getModeFor: function(env){ | ||
return this.getMode(this.envModeMap[env]); | ||
} | ||
@@ -992,0 +1022,0 @@ }); |
// Package of combined patterns to build rich MVC modules | ||
// v0.11.0 | ||
// Copyright (c) 2016-2017 Valerii Zinchenko | ||
// v0.12.0 | ||
// Copyright (c) 2016-2019 Valerii Zinchenko | ||
// Licensed under MIT (http://valerii-zinchenko.github.io/mvc-pack/blob/master/LICENSE.txt) | ||
// All source files are available at https://github.com/valerii-zinchenko/mvc-pack | ||
!function(t,e){void 0===t&&void 0!==window&&(t=window),"function"==typeof define&&define.amd?define(["class-wrapper","lodash"],function(o,n){return t["mvc-pack"]=e(o,n)}):"object"==typeof module&&module.exports?module.exports=e(require("class-wrapper"),require("lodash")):t["mvc-pack"]=e(t["class-wrapper"],t._)}(this,function(t,e){var o=t.Class,n=t.SingletonClass,r={whatIs:function(t){return Object.prototype.toString.call(t)},is:function(t,e){return this.whatIs(t)==="[object "+e+"]"}},i=o(function(t){t&&r.is(t,"Object")&&(this.config=t)},{model:null,config:null,connect:function(){},destruct:function(){this.model=null,this.config=null},setModel:function(t){if(!r.is(t,"Object"))throw new Error("Model for the mode is not defined");this.model=t}}),c=o(i,null,{view:null,destruct:function(){this.view=null,i.prototype.destruct.call(this)},setView:function(t){if(!(t instanceof s))throw new Error("Incorrect type of view component");this.view=t}}),s=o(i,null,{control:null,destruct:function(){this.control=null,i.prototype.destruct.call(this)},setControl:function(t){if(!(t instanceof c))throw new Error("Incorrect type of control component");this.control=t},render:function(){},update:function(){},_initElements:function(){},_attachEvents:function(){}}),l=n(s,function(){this.element=document.querySelector(this.selector)},{selector:"div",element:null,destruct:function(){this.element.remove(),s.prototype.destruct.call(this)},show:function(){this.element.classList.remove("hidden")},hide:function(){this.element.classList.add("hidden")},render:function(){return this._initElements(),this._attachEvents(),this.element}}),d=o(s,null,{template:"<div></div>",elements:[],_tmpContainer:"div",destruct:function(){this.elements.forEach(function(t){t.remove()}),s.prototype.destruct.call(this)},render:function(){return this._processTemplate(),this._initElements(),this._attachEvents(),this.elements},_processTemplate:function(){this.element=document.createElement(this._tmpContainer),this.element.innerHTML=e.template(this.template)(this);for(var t=this.element.children,o=0,n=t.length;o<n;o++)this.elements.push(t[o])}}),h=o(d,null,{template:"<div data-mvc-container></div>",_component:null,container:null,setComponent:function(t){if(!(t instanceof s))throw new Error('Incorrect type of the "component" argument. Expected AView');this._component=t},destruct:function(){this._component&&this._component.destruct(),d.prototype.destruct.call(this)},render:function(){return d.prototype.render.call(this),this._component&&this._component.elements.forEach(function(t){this.container.appendChild(t)},this),this.elements},update:function(){this._component&&this._component.update(),d.prototype.update.call(this)},_processTemplate:function(){d.prototype._processTemplate.call(this),this.container=this.element.querySelector("[data-mvc-container]")}}),u=o(function(t){if(!r.is(t,"Object"))throw new Error("Incorrect type of the mode's properties. Expected: Object");if(!r.is(t.model,"Object"))throw new Error("Incorrect type of the model. Expected: Object");if(!(t.view instanceof s))throw new Error("View should be inherited from AView");if(this.model=t.model,this._view=t.view,t.decorators&&r.is(t.decorators,"Object"))for(var e in t.decorators){if(!(t.decorators[e]instanceof h))throw new Error('Decorator "'+e+'" should be inherited from ADecorator');this._decorators[e]=t.decorators[e]}if(t.control){if(!(t.control instanceof c))throw new Error("Control should be inherited from AControl");this.control=t.control}this.view=this._view,this.connect()},{model:null,_view:null,view:null,control:null,_decorators:{},_isConnected:!1,connect:function(){if(!this._isConnected){this._view.setModel(this.model),this.control&&(this._view.setControl(this.control),this.control.setModel(this.model),this.control.setView(this._view),this.control.connect()),this._view.connect(),this._view.render();var t;for(var e in this._decorators)(t=this._decorators[e]).setModel(this.model),t.render();this._isConnected=!0}},decorateWith:function(t){if(t)if(r.is(t,"String"))this.decorateWith([t]);else{var e=this._view;if(this._decorators&&t.length>0)for(var o,n=0,i=t.length;n<i;n++)(o=this._decorators[t[n]])&&(o.setComponent(e),e=o);this.view=e}}}),a=o(function(t,e,o){if(!r.is(t,"Object")||!r.is(e,"Object"))throw new Error("Incorrect types of input arguments. Expected: Object model, Object modes");this.model=t,this.modes=e,r.is(o,"Object")&&(this.envModeMap=o)},{model:null,modes:null,envModeMap:{},getMode:function(t,e){var o=this.modes[t];return o&&e&&o.decorateWith(e),o||null},getModeFor:function(t,e){return this.getMode(this.envModeMap[t],e)}});return{AControl:c,AFMVCModule:function(t){if(!r.is(t,"Object"))throw new Error("Incorrect type of an input argument. Expected: Object MVCConstructors");if(!r.is(t.Model,"Function"))throw new Error("Model constructor should be a function");if(!r.is(t.Modes,"Object"))throw new Error("Incorrect type for model's modes");for(var e in t.Modes)if(!r.is(t.Modes[e],"Function"))throw new Error('Incorrect type of a mode "'+e+'", Function expected');return r.is(t.Module,"Function")||(t.Module=a),function(e,o,n){var r,i=new(t.Model.bind.apply(t.Model,[null].concat(e))),c={};for(var s in t.Modes)n&&(r=n[s]),c[s]=new t.Modes[s](i,r);return new t.Module(i,c,o)}},AFMode:function(t){if(!r.is(t,"Object"))throw new Error("Incorrect type of the constructors. Object expected.");if(!r.is(t.View,"Function"))throw new Error("Incorrect type of a view's constructor. Expected: Function");if(t.Control&&!r.is(t.Control,"Function"))throw new Error("Incorrect type of a control's constructor. Expected: Function");if(t.Decorators){if(!r.is(t.Decorators,"Object"))throw new Error("Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor");for(var e in t.Decorators)if(!r.is(t.Decorators[e],"Function"))throw new Error("Incorrect type of a decorator's constructor \""+e+'". Expected: Function')}return function(e,o){var n,r=new t.View(o);if(t.Decorators){n={};for(var i in t.Decorators)n[i]=new t.Decorators[i](o)}var c;return t.Control&&(c=new t.Control(o)),new u({model:e,view:r,decorators:n,control:c,config:o})}},AModeComponent:i,AView:s,DynamicView:d,ADecorator:h,MVCModule:a,Mode:u,StaticView:l,utils:r}}); | ||
!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["class-wrapper","@valerii-zinchenko/observer","lodash"],function(o,n,r){return e["mvc-pack"]=t(o,n,r)}):"object"==typeof module&&module.exports?module.exports=t(require("class-wrapper"),require("@valerii-zinchenko/observer"),require("lodash")):e["mvc-pack"]=t(e["class-wrapper"],e.Observer,e._)}(this,function(e,t,o){var n=e.Class,r={whatIs:function(e){return Object.prototype.toString.call(e)},is:function(e,t){return this.whatIs(e)==="[object "+t+"]"}},i=n(function(e){e&&r.is(e,"Object")&&(this.config=e),Object.keys(this._bind2this).forEach(function(e){this[e]=this._bind2this[e].bind(this)},this)},{__name:"AModeComponent",model:null,config:null,_bind2this:{},connect:function(){},destruct:function(){this.model=null,this.config=null},setModel:function(e){if(!r.is(e,"Object"))throw new Error("Model for the mode is not defined");this.model=e}}),c=n(i,null,{__name:"AControl",Encapsulate:[t],destruct:function(){this._events={},i.prototype.destruct.call(this)}}),s=n(i,null,{__name:"AView",control:null,destruct:function(){this.control=null,i.prototype.destruct.call(this)},setControl:function(e){if(!(e instanceof c))throw new Error("Incorrect type of control component");this.control=e},render:function(){},update:function(){},insertView:function(e,t){for(;e.firstChild;)e.removeChild(e.firstChild);this.appendView(e,t)},appendView:function(e,t){for(var o=0,n=t.elements.length;o<n;o++)e.appendChild(t.elements[o]);t.update()},_initElements:function(){},_attachEvents:function(){}}),l=n(s,function(){this.element=document.querySelector(this.selector)},{__name:"StaticView",selector:"div",element:null,destruct:function(){this.element.remove(),s.prototype.destruct.call(this)},show:function(){this.element.classList.remove("hidden")},hide:function(){this.element.classList.add("hidden")},render:function(){return this._initElements(),this._attachEvents(),this.element}}),d=n(s,null,{__name:"DynamicView",template:"<div></div>",elements:[],_tmpContainer:"div",destruct:function(){this.elements.forEach(function(e){e.remove()}),s.prototype.destruct.call(this)},render:function(){return this._processTemplate(),this._initElements(),this._attachEvents(),this.elements},_processTemplate:function(){this.element=document.createElement(this._tmpContainer),this.element.innerHTML=o.template(this.template)(this);for(var e=this.element.children,t=0,n=e.length;t<n;t++)this.elements.push(e[t])}}),u=n(d,null,{__name:"ADecorator",template:"<div data-mvc-container></div>",_component:null,container:null,setComponent:function(e){if(!(e instanceof s))throw new Error('Incorrect type of the "component" argument. Expected AView');this._component=e},destruct:function(){this._component&&this._component.destruct(),d.prototype.destruct.call(this)},render:function(){return d.prototype.render.call(this),this._component&&this._component.elements.forEach(function(e){this.container.appendChild(e)},this),this.elements},update:function(){this._component&&this._component.update(),d.prototype.update.call(this)},_processTemplate:function(){d.prototype._processTemplate.call(this),this.container=this.element.querySelector("[data-mvc-container]")}}),h=n(function(e){if(!r.is(e,"Object"))throw new Error("Incorrect type of the mode's properties. Expected: Object");if(!r.is(e.model,"Object"))throw new Error("Incorrect type of the model. Expected: Object");if(this.model=e.model,!r.is(e.View,"Function"))throw new Error("Incorrect type of a view's constructor. Expected: Function");if(this.View=e.View,e.Decorators){if(!r.is(e.Decorators,"Object"))throw new Error("Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor");for(var t in e.Decorators)if(!r.is(e.Decorators[t],"Function"))throw new Error("Incorrect type of a decorator's constructor \""+t+'". Expected: Function');this.Decorators=e.Decorators}if(this.config=e.config,e.Control){if(!r.is(e.Control,"Function"))throw new Error("Incorrect type of a control's constructor. Expected: Function");var o=new e.Control(this.config);if(!(o instanceof c))throw new Error("Control should be inherited from AControl");this.control=o,this.control.setModel(this.model),this.control.connect()}},{__name:"Mode",model:null,View:null,Decorators:{},control:null,_usages:{},getView:function(e,t){if(e||(e="default"),this._usages[e])return this._usages[e];var o=new this.View(this.config);if(!(o instanceof s))throw new Error("View should be inherited from AView");return o.setModel(this.model),this.control&&o.setControl(this.control),o.connect(),o.render(),o=this.decorate(o,t),this._usages[e]=o,o},decorate:function(e,t){if(!(e instanceof s))throw new Error("View should be inherited from AView");if(0===this.Decorators.length||!t)return e;r.is(t,"String")&&(t=[t]);for(var o,n,i=e,c=0,l=t.length;c<l;c++){if(!(n=this.Decorators[t[c]]))throw new Error('Constructor for decorator "'+t[c]+'" is not defined');if(!((o=new n(this.config))instanceof u))throw new Error('Decorator "'+t[c]+'" should be inherited from ADecorator');o.setComponent(i),o.setModel(this.model),o.render(),i=o}return i}}),a=n(function(e,t,o){if(!r.is(e,"Object")||!r.is(t,"Object"))throw new Error("Incorrect types of input arguments. Expected: Object model, Object modes");this.model=e,this.modes=t,r.is(o,"Object")&&(this.envModeMap=o)},{__name:"MVCModule",model:null,modes:null,envModeMap:{},getMode:function(e){return this.modes[e]||null},getModeFor:function(e){return this.getMode(this.envModeMap[e])}});return{AControl:c,AFMVCModule:function(e){if(!r.is(e,"Object"))throw new Error("Incorrect type of an input argument. Expected: Object MVCConstructors");if(!r.is(e.Model,"Function"))throw new Error("Model constructor should be a function");if(!r.is(e.Modes,"Object"))throw new Error("Incorrect type for model's modes");for(var t in e.Modes)if(!r.is(e.Modes[t],"Function"))throw new Error('Incorrect type of a mode "'+t+'", Function expected');return r.is(e.Module,"Function")||(e.Module=a),function(t,o,n){var r,i=new(e.Model.bind.apply(e.Model,[null].concat(t))),c={};for(var s in e.Modes)n&&(r=n[s]),c[s]=new e.Modes[s](i,r);return new e.Module(i,c,o)}},AFMode:function(e){if(!r.is(e,"Object"))throw new Error("Incorrect type of the constructors. Object expected.");return function(t,o){return new h({model:t,View:e.View,Control:e.Control,Decorators:e.Decorators,config:o})}},AModeComponent:i,AView:s,DynamicView:d,ADecorator:u,MVCModule:a,Mode:h,StaticView:l,utils:r}}); |
@@ -50,2 +50,3 @@ module.exports = function(grunt) { | ||
{'class-wrapper': 'ClassWrapper'}, | ||
{'@valerii-zinchenko/observer': 'Observer'}, | ||
{'lodash': '_'} | ||
@@ -55,2 +56,3 @@ ], | ||
'class-wrapper', | ||
'Observer', | ||
'_' | ||
@@ -57,0 +59,0 @@ ] |
@@ -19,3 +19,3 @@ /* | ||
* | ||
* @version 2.0.0 | ||
* @version 3.0.0 | ||
*/ | ||
@@ -31,31 +31,17 @@ | ||
* @extends AModeComponent | ||
* @mixes Observer | ||
*/ | ||
var AControl = Class(AModeComponent, null, /** @lends AControl.prototype */{ | ||
/** | ||
* Reference to the view. | ||
*/ | ||
view: null, | ||
__name: 'AControl', | ||
Encapsulate: [Observer], | ||
/** | ||
* Control destructor. | ||
* Control destructor | ||
*/ | ||
destruct: function() { | ||
this.view = null; | ||
this._events = {}; | ||
AModeComponent.prototype.destruct.call(this); | ||
}, | ||
/** | ||
* Set view | ||
* | ||
* @param {Object} view - View | ||
* | ||
* @throws {Error} Incorrect type of view component | ||
*/ | ||
setView: function(view) { | ||
if (!(view instanceof AView)) { | ||
throw new Error('Incorrect type of view component'); | ||
} | ||
this.view = view; | ||
} | ||
}); |
@@ -33,2 +33,4 @@ /* | ||
var ADecorator = Class(DynamicView, null, /** @lends ADecorator.prototype */{ | ||
__name: 'ADecorator', | ||
template: '<div data-mvc-container></div>', | ||
@@ -35,0 +37,0 @@ |
@@ -12,5 +12,5 @@ /* | ||
/** | ||
* @file It contains the implementation of [abstract mode factory]{@link AFMode} that returns [mode builder]{@link BMode}. | ||
* @file It contains the implementation of [abstract mode factory]{@link AFMode} that returns [mode builder]{@link FMode}. | ||
* | ||
* @see {@link BMode} | ||
* @see {@link FMode} | ||
* @see {@link Mode} | ||
@@ -20,3 +20,3 @@ * | ||
* | ||
* @version 3.0.0 | ||
* @version 4.0.0 | ||
*/ | ||
@@ -31,6 +31,2 @@ | ||
* @throws {Error} Incorrect type of the constructors. Object expected. | ||
* @throws {Error} Incorrect type of a view's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of a control's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor | ||
* @throws {Error} Incorrect type of a decorator's constructor "{name}". Expected: Function | ||
* | ||
@@ -40,4 +36,4 @@ * @param {Object} Constructors - Collection of constructors for a mode. | ||
* @param {Function} [Constructors.Control] - Constructor for the control component. | ||
* @param {Object} [Constructors.Decorators] - Map of decorators' names and decorators' construstors for a new mode. If some constructor is not a function, then it will be skipped in order to avoid NPE | ||
* @return {BMode} Mode builder. | ||
* @param {Object} [Constructors.Decorators] - Map of decorators' names and decorators' constructors for a new mode. If some constructor is not a function, then it will be skipped in order to avoid NPE | ||
* @return {FMode} Mode factory. | ||
*/ | ||
@@ -48,24 +44,7 @@ function AFMode(Constructors) { | ||
} | ||
if (!utils.is(Constructors.View, 'Function')) { | ||
throw new Error('Incorrect type of a view\'s constructor. Expected: Function'); | ||
} | ||
if (Constructors.Control && !utils.is(Constructors.Control, 'Function')) { | ||
throw new Error('Incorrect type of a control\'s constructor. Expected: Function'); | ||
} | ||
if (Constructors.Decorators) { | ||
if (!utils.is(Constructors.Decorators, 'Object')) { | ||
throw new Error('Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor'); | ||
} | ||
for (var key in Constructors.Decorators) { | ||
if (!utils.is(Constructors.Decorators[key], 'Function')) { | ||
throw new Error('Incorrect type of a decorator\'s constructor "' + key + '". Expected: Function'); | ||
} | ||
} | ||
} | ||
/** | ||
* Mode builder. | ||
* Mode factory. | ||
* | ||
* @name BMode | ||
* @name FMode | ||
* @type {Function} | ||
@@ -78,22 +57,7 @@ * | ||
return function(model, config) { | ||
var view = new Constructors.View(config); | ||
var decorators; | ||
if (Constructors.Decorators) { | ||
decorators = {}; | ||
for (var key in Constructors.Decorators) { | ||
decorators[key] = new Constructors.Decorators[key](config); | ||
} | ||
} | ||
var control; | ||
if (Constructors.Control) { | ||
control = new Constructors.Control(config); | ||
} | ||
return new Mode({ | ||
model: model, | ||
view: view, | ||
decorators: decorators, | ||
control: control, | ||
View: Constructors.View, | ||
Control: Constructors.Control, | ||
Decorators: Constructors.Decorators, | ||
config: config | ||
@@ -100,0 +64,0 @@ }); |
@@ -20,3 +20,3 @@ /* | ||
* | ||
* @version 3.0.1 | ||
* @version 3.1.0 | ||
*/ | ||
@@ -38,3 +38,10 @@ | ||
} | ||
// Automatically bind all defined function to "this" and store them into "this" under the same name | ||
Object.keys(this._bind2this).forEach(function(name) { | ||
this[name] = this._bind2this[name].bind(this); | ||
}, this); | ||
}, /** @lends AModeComponent.prototype */{ | ||
__name: 'AModeComponent', | ||
/** | ||
@@ -55,2 +62,11 @@ * Reference to the model. | ||
/** | ||
* Set of function, which will be bound automatically to "this" by constructing an instance. | ||
* This is most useful for event handlers, which in most cases should have the context of current class. | ||
* Be careful, as that can overwrite other properties/methods defined in the class. | ||
* | ||
* @type {Object} | ||
*/ | ||
_bind2this: {}, | ||
/** | ||
* Connect view component to the module. | ||
@@ -57,0 +73,0 @@ * This is called after constructor and setting of the model and control components. |
@@ -35,2 +35,4 @@ /* | ||
var AView = Class(AModeComponent, null, /** @lends AView.prototype */{ | ||
__name: 'AView', | ||
/** | ||
@@ -80,2 +82,33 @@ * Reference to the control. | ||
/** | ||
* Insert additionl module into current view | ||
* | ||
* @param {DOMElement} container - Container element, where the module is going to be inserted. This should be empty, because it will be emptied before insertion | ||
* @param {AView} view - View to insert | ||
*/ | ||
insertView: function(container, view) { | ||
// cleanup the container | ||
while (container.firstChild) { | ||
container.removeChild(container.firstChild); | ||
} | ||
this.appendView(container, view); | ||
}, | ||
/** | ||
* Appends the view's elements into the container | ||
* | ||
* @param {DOMElement} container - Container element, where the module is going to be inserted. This should be empty, because it will be emptied before insertion | ||
* @param {AView} view - View to insert | ||
*/ | ||
appendView: function(container, view) { | ||
// insert view's elements | ||
for (var n = 0, N = view.elements.length; n < N; n++) { | ||
container.appendChild(view.elements[n]); | ||
} | ||
view.update(); | ||
}, | ||
/** | ||
* Initialize required for view elements. | ||
@@ -82,0 +115,0 @@ */ |
@@ -20,2 +20,1 @@ /* | ||
var Class = ClassWrapper.Class; | ||
var SingletonClass = ClassWrapper.SingletonClass; |
@@ -34,2 +34,4 @@ /* | ||
var DynamicView = Class(AView, null, /** @lends DynamicView.prototype */{ | ||
__name: 'DynamicView', | ||
/** | ||
@@ -36,0 +38,0 @@ * Main template. |
196
lib/Mode.js
@@ -14,10 +14,11 @@ /* | ||
* | ||
* @see {@link AFMode} | ||
* @see {@link AModeComponent} | ||
* @see {@link AView} | ||
* @see {@link AControl} | ||
* @see {@link AFMode} | ||
* @see {@link ADecorator} | ||
* | ||
* @author Valerii Zinchenko | ||
* | ||
* @version 5.0.0 | ||
* @version 6.0.0 | ||
*/ | ||
@@ -31,13 +32,17 @@ | ||
* @class | ||
* @param {Object} properties - Mode's properties. | ||
* @param {Object} properties.model - Model. | ||
* @param {AView} properties.view - Main mode's view. | ||
* @param {AControl} [properties.control] - Mode's control. | ||
* @param {Object} [properties.decorators] - Object of decorators for a view, where the key is a decorator name and value is a decorator. | ||
* | ||
* @throws {Error} Incorrect type of the mode's properties. Expected: Object | ||
* @throws {Error} Incorrect type of the model. Expected: Object | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* @throws {Error} Incorrect type of a view's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of a control's constructor. Expected: Function | ||
* @throws {Error} Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor | ||
* @throws {Error} Incorrect type of a decorator's constructor "{name}". Expected: Function | ||
* @throws {Error} Control should be inherited from AControl | ||
* | ||
* @param {Object} properties - Mode's properties. | ||
* @param {Object} properties.model - Model. | ||
* @param {Function} properties.View - Constructor for the view component. | ||
* @param {Function} [properties.Control] - Constructor for the control component. | ||
* @param {Object} [properties.Decorators] - Map of decorators' names and decorators' construstors for a new mode. | ||
* @param {Object} [properties.config] - Configurations for control, view and decorator components. | ||
*/ | ||
@@ -48,37 +53,50 @@ var Mode = Class(function(properties) { | ||
} | ||
if (!utils.is(properties.model, 'Object')) { | ||
if (utils.is(properties.model, 'Object')) { | ||
this.model = properties.model; | ||
} else { | ||
throw new Error('Incorrect type of the model. Expected: Object'); | ||
} | ||
if (!(properties.view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
if (utils.is(properties.View, 'Function')) { | ||
this.View = properties.View; | ||
} else { | ||
throw new Error('Incorrect type of a view\'s constructor. Expected: Function'); | ||
} | ||
this.model = properties.model; | ||
this._view = properties.view; | ||
if (properties.Decorators) { | ||
if (!utils.is(properties.Decorators, 'Object')) { | ||
throw new Error('Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor'); | ||
} | ||
if (properties.decorators && utils.is(properties.decorators, 'Object')) { | ||
for (var key in properties.decorators) { | ||
if ( !(properties.decorators[key] instanceof ADecorator) ) { | ||
throw new Error('Decorator "' + key + '" should be inherited from ADecorator'); | ||
for (var key in properties.Decorators) { | ||
if (!utils.is(properties.Decorators[key], 'Function')) { | ||
throw new Error('Incorrect type of a decorator\'s constructor "' + key + '". Expected: Function'); | ||
} | ||
} | ||
this._decorators[key] = properties.decorators[key]; | ||
} | ||
this.Decorators = properties.Decorators; | ||
} | ||
if (properties.control) { | ||
if ( !(properties.control instanceof AControl) ) { | ||
this.config = properties.config; | ||
if (properties.Control) { | ||
if (!utils.is(properties.Control, 'Function')) { | ||
throw new Error('Incorrect type of a control\'s constructor. Expected: Function'); | ||
} | ||
var control = new properties.Control(this.config); | ||
if ( !(control instanceof AControl) ) { | ||
throw new Error('Control should be inherited from AControl'); | ||
} | ||
this.control = properties.control; | ||
this.control = control; | ||
this.control.setModel(this.model); | ||
this.control.connect(); | ||
} | ||
}, /** @lends Mode.prototype */{ | ||
__name: 'Mode', | ||
this.view = this._view; | ||
this.connect(); | ||
}, /** @lends Mode.prototype */{ | ||
/** | ||
* Reference to the model. | ||
* | ||
* @type {Objcet} | ||
@@ -89,18 +107,15 @@ */ | ||
/** | ||
* Reference to the main view. | ||
* | ||
* @type {AView} | ||
* View constructor | ||
* @type {Function} | ||
*/ | ||
_view: null, | ||
View: null, | ||
/** | ||
* Current view. This can be an original or decorated view. | ||
* | ||
* @type {DynamicView} | ||
* Map of decorators' names and decorators' construstors for a new mode | ||
* @type {Object} | ||
*/ | ||
view: null, | ||
Decorators: {}, | ||
/** | ||
* Reference to the control. | ||
* | ||
* @type {AControl} | ||
@@ -111,75 +126,92 @@ */ | ||
/** | ||
* Object of decorators, where the key is a name of a deocorator and value is a decorator object. | ||
* | ||
* Map of usages and created views for it | ||
* @type {Object} | ||
*/ | ||
_decorators: {}, | ||
_usages: {}, | ||
/** | ||
* This indicates if a mode components are already connected or not. | ||
* Get an instance of the view for a specific usage. | ||
* | ||
* @type {Boolean} | ||
* For each usage a new instance of the view will be created and, if needed, decorated also with new instances of decorators. | ||
* This allows to use the same view in different places with or without different decorators. | ||
* | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* | ||
* @param {String} [usage = 'default'] - Define for how/where he view is going to be used. For different usages different instances of a view and decorators will be created. | ||
* @param {String[] | String} [decorators] - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
*/ | ||
_isConnected: false, | ||
getView: function(usage, decorations) { | ||
if (!usage) { | ||
usage = 'default'; | ||
} | ||
if (this._usages[usage]) { | ||
return this._usages[usage]; | ||
} | ||
/** | ||
* Connect mode components | ||
*/ | ||
connect: function() { | ||
if (this._isConnected) { | ||
return; | ||
var view = new this.View(this.config); | ||
if (!(view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
} | ||
this._view.setModel(this.model); | ||
view.setModel(this.model); | ||
if (this.control) { | ||
this._view.setControl(this.control); | ||
this.control.setModel(this.model); | ||
this.control.setView(this._view); | ||
this.control.connect(); | ||
view.setControl(this.control); | ||
} | ||
this._view.connect(); | ||
this._view.render(); | ||
view.connect(); | ||
view.render(); | ||
view = this.decorate(view, decorations); | ||
var decorator; | ||
for (var key in this._decorators) { | ||
decorator = this._decorators[key]; | ||
decorator.setModel(this.model); | ||
decorator.render(); | ||
} | ||
this._usages[usage] = view; | ||
this._isConnected = true; | ||
return view; | ||
}, | ||
/** | ||
* Apply decorators to the original view. | ||
* Apply decorators to a view. | ||
* | ||
* @param {String[] | String } decorators - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
* @throws {Error} View should be inherited from AView | ||
* @throws {Error} Constructor for decorator "{name}" is not defined | ||
* @throws {Error} Decorator "{name}" should be inherited from ADecorator | ||
* | ||
* @param {AView} view - View, which should be decorated. | ||
* @param {String[] | String} decorators - An array of decorators name or a single decorator name. Decorators will be applied in the same order as they are defined. | ||
* @return {Aview} | ||
*/ | ||
decorateWith: function(decorators){ | ||
if (!decorators) { | ||
return; | ||
decorate: function(view, decorators){ | ||
if (!(view instanceof AView)) { | ||
throw new Error('View should be inherited from AView'); | ||
} | ||
if (this.Decorators.length === 0 || !decorators) { | ||
return view; | ||
} | ||
if (utils.is(decorators, 'String')){ | ||
this.decorateWith([decorators]); | ||
return; | ||
decorators = [decorators]; | ||
} | ||
var v = this._view; | ||
var v = view; | ||
var decorator; | ||
var Constructor; | ||
for (var n = 0, N = decorators.length; n < N; n++) { | ||
Constructor = this.Decorators[decorators[n]]; | ||
if (!Constructor) { | ||
throw new Error('Constructor for decorator "' + decorators[n] + '" is not defined'); | ||
} | ||
if (this._decorators && decorators.length > 0) { | ||
var decorator; | ||
for (var n = 0, N = decorators.length; n < N; n++) { | ||
decorator = this._decorators[decorators[n]]; | ||
if (decorator) { | ||
decorator.setComponent(v); | ||
decorator = new Constructor(this.config); | ||
v = decorator; | ||
} | ||
if ( !(decorator instanceof ADecorator) ) { | ||
throw new Error('Decorator "' + decorators[n] + '" should be inherited from ADecorator'); | ||
} | ||
decorator.setComponent(v); | ||
decorator.setModel(this.model); | ||
decorator.render(); | ||
v = decorator; | ||
} | ||
this.view = v; | ||
return v; | ||
} | ||
}); |
@@ -45,2 +45,4 @@ /* | ||
}, /** @lends MVCModule.prototype */{ | ||
__name: 'MVCModule', | ||
/** | ||
@@ -72,13 +74,6 @@ * Module's model | ||
* @param {String} modeName - Mode name. | ||
* @param {String | String[]} [decorators] - Mode's decorators. | ||
* @return {Mode | Null} | ||
*/ | ||
getMode: function(modeName, decorators) { | ||
var mode = this.modes[modeName]; | ||
if (mode && decorators) { | ||
mode.decorateWith(decorators); | ||
} | ||
return mode || null; | ||
getMode: function(modeName) { | ||
return this.modes[modeName] || null; | ||
}, | ||
@@ -90,8 +85,7 @@ | ||
* @param {String} env - Target environment. | ||
* @param {String | String[]} [decorators] - Mode's decorators. | ||
* @return {Mode | Null} | ||
*/ | ||
getModeFor: function(env, decorators){ | ||
return this.getMode(this.envModeMap[env], decorators); | ||
getModeFor: function(env){ | ||
return this.getMode(this.envModeMap[env]); | ||
} | ||
}); |
@@ -27,3 +27,3 @@ /* | ||
* Static view. | ||
* It implements parent's [render()]{@link render} method to initalize required elements and attach eventh. | ||
* It implements parent's [render()]{@link render} method to initialize required elements and attach events. | ||
* | ||
@@ -33,5 +33,7 @@ * @class | ||
*/ | ||
var StaticView = SingletonClass(AView, function() { | ||
var StaticView = Class(AView, function() { | ||
this.element = document.querySelector(this.selector); | ||
}, /** @lends StaticView.prototype */{ | ||
__name: 'StaticView', | ||
/** | ||
@@ -38,0 +40,0 @@ * Selector of a static element from a DOM |
{ | ||
"name": "mvc-pack", | ||
"version": "0.11.0", | ||
"version": "0.12.0", | ||
"description": "Package of combined patterns to build rich MVC modules", | ||
@@ -16,4 +16,4 @@ "main": "dest/mvc-pack.js", | ||
"doc": "grunt doc", | ||
"preversion": "npm test", | ||
"prepublishOnly": "grunt build" | ||
"prepublishOnly": "grunt build", | ||
"build": "grunt build" | ||
}, | ||
@@ -27,2 +27,3 @@ "directories": { | ||
"dependencies": { | ||
"@valerii-zinchenko/observer": "^1.0.2", | ||
"class-wrapper": "^1.0.3", | ||
@@ -42,6 +43,5 @@ "lodash": "^4.17.4" | ||
"grunt-template": "^1.0.0", | ||
"grunt-umd": "^2.4.0", | ||
"grunt-umd": "^3.0.0", | ||
"istanbul": "^0.4.5", | ||
"jsdoc-inheritance-diagram": "^1.0.0", | ||
"libumd": "github:valerii-zinchenko/libumd", | ||
"jsdoc-inheritance-diagram": "^1.2.1", | ||
"load-grunt-tasks": "^3.5.2", | ||
@@ -48,0 +48,0 @@ "mocha": "^4.0.1", |
@@ -8,2 +8,10 @@ 'use strict'; | ||
}); | ||
test('Mixing of Observer', function() { | ||
var c = new AControl(); | ||
assert.equal(c.listen, Observer.prototype.listen); | ||
assert.equal(c.trigger, Observer.prototype.trigger); | ||
assert.equal(c.removeListener, Observer.prototype.removeListener); | ||
}); | ||
}); | ||
@@ -22,48 +30,7 @@ | ||
suite('setView', function(){ | ||
suite('incorrect view instance', function(){ | ||
[].concat( | ||
[ | ||
undefined, | ||
null, | ||
false, | ||
true, | ||
0, | ||
1, | ||
'', | ||
'1', | ||
[], | ||
{}, | ||
function(){} | ||
].map(function(input){ | ||
return { | ||
title: 'when type of input: ' + Object.prototype.toString.call(input) + ' and value: ' + input, | ||
input: input | ||
}; | ||
}) | ||
).forEach(function(testCase){ | ||
test(testCase.title, function(){ | ||
assert.throw(function(){ | ||
aControl.setView(testCase.input); | ||
}, Error, 'Incorrect type of view component'); | ||
}); | ||
}); | ||
}); | ||
test('correct view instance', function(){ | ||
var view = new AView({}); | ||
assert.doesNotThrow(function(){ | ||
aControl.setView(view); | ||
}); | ||
assert.equal(aControl.view, view, 'View was incorrectly set'); | ||
}); | ||
}); | ||
test('desctuct', function(){ | ||
assert.doesNotThrow(function(){ | ||
aControl.setView(new AView({})); | ||
aControl.destruct(); | ||
}); | ||
assert.isNull(aControl.view, 'destruct() should set view to null'); | ||
assert.isTrue(AModeComponent.prototype.destruct.calledOnce, 'Parent\'s destruct() should be called'); | ||
@@ -70,0 +37,0 @@ }); |
@@ -1,3 +0,1 @@ | ||
'use strict'; | ||
suite('AFMode', function(){ | ||
@@ -23,77 +21,2 @@ suite('incorrect input argument', function(){ | ||
}; | ||
}), | ||
[ | ||
undefined, | ||
null, | ||
false, | ||
true, | ||
0, | ||
1, | ||
'', | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a View constructor: ' + Object.prototype.toString(input) + ' with a value: ' + input, | ||
input: {View: input}, | ||
expected: 'Incorrect type of a view\'s constructor. Expected: Function' | ||
}; | ||
}), | ||
[ | ||
true, | ||
1, | ||
'str', | ||
[], | ||
function(){} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Decorators: ' + Object.prototype.toString(input) + ' with a value: ' + input, | ||
input: { | ||
View: function(){}, | ||
Decorators: input | ||
}, | ||
expected: 'Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor' | ||
}; | ||
}), | ||
[ | ||
undefined, | ||
null, | ||
false, | ||
true, | ||
0, | ||
1, | ||
'', | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Decorator constructor: ' + Object.prototype.toString(input) + ' with a value: ' + input, | ||
input: { | ||
View: function(){}, | ||
Decorators: { | ||
Deco: input | ||
} | ||
}, | ||
expected: 'Incorrect type of a decorator\'s constructor "Deco". Expected: Function' | ||
}; | ||
}), | ||
[ | ||
true, | ||
1, | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Control: ' + Object.prototype.toString(input) + ' with a value: ' + input, | ||
input: { | ||
View: function(){}, | ||
Control: input | ||
}, | ||
expected: 'Incorrect type of a control\'s constructor. Expected: Function' | ||
}; | ||
}) | ||
@@ -140,3 +63,3 @@ ).forEach(function(testCase){ | ||
suite('Mode builder. Integration tests with Mode', function(){ | ||
suite('Mode factory. Integration tests with Mode', function(){ | ||
setup(function(){ | ||
@@ -159,9 +82,3 @@ sinon.spy(AModeComponent.prototype, "connect"); | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = (AFMode({View: AView}))(model); | ||
}); | ||
assert.isNotNull(result.view, 'View component of the mode was not setup'); | ||
assert.equal(result.view.model, model, 'Model was incorrectly set'); | ||
var result = (AFMode({View: AView}))(model); | ||
}); | ||
@@ -172,21 +89,11 @@ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = (AFMode({ | ||
View: AView, | ||
Control: AControl | ||
}))(model); | ||
}); | ||
assert.isNotNull(result.view, 'View component of the mode was not setup'); | ||
assert.isNotNull(result.control, 'Control component of the mode was not setup'); | ||
assert.equal(result.control.model, model, 'Model was incorrectly set'); | ||
var result = (AFMode({ | ||
View: AView, | ||
Control: AControl | ||
}))(model); | ||
}); | ||
test('with Decorators', function(){ | ||
var constructor = sinon.stub(); | ||
var Deco = Class(ADecorator, null, {}); | ||
var Deco = new Class(ADecorator, constructor, {}); | ||
var incorrectDeco = {}; | ||
var result = (AFMode({ | ||
@@ -198,6 +105,2 @@ View: AView, | ||
}))({}); | ||
assert.isTrue(constructor.calledOnce, 'Decorator should be created in the mode builder'); | ||
assert.instanceOf(result._decorators.Deco, Deco, 'Decorator was incorrectly set'); | ||
assert.isUndefined(result._decorators.incorrectDeco, 'Incorrect docarator should not be passed further to a mode constructor'); | ||
}); | ||
@@ -208,14 +111,8 @@ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = (AFMode({ | ||
View: AView, | ||
Control: AControl | ||
}))({}, config); | ||
}); | ||
assert.equal(result.view.config, config, 'Configuration was incorrectly set to the view'); | ||
assert.equal(result.control.config, config, 'Configuration was incorrectly set to the control'); | ||
var result = (AFMode({ | ||
View: AView, | ||
Control: AControl | ||
}))({}, config); | ||
}); | ||
}); | ||
}); |
344
test/Mode.js
@@ -9,2 +9,3 @@ 'use strict'; | ||
return { | ||
title: 'incorrect type of input argument: ' + JSON.stringify(input), | ||
input: input, | ||
@@ -17,2 +18,3 @@ exception: 'Incorrect type of the mode\'s properties. Expected: Object' | ||
return { | ||
title: 'incorrect type of model: ' + JSON.stringify(input), | ||
input: {model: input}, | ||
@@ -23,27 +25,85 @@ exception: 'Incorrect type of the model. Expected: Object' | ||
[undefined, null, 0, 1, false, true, '', 'str', [], function(){}].map(function(input){ | ||
[ | ||
undefined, | ||
null, | ||
false, | ||
true, | ||
0, | ||
1, | ||
'', | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a View constructor: ' + Object.prototype.toString.call(input) + ' with a value: ' + JSON.stringify(input), | ||
input: { | ||
model: {}, | ||
view: input | ||
View: input | ||
}, | ||
exception: 'View should be inherited from AView' | ||
exception: 'Incorrect type of a view\'s constructor. Expected: Function' | ||
}; | ||
}), | ||
[undefined, null, 0, 1, false, true, '', 'str', [], function(){}].map(function(input){ | ||
var model = {}; | ||
[ | ||
true, | ||
1, | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Control: ' + Object.prototype.toString.call(input) + ' with a value: ' + JSON.stringify(input), | ||
input: { | ||
model: model, | ||
view: new AView(model), | ||
decorators: { | ||
deco: input | ||
model: {}, | ||
View: function(){}, | ||
Control: input | ||
}, | ||
exception: 'Incorrect type of a control\'s constructor. Expected: Function' | ||
}; | ||
}), | ||
[ | ||
true, | ||
1, | ||
'str', | ||
[], | ||
function(){} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Decorators: ' + Object.prototype.toString.call(input) + ' with a value: ' + JSON.stringify(input), | ||
input: { | ||
model: {}, | ||
View: function(){}, | ||
Decorators: input | ||
}, | ||
exception: 'Incorrect type of map of decorators. Expected: Object. Where key is a decorator name, and value is a decorator constructor' | ||
}; | ||
}), | ||
[ | ||
undefined, | ||
null, | ||
false, | ||
true, | ||
0, | ||
1, | ||
'', | ||
'str', | ||
[], | ||
{} | ||
].map(function(input){ | ||
return { | ||
title: 'type of a Decorator constructor: ' + Object.prototype.toString.call(input) + ' with a value: ' + JSON.stringify(input), | ||
input: { | ||
model: {}, | ||
View: function(){}, | ||
Decorators: { | ||
Deco: input | ||
} | ||
}, | ||
exception: 'Decorator "deco" should be inherited from ADecorator' | ||
exception: 'Incorrect type of a decorator\'s constructor "Deco". Expected: Function' | ||
}; | ||
}), | ||
[1, true, 'str', [], function(){}, {}].map(function(input){ | ||
[1, true, 'str', [], {}].map(function(input){ | ||
var model = {}; | ||
@@ -53,10 +113,19 @@ return { | ||
model: model, | ||
view: new AView(model), | ||
control: input | ||
View: AView, | ||
Control: input | ||
}, | ||
exception: 'Control should be inherited from AControl' | ||
exception: 'Incorrect type of a control\'s constructor. Expected: Function' | ||
}; | ||
}) | ||
}), | ||
{ | ||
title: 'control instance is not an instance of AControl', | ||
input: { | ||
model: {}, | ||
View: AView, | ||
Control: function(){} | ||
}, | ||
exception: 'Control should be inherited from AControl' | ||
} | ||
).forEach(function(testCase){ | ||
test('input: ' + JSON.stringify(testCase.input), function(){ | ||
test(testCase.title || ('input: ' +JSON.stringify(testCase.input)), function(){ | ||
assert.throw(function(){ | ||
@@ -77,4 +146,4 @@ new Mode(testCase.input); | ||
model: {}, | ||
view: new AView({}), | ||
control: testCase | ||
View: AView, | ||
Control: testCase | ||
}); | ||
@@ -87,19 +156,2 @@ }); | ||
}); | ||
suite('incompatible decorators object', function(){ | ||
[undefined, null, 0, 1, false, true, '', 'str', [], function(){}].forEach(function(testCase){ | ||
test('input: ' + testCase, function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = new Mode({ | ||
model: {}, | ||
view: new AView({}), | ||
decorators: testCase | ||
}); | ||
}); | ||
assert.isTrue(Object.keys(result._decorators).length === 0, 'Initiali object of decorators should not be changed'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -109,6 +161,8 @@ | ||
setup(function(){ | ||
sinon.spy(Mode.prototype, 'connect'); | ||
sinon.spy(AControl.prototype, 'setModel'); | ||
sinon.spy(AControl.prototype, 'connect'); | ||
}); | ||
teardown(function(){ | ||
Mode.prototype.connect.restore(); | ||
AControl.prototype.setModel.restore(); | ||
AControl.prototype.connect.restore(); | ||
}); | ||
@@ -118,26 +172,25 @@ | ||
var model = {}; | ||
var view = new AView(); | ||
var control = new AControl(); | ||
var decorators = { | ||
deco: new (Class(ADecorator, null, {})), | ||
anotherdeco: new (Class(ADecorator, null, {})) | ||
var Decorators = { | ||
deco: Class(ADecorator, null, {}), | ||
anotherdeco: Class(ADecorator, null, {}) | ||
}; | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = new Mode({ | ||
model: model, | ||
view: view, | ||
control: control, | ||
decorators: decorators | ||
}); | ||
var result = new Mode({ | ||
model: model, | ||
View: AView, | ||
Control: AControl, | ||
Decorators: Decorators | ||
}); | ||
assert.equal(result.model, model, 'Model was incorrectly set'); | ||
assert.equal(result._view, view, 'Main undecorated view was incorrectly set'); | ||
assert.equal(result.view, view, 'Final view was incorrectly set'); | ||
assert.equal(result.control, control, 'Control was incorrectly set'); | ||
assert.isUndefined(result._decorators.nofn, 'Incorrect decorator should not be set to a map of available decorators'); | ||
assert.isUndefined(result._decorators.incomp, 'Incompatible decoratorshould not be set to a map of available decorators'); | ||
assert.equal(result._decorators.deco, decorators.deco, 'Compatible decorator\'s constructor was lost'); | ||
assert.instanceOf(result.control, AControl, 'Control was incorrectly built'); | ||
assert.equal(result.View, AView, 'View constructor was incorrectly set'); | ||
assert.equal(result.Decorators, Decorators, 'Constructors for decorators were incorrectly set'); | ||
assert.isTrue(result.control.setModel.calledOnce, 'Model should be set to the control'); | ||
assert.isTrue(result.control.setModel.withArgs(result.model).calledOnce, 'Something wrong was set to the control as a model'); | ||
assert.isTrue(result.control.connect.calledOnce, 'Control\'s "connect" method should be called to execute the connection routine'); | ||
assert.isTrue(result.control.connect.calledAfter(result.control.setModel), 'Control\'s "connect" method should be called after model was set to it'); | ||
}); | ||
@@ -152,7 +205,7 @@ }); | ||
model: {}, | ||
view: new AView(), | ||
control: new AControl(), | ||
decorators: { | ||
deco0: new (Class(ADecorator, null, {})), | ||
deco1: new (Class(ADecorator, null, {})) | ||
View: AView, | ||
Control: AControl, | ||
Decorators: { | ||
deco0: Class(ADecorator, null, {}), | ||
deco1: Class(ADecorator, null, {}) | ||
} | ||
@@ -165,175 +218,20 @@ }); | ||
suite('connect', function(){ | ||
suite('getView', function(){ | ||
setup(function(){ | ||
mode._isConnected = false; | ||
sinon.spy(mode._view, 'setModel'); | ||
sinon.spy(mode._view, 'connect'); | ||
sinon.spy(mode._view, 'setControl'); | ||
sinon.spy(mode._view, 'render'); | ||
sinon.spy(AControl.prototype, 'setModel'); | ||
sinon.spy(AControl.prototype, 'setView'); | ||
sinon.spy(AControl.prototype, 'connect'); | ||
sinon.spy(mode._decorators.deco0, 'setModel'); | ||
sinon.spy(mode._decorators.deco0, 'render'); | ||
sinon.spy(mode._decorators.deco1, 'setModel'); | ||
sinon.spy(mode._decorators.deco1, 'render'); | ||
}); | ||
teardown(function(){ | ||
mode._view.setModel.restore(); | ||
mode._view.connect.restore(); | ||
mode._view.setControl.restore(); | ||
mode._view.render.restore(); | ||
AControl.prototype.setModel.restore(); | ||
AControl.prototype.setView.restore(); | ||
AControl.prototype.connect.restore(); | ||
mode._decorators.deco0.setModel.restore(); | ||
mode._decorators.deco0.render.restore(); | ||
mode._decorators.deco1.setModel.restore(); | ||
mode._decorators.deco1.render.restore(); | ||
}); | ||
test('without control', function(){ | ||
mode.control = null; | ||
assert.doesNotThrow(function(){ | ||
mode.connect(); | ||
mode.connect(); | ||
}); | ||
}); | ||
assert.isTrue(mode._view.setModel.withArgs(mode.model).calledOnce, 'Model should be set to the view'); | ||
assert.isTrue(mode._view.connect.calledOnce, 'Mode components was not connected to the view'); | ||
assert.isFalse(mode._view.setControl.called, 'Control component should not be set when the mode hasn\'t control component'); | ||
assert.isTrue(mode._view.render.calledAfter(mode._view.connect), 'View should be rendered after the connecting of all modes\' components'); | ||
var decorator; | ||
for (var key in this._decorators) { | ||
decorator = mode._decorators[key]; | ||
assert.isTrue(decorator.setModel.withArgs(mode.model).calledOnce, 'Model should be set into the decorator ' + key); | ||
assert.isTrue(decorator.render.calledAfter(decorator.setModel), 'Decorator "' + key + '" should be rendered after the model will be set'); | ||
} | ||
suite('decorate', function(){ | ||
setup(function(){ | ||
sinon.spy(ADecorator.prototype, 'setComponent'); | ||
}); | ||
test('with control', function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.connect(); | ||
mode.connect(); | ||
}); | ||
assert.isTrue(mode._view.setModel.withArgs(mode.model).calledOnce, 'Model should be set to the view'); | ||
assert.isTrue(mode._view.setControl.calledOnce, 'Control component was not be set'); | ||
assert.isTrue(mode._view.connect.calledOnce, 'Mode components was not connected to the view'); | ||
assert.isTrue(mode._view.setModel.calledBefore(mode._view.setControl), 'Model should be set first'); | ||
assert.isTrue(mode._view.connect.calledAfter(mode._view.setControl), 'Control should be set to the view before connect()'); | ||
assert.isTrue(mode._view.render.calledAfter(mode._view.connect), 'View should be rendered after the connecting of all modes\' components'); | ||
assert.isTrue(mode.control.setModel.withArgs(mode.model).calledOnce, 'Model should be set to the control'); | ||
assert.isTrue(mode.control.setView.calledOnce, 'Control component was not be set'); | ||
assert.isTrue(mode.control.connect.calledOnce, 'Mode components was not connected to the view'); | ||
assert.isTrue(mode.control.setModel.calledBefore(mode.control.setView), 'Model should be set first'); | ||
assert.isTrue(mode.control.connect.calledAfter(mode.control.setView), 'View should be set to the ontrol before connect()'); | ||
var decorator; | ||
for (var key in this._decorators) { | ||
decorator = mode._decorators[key]; | ||
assert.isTrue(decorator.setModel.withArgs(mode.model).calledOnce, 'Model should be set into the decorator ' + key); | ||
assert.isTrue(decorator.render.calledAfter(decorator.setModel), 'Decorator "' + key + '" should be rendered after the model will be set'); | ||
} | ||
teardown(function(){ | ||
ADecorator.prototype.setComponent.restore(); | ||
}); | ||
}); | ||
suite('decorateWith', function(){ | ||
suite('order of decoration and called methods', function(){ | ||
setup(function(){ | ||
sinon.stub(ADecorator.prototype, 'setComponent'); | ||
}); | ||
teardown(function(){ | ||
ADecorator.prototype.setComponent.restore(); | ||
}); | ||
test('decorator does not exist', function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.decorateWith('nothing'); | ||
}); | ||
assert.isFalse(ADecorator.prototype.setComponent.called, '"setComponent" should not be called at all, if decorator does not registered'); | ||
}); | ||
test('one decorator', function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.decorateWith('deco0'); | ||
}); | ||
assert.isTrue(ADecorator.prototype.setComponent.calledOnce, 'Decorator should accept some decorated view only once'); | ||
assert.isTrue(ADecorator.prototype.setComponent.withArgs(mode._view).called, 'Decorator should accept initial mode view only once'); | ||
}); | ||
test('two decorators', function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.decorateWith(['deco0', 'deco1']); | ||
}); | ||
assert.isTrue(ADecorator.prototype.setComponent.calledTwice, 'Decorator should accept some decorated view only once'); | ||
assert.isTrue(mode._decorators.deco0.setComponent.withArgs(mode._view).calledBefore(mode._decorators.deco1.setComponent.withArgs(mode._decorators.deco0)), 'Decorators were applied in the incorrect sequesnce'); | ||
}); | ||
}); | ||
suite('public view', function(){ | ||
suite('no decorating', function(){ | ||
[undefined, null, false, true, 0, 1, {}, function(){}, '', []].forEach(function(input){ | ||
test('input: ' + JSON.stringify(input), function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.decorateWith(input); | ||
}); | ||
assert.equal(mode.view, mode._view, 'Incorrect final reference was set to the this.view'); | ||
}); | ||
}); | ||
}); | ||
suite('decorating', function(){ | ||
[ | ||
{ | ||
decorators: 'deco0', | ||
expected: 'deco0' | ||
}, | ||
{ | ||
decorators: 'deco1', | ||
expected: 'deco1' | ||
}, | ||
{ | ||
decorators: ['deco1', 'deco0'], | ||
expected: 'deco0' | ||
}, | ||
{ | ||
decorators: ['deco0', 'deco1'], | ||
expected: 'deco1' | ||
}, | ||
{ | ||
decorators: ['none', 'deco0', 'deco1'], | ||
expected: 'deco1' | ||
}, | ||
{ | ||
decorators: ['deco0', 'none', 'deco1'], | ||
expected: 'deco1' | ||
}, | ||
{ | ||
decorators: ['deco0', 'deco1', 'none'], | ||
expected: 'deco1' | ||
} | ||
].forEach(function(testCase){ | ||
test(JSON.stringify(testCase.decorators), function(){ | ||
assert.doesNotThrow(function(){ | ||
mode.decorateWith(testCase.decorators); | ||
}); | ||
assert.equal(mode.view, mode._decorators[testCase.expected], 'Incorrect final reference was set to the this.view'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -71,12 +71,16 @@ 'use strict'; | ||
setup(function() { | ||
uut = new MVCModule({}, { | ||
mode: (AFMode({ | ||
View: AView, | ||
Decorators: { | ||
deco: ADecorator | ||
} | ||
}))({}) | ||
}, { | ||
someEnv: 'mode' | ||
}); | ||
uut = new MVCModule( | ||
{}, | ||
{ | ||
mode: (AFMode({ | ||
View: AView, | ||
Decorators: { | ||
deco: ADecorator | ||
} | ||
}))({}) | ||
}, | ||
{ | ||
someEnv: 'mode' | ||
} | ||
); | ||
}); | ||
@@ -88,60 +92,29 @@ teardown(function() { | ||
suite('getMode', function() { | ||
test('Undefined mode', function() { | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getMode('str'); | ||
}); | ||
test('null should be returned if requested mode is not found', function() { | ||
var result = uut.getMode('str'); | ||
assert.isNull(result, 'Null should be returned if desired mode is not exist'); | ||
assert.isNull(result); | ||
}); | ||
test('existing mode', function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getMode('mode'); | ||
}); | ||
test('mode object should be returned for the existing mapping', function(){ | ||
var result = uut.getMode('mode'); | ||
assert.equal(result, uut.modes.mode, 'Incorrect existing mode was returned'); | ||
assert.equal(result, uut.modes.mode); | ||
}); | ||
test('with decorator', function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getMode('mode', 'deco'); | ||
}); | ||
assert.equal(result.view, uut.modes.mode._decorators.deco, 'Afeter decorating the "view" should point to the decorator object'); | ||
}); | ||
}); | ||
suite('getModeFor', function(){ | ||
test('not registered mapping', function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getModeFor('eeeeenv'); | ||
}); | ||
test('null should be returned if mapping to a mode is not found', function(){ | ||
var result = uut.getModeFor('eeeeenv'); | ||
assert.isNull(result, 'Null should be returned, because it is not defined what mode should be used for a specific environment'); | ||
assert.isNull(result); | ||
}); | ||
test('registered mapping', function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getModeFor('someEnv'); | ||
}); | ||
test('mode should be returned for the existing mapping', function(){ | ||
var result = uut.getModeFor('someEnv'); | ||
assert.equal(result, uut.modes.mode, 'Incorrect mode was returned'); | ||
assert.equal(result, uut.modes.mode); | ||
}); | ||
test('registered mapping with a decorator', function(){ | ||
var result; | ||
assert.doesNotThrow(function(){ | ||
result = uut.getModeFor('someEnv', 'deco'); | ||
}); | ||
assert.equal(result, uut.modes.mode, 'Incorrect mode was returned'); | ||
assert.equal(result.view, uut.modes.mode._decorators.deco, 'Mode was not decorated'); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -12,6 +12,2 @@ 'use strict'; | ||
}); | ||
test('is singleton', function(){ | ||
assert.equal(new StaticView(), new StaticView(), 'StaticView should behave as singleton'); | ||
}); | ||
}); | ||
@@ -18,0 +14,0 @@ |
Sorry, the diff of this file is not supported yet
16
100502
3
3106
+ Added@valerii-zinchenko/observer@1.0.3(transitive)