backbone-base-view
Advanced tools
Comparing version 2.0.0 to 2.1.0
{ | ||
"name": "backboneBaseView", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Baseview is a extended backbone view with convenient methods for manipulating subviews and events.", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/dbrekalo/backbone-base-view", |
@@ -14,3 +14,3 @@ (function(root, factory) { | ||
var variableInEventStringRE = /{{(\S+)}}/g; | ||
var variableInEventStringRE = /{{\s*(\S+)\s*}}/g; | ||
var specialSelectors = { | ||
@@ -46,4 +46,4 @@ 'window': window, | ||
if (this.assignOptions) { | ||
var defaults = _.result(this, 'defaults'); | ||
this.options = this.assignOptions === 'deep' ? $.extend(true, {}, defaults, options) : _.extend({}, defaults, options); | ||
this.writeOptions.apply(this, arguments); | ||
this.optionRules && this.validateOptions(this.options, this.optionRules); | ||
} | ||
@@ -60,2 +60,56 @@ | ||
writeOptions: function(options) { | ||
var defaults = _.result(this, 'defaults'); | ||
var ruleDefaults = {}; | ||
this.optionRules && _.each(this.optionRules, function(data, optionName) { | ||
ruleDefaults[optionName] = data.default; | ||
}); | ||
if (this.assignOptions === 'deep') { | ||
this.options = $.extend(true, {}, defaults, ruleDefaults, options); | ||
} else { | ||
this.options = $.extend({}, defaults, ruleDefaults, options); | ||
} | ||
return this; | ||
}, | ||
validateOptions: function(options, rules) { | ||
var errors = []; | ||
_.each(rules, function(data, optionName) { | ||
var optionValue = options[optionName]; | ||
var optionValueType = typeof optionValue; | ||
if (data.required !== false || optionValueType !== 'undefined') { | ||
if (data.type && optionValueType !== data.type) { | ||
errors.push('Option "' + optionName +'" is ' + optionValueType + ', expected ' + data.type + '.'); | ||
} | ||
if (data.rule && !data.rule(optionValue)) { | ||
errors.push('Option "' + optionName +'" breaks defined rule.'); | ||
} | ||
if (data.instanceOf && !(optionValue instanceof data.instanceOf)) { | ||
errors.push('Option "' + optionName +'" is not instance of defined constructor.'); | ||
} | ||
} | ||
}); | ||
if (errors.length) { | ||
throw new Error(errors.join(' ')); | ||
} else { | ||
return this; | ||
} | ||
}, | ||
setupEvents: function(eventsMap) { | ||
@@ -62,0 +116,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(a,b){"function"==typeof define&&define.amd?define(["jquery","backbone","underscore"],b):"object"==typeof module&&module.exports?module.exports=b(require("jquery"),require("backbone"),require("underscore")):a.BaseView=b(a.jQuery,a.Backbone,a._)}(this,function(a,b,c){var d={window:window,document:window.document},e=function(a,b){return a.replace(/{{(\S+)}}/g,function(a,c){var d=0===c.indexOf("this."),e=d?b:window,f=(d?c.slice(5):c).split(".");for(var g in f)if(void 0===(e=e[f[g]]))throw new Error("Undefined variable in event string");return e})},f=b.View.extend({constructor:function(d){if(this.assignOptions){var e=c.result(this,"defaults");this.options="deep"===this.assignOptions?a.extend(!0,{},e,d):c.extend({},e,d)}b.View.apply(this,arguments),this.events&&this.setupEvents()},delegatedEvents:!0,parseEventVariables:!0,assignOptions:!1,setupEvents:function(b){var f=b||this.events,g="function"==typeof f?f.call(this):f,h=this;if(g){var i=this.ens=this.ens||"."+this.cid;c.each(g,function(b,c){h.parseEventVariables&&(c=e(c,h));var f=0===c.indexOf("one:"),g=(f?c.slice(4):c).split(" "),j=g[0]+i,k=g.slice(1).join(" "),l=h.$el;d[k]?(l=h["$"+k]=h["$"+k]||a(d[k]),k=void 0):h.delegatedEvents||((h.elementsWithBoundEvents=h.elementsWithBoundEvents||[]).push(l=l.find(k)),k=void 0),l[f?"one":"on"](j,k,function(){("function"==typeof b?b:h[b]).apply(h,arguments)})})}return this},addDismissListener:function(b,c){var d=this;if(!b)throw new Error("Dismiss listener name not speficied");return c=a.extend({$el:this.$el},c),this.$document=this.$document||a(document),this.ens=this.ens||"."+this.cid,this.dismissListeners=this.dismissListeners||{},this.dismissListeners[b]||(this.dismissListeners[b]=function(e){27!==e.keyCode&&(a(e.target).is(c.$el)||a.contains(c.$el.get(0),e.target))||d[b].call(d)},this.$document.on("click"+this.ens+" keyup"+this.ens,this.dismissListeners[b])),this},removeDismissListener:function(a){if(!a)throw new Error("Name of dismiss listener to remove not specified");return this.dismissListeners&&this.dismissListeners[a]&&(this.$document.off("click keyup",this.dismissListeners[a]),delete this.dismissListeners[a]),this},removeEvents:function(){var a=this.ens;return a&&(this.$el&&this.$el.off(a),this.$document&&this.$document.off(a),this.$window&&this.$window.off(a),this.elementsWithBoundEvents&&(c.each(this.elementsWithBoundEvents,function(b){b.off(a)}),delete this.elementsWithBoundEvents),delete this.dismissListeners),this},addView:function(a,b){return this.views=this.views||{},this.views[a.cid]=a,a.model&&(this.viewsWithModel=this.viewsWithModel||{},this.viewsWithModel[a.model.cid]=a),b&&(this.viewsGroups=this.viewsGroups||{},this.viewsGroups[b]=this.viewsGroups[b]||{},this.viewsGroups[b][a.cid]=a),this.listenToOnce(a,"afterRemove detachView",function(){delete this.views[a.cid],a.model&&this.viewsWithModel&&delete this.viewsWithModel[a.model.cid],b&&this.viewsGroups&&this.viewsGroups[b]&&delete this.viewsGroups[b][a.cid]}),a},getGroupViews:function(a){return this.viewsGroups&&this.viewsGroups[a]?c.values(this.viewsGroups[a]):[]},hasView:function(a){return this.views&&Boolean(this.views[a.cid])},detachView:function(){return this.trigger("detachView"),this},attachToView:function(a,b){return this.detachView(),a.addView(this,b),this},removeViews:function(a){return a?this.viewsGroups&&this.viewsGroups[a]&&(c.invoke(this.viewsGroups[a],"remove"),delete this.viewsGroups[a]):(this.views&&c.invoke(this.views,"remove"),delete this.views,delete this.viewsWithModel,delete this.viewsGroups),this},getViewByModel:function(a){return this.viewsWithModel&&this.viewsWithModel[a.cid]},removeViewByModel:function(a){return this.viewsWithModel&&this.viewsWithModel[a.cid]&&this.viewsWithModel[a.cid].remove(),this},addDeferred:function(a){return this.deferreds=this.deferreds||[],c.indexOf(this.deferreds,a)<0&&this.deferreds.push(a),a},abortDeferreds:function(){return this.deferreds&&c.each(this.deferreds,function(a){"object"==typeof a&&a.state&&"pending"===a.state()&&(a.abort?a.abort():a.reject())}),delete this.deferreds,this},when:function(b,d,e){c.each(b=c.isArray(b)?b:[b],function(a){this.addDeferred(a)},this);var f=a.when.apply(a,b);return d&&f.done(c.bind(d,this)),e&&f.fail(c.bind(e,this)),f},remove:function(){return this.trigger("beforeRemove"),this.removeEvents().abortDeferreds().removeViews(),b.View.prototype.remove.call(this),this.trigger("afterRemove"),this},setElement:function(a){return this._setElement(a),this}});return c.extend(f.prototype,{undelegateEvents:f.prototype.removeEvents,delegateEvents:f.prototype.setupEvents}),c.each(["appendTo","prependTo","insertBefore","insertAfter"],function(a){f.prototype[a]=function(b){return this.$el[a](b instanceof f?b.$el:b),this}}),f}); | ||
!function(e,i){"function"==typeof define&&define.amd?define(["jquery","backbone","underscore"],i):"object"==typeof module&&module.exports?module.exports=i(require("jquery"),require("backbone"),require("underscore")):e.BaseView=i(e.jQuery,e.Backbone,e._)}(this,function(e,i,t){var s=/{{\s*(\S+)\s*}}/g,n={window:window,document:window.document},o=function(e,i){return e.replace(s,function(e,t){var s=0===t.indexOf("this."),n=s?i:window,o=(s?t.slice(5):t).split(".");for(var r in o)if(void 0===(n=n[o[r]]))throw new Error("Undefined variable in event string");return n})},r=i.View.extend({constructor:function(e){this.assignOptions&&(this.writeOptions.apply(this,arguments),this.optionRules&&this.validateOptions(this.options,this.optionRules)),i.View.apply(this,arguments),this.events&&this.setupEvents()},delegatedEvents:!0,parseEventVariables:!0,assignOptions:!1,writeOptions:function(i){var s=t.result(this,"defaults"),n={};return this.optionRules&&t.each(this.optionRules,function(e,i){n[i]=e.default}),"deep"===this.assignOptions?this.options=e.extend(!0,{},s,n,i):this.options=e.extend({},s,n,i),this},validateOptions:function(e,i){var s=[];if(t.each(i,function(i,t){var n=e[t],o=typeof n;!1===i.required&&"undefined"===o||(i.type&&o!==i.type&&s.push('Option "'+t+'" is '+o+", expected "+i.type+"."),i.rule&&!i.rule(n)&&s.push('Option "'+t+'" breaks defined rule.'),!i.instanceOf||n instanceof i.instanceOf||s.push('Option "'+t+'" is not instance of defined constructor.'))}),s.length)throw new Error(s.join(" "));return this},setupEvents:function(i){var s=i||this.events,r="function"==typeof s?s.call(this):s,h=this;if(r){var d=this.ens=this.ens||"."+this.cid;t.each(r,function(i,t){h.parseEventVariables&&(t=o(t,h));var s=0===t.indexOf("one:"),r=(s?t.slice(4):t).split(" "),u=r[0]+d,c=r.slice(1).join(" "),f=h.$el;n[c]?(f=h["$"+c]=h["$"+c]||e(n[c]),c=void 0):h.delegatedEvents||((h.elementsWithBoundEvents=h.elementsWithBoundEvents||[]).push(f=f.find(c)),c=void 0),f[s?"one":"on"](u,c,function(){("function"==typeof i?i:h[i]).apply(h,arguments)})})}return this},addDismissListener:function(i,t){var s=this;if(!i)throw new Error("Dismiss listener name not speficied");return t=e.extend({$el:this.$el},t),this.$document=this.$document||e(document),this.ens=this.ens||"."+this.cid,this.dismissListeners=this.dismissListeners||{},this.dismissListeners[i]||(this.dismissListeners[i]=function(n){27!==n.keyCode&&(e(n.target).is(t.$el)||e.contains(t.$el.get(0),n.target))||s[i].call(s)},this.$document.on("click"+this.ens+" keyup"+this.ens,this.dismissListeners[i])),this},removeDismissListener:function(e){if(!e)throw new Error("Name of dismiss listener to remove not specified");return this.dismissListeners&&this.dismissListeners[e]&&(this.$document.off("click keyup",this.dismissListeners[e]),delete this.dismissListeners[e]),this},removeEvents:function(){var e=this.ens;return e&&(this.$el&&this.$el.off(e),this.$document&&this.$document.off(e),this.$window&&this.$window.off(e),this.elementsWithBoundEvents&&(t.each(this.elementsWithBoundEvents,function(i){i.off(e)}),delete this.elementsWithBoundEvents),delete this.dismissListeners),this},addView:function(e,i){return this.views=this.views||{},this.views[e.cid]=e,e.model&&(this.viewsWithModel=this.viewsWithModel||{},this.viewsWithModel[e.model.cid]=e),i&&(this.viewsGroups=this.viewsGroups||{},this.viewsGroups[i]=this.viewsGroups[i]||{},this.viewsGroups[i][e.cid]=e),this.listenToOnce(e,"afterRemove detachView",function(){delete this.views[e.cid],e.model&&this.viewsWithModel&&delete this.viewsWithModel[e.model.cid],i&&this.viewsGroups&&this.viewsGroups[i]&&delete this.viewsGroups[i][e.cid]}),e},getGroupViews:function(e){return this.viewsGroups&&this.viewsGroups[e]?t.values(this.viewsGroups[e]):[]},hasView:function(e){return this.views&&Boolean(this.views[e.cid])},detachView:function(){return this.trigger("detachView"),this},attachToView:function(e,i){return this.detachView(),e.addView(this,i),this},removeViews:function(e){return e?this.viewsGroups&&this.viewsGroups[e]&&(t.invoke(this.viewsGroups[e],"remove"),delete this.viewsGroups[e]):(this.views&&t.invoke(this.views,"remove"),delete this.views,delete this.viewsWithModel,delete this.viewsGroups),this},getViewByModel:function(e){return this.viewsWithModel&&this.viewsWithModel[e.cid]},removeViewByModel:function(e){return this.viewsWithModel&&this.viewsWithModel[e.cid]&&this.viewsWithModel[e.cid].remove(),this},addDeferred:function(e){return this.deferreds=this.deferreds||[],t.indexOf(this.deferreds,e)<0&&this.deferreds.push(e),e},abortDeferreds:function(){return this.deferreds&&t.each(this.deferreds,function(e){"object"==typeof e&&e.state&&"pending"===e.state()&&(e.abort?e.abort():e.reject())}),delete this.deferreds,this},when:function(i,s,n){t.each(i=t.isArray(i)?i:[i],function(e){this.addDeferred(e)},this);var o=e.when.apply(e,i);return s&&o.done(t.bind(s,this)),n&&o.fail(t.bind(n,this)),o},remove:function(){return this.trigger("beforeRemove"),this.removeEvents().abortDeferreds().removeViews(),i.View.prototype.remove.call(this),this.trigger("afterRemove"),this},setElement:function(e){return this._setElement(e),this}});return t.extend(r.prototype,{undelegateEvents:r.prototype.removeEvents,delegateEvents:r.prototype.setupEvents}),t.each(["appendTo","prependTo","insertBefore","insertAfter"],function(e){r.prototype[e]=function(i){return this.$el[e](i instanceof r?i.$el:i),this}}),r}); |
{ | ||
"name": "backbone-base-view", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "Baseview is a extended backbone view with convenient methods for manipulating subviews and events.", | ||
@@ -29,3 +29,3 @@ "main": "src/baseView.js", | ||
"attire": "^1.3.2", | ||
"chai": "^3.5.0", | ||
"chai": "^4.1.2", | ||
"coveralls": "^2.11.15", | ||
@@ -36,18 +36,18 @@ "grunt": "^1.0.1", | ||
"grunt-contrib-copy": "^1.0.0", | ||
"grunt-contrib-uglify": "^2.0.0", | ||
"grunt-contrib-uglify": "^3.0.1", | ||
"grunt-contrib-watch": "^1.0.0", | ||
"grunt-eslint": "^19.0.0", | ||
"istanbul-instrumenter-loader": "^2.0.0", | ||
"karma": "^1.4.1", | ||
"karma-chrome-launcher": "^2.0.0", | ||
"karma-coverage-istanbul-reporter": "^0.2.3", | ||
"grunt-eslint": "^20.1.0", | ||
"istanbul-instrumenter-loader": "^3.0.0", | ||
"karma": "^1.7.1", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-coverage-istanbul-reporter": "^1.3.0", | ||
"karma-mocha": "^1.3.0", | ||
"karma-phantomjs-launcher": "^1.0.2", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
"karma-spec-reporter": "0.0.26", | ||
"karma-webpack": "^2.0.2", | ||
"karma-spec-reporter": "0.0.31", | ||
"karma-webpack": "^2.0.4", | ||
"load-grunt-tasks": "^3.5.2", | ||
"mocha": "^3.2.0", | ||
"mocha": "^3.5.1", | ||
"mocha-loader": "^1.1.0", | ||
"webpack": "^2.2.1" | ||
"webpack": "^3.5.6" | ||
}, | ||
@@ -54,0 +54,0 @@ "dependencies": { |
@@ -16,2 +16,15 @@ # Backbone base view | ||
### events | ||
Define one time events, inject variables and add window and document listeners. | ||
```js | ||
events: { | ||
'click .selector': 'handler', | ||
'click {{this.someVariable}}': 'handler', // variable will be injected | ||
'one:submit form': 'oneSubmit', // handler will run only once | ||
'resize window': 'onWindowResize', | ||
'keyup document': 'onDocumentKeyup' | ||
} | ||
``` | ||
--- | ||
### assignOptions: false|true|'deep' | ||
@@ -31,12 +44,14 @@ If defined user passed options will be merged with defaults and written to viewInstance.options. False by default. | ||
### events | ||
Define one time events, inject variables and add window and document listeners. | ||
### Options type checking and validation | ||
Options provided by type defaults and and constructor parameters can be type checked. | ||
```js | ||
events: { | ||
'click .selector': 'handler', | ||
'click {{this.someVariable}}': 'handler', // variable will be injected | ||
'one:submit form': 'oneSubmit', // handler will run only once | ||
'resize window': 'onWindowResize', | ||
'keyup document': 'onDocumentKeyup' | ||
} | ||
var MusicianView = BaseView.extend({ | ||
optionRules: { | ||
instrument: {type: 'string'}, | ||
age: {type: 'number', default: 18, rule: function(age) { | ||
return age >= 18; | ||
}}, | ||
mentor: {instanceOf: BaseView} | ||
} | ||
}); | ||
``` | ||
@@ -43,0 +58,0 @@ --- |
@@ -14,3 +14,3 @@ (function(root, factory) { | ||
var variableInEventStringRE = /{{(\S+)}}/g; | ||
var variableInEventStringRE = /{{\s*(\S+)\s*}}/g; | ||
var specialSelectors = { | ||
@@ -46,4 +46,4 @@ 'window': window, | ||
if (this.assignOptions) { | ||
var defaults = _.result(this, 'defaults'); | ||
this.options = this.assignOptions === 'deep' ? $.extend(true, {}, defaults, options) : _.extend({}, defaults, options); | ||
this.writeOptions.apply(this, arguments); | ||
this.optionRules && this.validateOptions(this.options, this.optionRules); | ||
} | ||
@@ -60,2 +60,56 @@ | ||
writeOptions: function(options) { | ||
var defaults = _.result(this, 'defaults'); | ||
var ruleDefaults = {}; | ||
this.optionRules && _.each(this.optionRules, function(data, optionName) { | ||
ruleDefaults[optionName] = data.default; | ||
}); | ||
if (this.assignOptions === 'deep') { | ||
this.options = $.extend(true, {}, defaults, ruleDefaults, options); | ||
} else { | ||
this.options = $.extend({}, defaults, ruleDefaults, options); | ||
} | ||
return this; | ||
}, | ||
validateOptions: function(options, rules) { | ||
var errors = []; | ||
_.each(rules, function(data, optionName) { | ||
var optionValue = options[optionName]; | ||
var optionValueType = typeof optionValue; | ||
if (data.required !== false || optionValueType !== 'undefined') { | ||
if (data.type && optionValueType !== data.type) { | ||
errors.push('Option "' + optionName +'" is ' + optionValueType + ', expected ' + data.type + '.'); | ||
} | ||
if (data.rule && !data.rule(optionValue)) { | ||
errors.push('Option "' + optionName +'" breaks defined rule.'); | ||
} | ||
if (data.instanceOf && !(optionValue instanceof data.instanceOf)) { | ||
errors.push('Option "' + optionName +'" is not instance of defined constructor.'); | ||
} | ||
} | ||
}); | ||
if (errors.length) { | ||
throw new Error(errors.join(' ')); | ||
} else { | ||
return this; | ||
} | ||
}, | ||
setupEvents: function(eventsMap) { | ||
@@ -62,0 +116,0 @@ |
@@ -70,2 +70,107 @@ var assert = require('chai').assert; | ||
describe('BaseView option rules', function() { | ||
it('type check provided options', function() { | ||
var Guitarist = BaseView.extend({ | ||
assignOptions: true, | ||
optionRules: { | ||
name: {type: 'string'}, | ||
instrument: {type: 'string', required: false}, | ||
} | ||
}); | ||
assert.throws(function() { | ||
new Guitarist(); | ||
}, 'Option "name" is undefined, expected string.'); | ||
assert.throws(function() { | ||
new Guitarist({ | ||
name: 'George', | ||
instrument: 42 | ||
}); | ||
}, 'Option "instrument" is number, expected string.'); | ||
}); | ||
it('validate options with rule callback', function() { | ||
var Guitarist = BaseView.extend({ | ||
assignOptions: true, | ||
optionRules: { | ||
age: {type: 'number', rule: function(age) { | ||
return age > 18; | ||
}} | ||
} | ||
}); | ||
var GuitarPro = Guitarist.extend(); | ||
assert.throws(function() { | ||
new Guitarist({ | ||
age: 'teen' | ||
}); | ||
}, 'Option "age" is string, expected number. Option "age" breaks defined rule.'); | ||
assert.throws(function() { | ||
new GuitarPro({ | ||
age: 15 | ||
}); | ||
}, 'Option "age" breaks defined rule.'); | ||
}); | ||
it('do option instance of checks', function() { | ||
var Person = function() {}; | ||
var Guitarist = BaseView.extend({ | ||
assignOptions: true, | ||
optionRules: { | ||
mentor: {instanceOf: Person} | ||
} | ||
}); | ||
assert.throws(function() { | ||
new Guitarist({ | ||
mentor: 'pero' | ||
}); | ||
}, 'Option "mentor" is not instance of defined constructor.'); | ||
assert.throws(function() { | ||
new Guitarist({ | ||
mentor: new Date() | ||
}); | ||
}, 'Option "mentor" is not instance of defined constructor.'); | ||
assert.throws(function() { | ||
new Guitarist(); | ||
}, 'Option "mentor" is not instance of defined constructor.'); | ||
}); | ||
it('merge default values', function() { | ||
var Guitarist = BaseView.extend({ | ||
assignOptions: true, | ||
defaults: { | ||
instrument: 'guitar' | ||
}, | ||
optionRules: { | ||
name: {type: 'string', default: ''}, | ||
instrument: {type: 'string'}, | ||
age: {type: 'number', default: 18} | ||
} | ||
}); | ||
assert.deepEqual(new Guitarist().options, { | ||
name: '', | ||
instrument: 'guitar', | ||
age: 18 | ||
}); | ||
}); | ||
}); | ||
describe('BaseView events', function() { | ||
@@ -72,0 +177,0 @@ |
Sorry, the diff of this file is not supported yet
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
88182
1239
194
0