Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Correct radio change events and optimize CollectionBinder::getManagerForModel #133

Merged
merged 4 commits into from

2 participants

@katowulf
Collaborator

Fixes for #131 and #132. All test cases passing (see issues for samples and details)

katowulf added some commits
@katowulf katowulf Trigger change events for radio and checkbox items. Currently, no .on…
…('change'...) is fired if the model causes an update. This is necessary for widgets and listeners acting directly on the checkbox/radio items.

The change triggers must be deferred, however, or the old model value is actually thrown out to test units (and presumably public listeners).
5432d50
@katowulf katowulf Fixes theironcook/Backbone.ModelBinder#131
Optimize CollectionBinder::getManagerForModel to use keyed values already stored internally.

defaultBindings was generating a js error; this did not cause any test cases to fail because it occurred after test cases completed. However, since the checkboxes return boolean values, there is no .replace value available; corrected.
129be43
@katowulf katowulf Fixes theironcook/Backbone.ModelBinder#132 - fire checkbox and radio …
…change events when updated from model. Fix tested in all current browser versions (IE 9) and all test cases pass in those browsers.
36476d5
@katowulf katowulf Minified versions of ModelBinder and CollectionBinder for fixes their… ff53fcb
@katowulf
Collaborator

I committed this locally then realized it wasn't working in all browsers during testing. The next commit reverts and corrects this change.

@theironcook theironcook merged commit 21b44e9 into theironcook:master
@katowulf

value.replace doesn't work with booleans; probably never did; it didn't fire until after the test unit was resolved so it never got caught

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 25, 2013
  1. @katowulf

    Trigger change events for radio and checkbox items. Currently, no .on…

    katowulf authored
    …('change'...) is fired if the model causes an update. This is necessary for widgets and listeners acting directly on the checkbox/radio items.
    
    The change triggers must be deferred, however, or the old model value is actually thrown out to test units (and presumably public listeners).
Commits on Apr 28, 2013
  1. @katowulf

    Fixes theironcook/Backbone.ModelBinder#131

    katowulf authored
    Optimize CollectionBinder::getManagerForModel to use keyed values already stored internally.
    
    defaultBindings was generating a js error; this did not cause any test cases to fail because it occurred after test cases completed. However, since the checkboxes return boolean values, there is no .replace value available; corrected.
  2. @katowulf

    Fixes theironcook/Backbone.ModelBinder#132 - fire checkbox and radio …

    katowulf authored
    …change events when updated from model. Fix tested in all current browser versions (IE 9) and all test cases pass in those browsers.
  3. @katowulf
This page is out of date. Refresh to see the latest.
View
12 Backbone.CollectionBinder.js
@@ -71,17 +71,7 @@
},
getManagerForModel: function(model){
- var i, elManager, elManagers = _.values(this._elManagers);
-
- for(i = 0; i < elManagers.length; i++){
- elManager = elManagers[i];
-
- if(elManager.getModel() === model){
- return elManager;
- }
- }
-
- return undefined;
+ return this._elManagers[_.isObject(model)? model.cid : model];
},
_onCollectionAdd: function(model){
View
2  Backbone.CollectionBinder.min.js
@@ -2,4 +2,4 @@
// (c) 2013 Bart Wood
// Distributed Under MIT License
-(function(){if(!Backbone){throw"Please include Backbone.js before Backbone.ModelBinder.js"}if(!Backbone.ModelBinder){throw"Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js"}Backbone.CollectionBinder=function(e,t){_.bindAll(this);this._elManagers={};this._elManagerFactory=e;if(!this._elManagerFactory)throw"elManagerFactory must be defined.";this._elManagerFactory.trigger=this.trigger;this._options=t||{}};Backbone.CollectionBinder.VERSION="1.0.1";_.extend(Backbone.CollectionBinder.prototype,Backbone.Events,{bind:function(e,t){this.unbind();if(!e)throw"collection must be defined";if(!t)throw"parentEl must be defined";this._collection=e;this._elManagerFactory.setParentEl(t);this._onCollectionReset();this._collection.on("add",this._onCollectionAdd,this);this._collection.on("remove",this._onCollectionRemove,this);this._collection.on("reset",this._onCollectionReset,this);this._collection.on("sort",this._onCollectionSort,this)},unbind:function(){if(this._collection!==undefined){this._collection.off("add",this._onCollectionAdd);this._collection.off("remove",this._onCollectionRemove);this._collection.off("reset",this._onCollectionReset);this._collection.off("sort",this._onCollectionSort)}this._removeAllElManagers()},getManagerForEl:function(e){var t,n,r=_.values(this._elManagers);for(t=0;t<r.length;t++){n=r[t];if(n.isElContained(e)){return n}}return undefined},getManagerForModel:function(e){var t,n,r=_.values(this._elManagers);for(t=0;t<r.length;t++){n=r[t];if(n.getModel()===e){return n}}return undefined},_onCollectionAdd:function(e){this._elManagers[e.cid]=this._elManagerFactory.makeElManager(e);this._elManagers[e.cid].createEl();if(this._options["autoSort"]){this.sortRootEls()}},_onCollectionRemove:function(e){this._removeElManager(e)},_onCollectionReset:function(){this._removeAllElManagers();this._collection.each(function(e){this._onCollectionAdd(e)},this);this.trigger("elsReset",this._collection)},_onCollectionSort:function(){if(this._options["autoSort"]){this.sortRootEls()}},_removeAllElManagers:function(){_.each(this._elManagers,function(e){e.removeEl();delete this._elManagers[e._model.cid]},this);delete this._elManagers;this._elManagers={}},_removeElManager:function(e){if(this._elManagers[e.cid]!==undefined){this._elManagers[e.cid].removeEl();delete this._elManagers[e.cid]}},sortRootEls:function(){this._collection.each(function(e,t){var n=this.getManagerForModel(e);if(n){var r=n.getEl();var i=$(this._elManagerFactory.getParentEl()).children();if(i[t]!==r[0]){r.detach();r.insertBefore(i[t])}}},this)}});Backbone.CollectionBinder.ElManagerFactory=function(e,t){_.bindAll(this);this._elHtml=e;this._bindings=t;if(!_.isString(this._elHtml))throw"elHtml must be a valid html string"};_.extend(Backbone.CollectionBinder.ElManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._el=$(this._elHtml);$(this._parentEl).append(this._el);if(this._bindings){if(_.isString(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,Backbone.ModelBinder.createDefaultBindings(this._el,this._bindings))}else if(_.isObject(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,this._bindings)}else{throw"Unsupported bindings type, please use a boolean or a bindings hash"}}this.trigger("elCreated",this._model,this._el)},removeEl:function(){if(this._modelBinder!==undefined){this._modelBinder.unbind()}this._el.remove();this.trigger("elRemoved",this._model,this._el)},isElContained:function(e){return this._el===e||$(this._el).has(e).length>0},getModel:function(){return this._model},getEl:function(){return this._el}};_.extend(t,this);return t}});Backbone.CollectionBinder.ViewManagerFactory=function(e){_.bindAll(this);this._viewCreator=e;if(!_.isFunction(this._viewCreator))throw"viewCreator must be a valid function that accepts a model and returns a backbone view"};_.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._view=this._viewCreator(e);$(this._parentEl).append(this._view.render(this._model).el);this.trigger("elCreated",this._model,this._view)},removeEl:function(){if(this._view.close!==undefined){this._view.close()}else{this._view.$el.remove();console.log("warning, you should implement a close() function for your view, you might end up with zombies")}this.trigger("elRemoved",this._model,this._view)},isElContained:function(e){return this._view.el===e||this._view.$el.has(e).length>0},getModel:function(){return this._model},getView:function(){return this._view},getEl:function(){return this._view.$el}};_.extend(t,this);return t}})}).call(this)
+(function(){if(!Backbone){throw"Please include Backbone.js before Backbone.ModelBinder.js"}if(!Backbone.ModelBinder){throw"Please include Backbone.ModelBinder.js before Backbone.CollectionBinder.js"}Backbone.CollectionBinder=function(e,t){_.bindAll(this);this._elManagers={};this._elManagerFactory=e;if(!this._elManagerFactory)throw"elManagerFactory must be defined.";this._elManagerFactory.trigger=this.trigger;this._options=t||{}};Backbone.CollectionBinder.VERSION="1.0.1";_.extend(Backbone.CollectionBinder.prototype,Backbone.Events,{bind:function(e,t){this.unbind();if(!e)throw"collection must be defined";if(!t)throw"parentEl must be defined";this._collection=e;this._elManagerFactory.setParentEl(t);this._onCollectionReset();this._collection.on("add",this._onCollectionAdd,this);this._collection.on("remove",this._onCollectionRemove,this);this._collection.on("reset",this._onCollectionReset,this);this._collection.on("sort",this._onCollectionSort,this)},unbind:function(){if(this._collection!==undefined){this._collection.off("add",this._onCollectionAdd);this._collection.off("remove",this._onCollectionRemove);this._collection.off("reset",this._onCollectionReset);this._collection.off("sort",this._onCollectionSort)}this._removeAllElManagers()},getManagerForEl:function(e){var t,n,r=_.values(this._elManagers);for(t=0;t<r.length;t++){n=r[t];if(n.isElContained(e)){return n}}return undefined},getManagerForModel:function(e){return this._elManagers[_.isObject(e)?e.cid:e]},_onCollectionAdd:function(e){this._elManagers[e.cid]=this._elManagerFactory.makeElManager(e);this._elManagers[e.cid].createEl();if(this._options["autoSort"]){this.sortRootEls()}},_onCollectionRemove:function(e){this._removeElManager(e)},_onCollectionReset:function(){this._removeAllElManagers();this._collection.each(function(e){this._onCollectionAdd(e)},this);this.trigger("elsReset",this._collection)},_onCollectionSort:function(){if(this._options["autoSort"]){this.sortRootEls()}},_removeAllElManagers:function(){_.each(this._elManagers,function(e){e.removeEl();delete this._elManagers[e._model.cid]},this);delete this._elManagers;this._elManagers={}},_removeElManager:function(e){if(this._elManagers[e.cid]!==undefined){this._elManagers[e.cid].removeEl();delete this._elManagers[e.cid]}},sortRootEls:function(){this._collection.each(function(e,t){var n=this.getManagerForModel(e);if(n){var r=n.getEl();var i=$(this._elManagerFactory.getParentEl()).children();if(i[t]!==r[0]){r.detach();r.insertBefore(i[t])}}},this)}});Backbone.CollectionBinder.ElManagerFactory=function(e,t){_.bindAll(this);this._elHtml=e;this._bindings=t;if(!_.isString(this._elHtml))throw"elHtml must be a valid html string"};_.extend(Backbone.CollectionBinder.ElManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._el=$(this._elHtml);$(this._parentEl).append(this._el);if(this._bindings){if(_.isString(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,Backbone.ModelBinder.createDefaultBindings(this._el,this._bindings))}else if(_.isObject(this._bindings)){this._modelBinder=new Backbone.ModelBinder;this._modelBinder.bind(this._model,this._el,this._bindings)}else{throw"Unsupported bindings type, please use a boolean or a bindings hash"}}this.trigger("elCreated",this._model,this._el)},removeEl:function(){if(this._modelBinder!==undefined){this._modelBinder.unbind()}this._el.remove();this.trigger("elRemoved",this._model,this._el)},isElContained:function(e){return this._el===e||$(this._el).has(e).length>0},getModel:function(){return this._model},getEl:function(){return this._el}};_.extend(t,this);return t}});Backbone.CollectionBinder.ViewManagerFactory=function(e){_.bindAll(this);this._viewCreator=e;if(!_.isFunction(this._viewCreator))throw"viewCreator must be a valid function that accepts a model and returns a backbone view"};_.extend(Backbone.CollectionBinder.ViewManagerFactory.prototype,{setParentEl:function(e){this._parentEl=e},getParentEl:function(){return this._parentEl},makeElManager:function(e){var t={_model:e,createEl:function(){this._view=this._viewCreator(e);$(this._parentEl).append(this._view.render(this._model).el);this.trigger("elCreated",this._model,this._view)},removeEl:function(){if(this._view.close!==undefined){this._view.close()}else{this._view.$el.remove();console.log("warning, you should implement a close() function for your view, you might end up with zombies")}this.trigger("elRemoved",this._model,this._view)},isElContained:function(e){return this._view.el===e||this._view.$el.has(e).length>0},getModel:function(){return this._model},getView:function(){return this._view},getEl:function(){return this._view.$el}};_.extend(t,this);return t}})}).call(this)
View
12 Backbone.ModelBinder.js
@@ -407,19 +407,19 @@
switch (el.attr('type')) {
case 'radio':
if (el.val() === convertedValue) {
+ // must defer the change trigger or the change will actually fire with the old value
+ el.prop('checked') || _.defer(function() { el.trigger('change'); });
el.prop('checked', true);
}
else {
+ // must defer the change trigger or the change will actually fire with the old value
el.prop('checked', false);
}
break;
case 'checkbox':
- if (convertedValue) {
- el.prop('checked', true);
- }
- else {
- el.prop('checked', false);
- }
+ // must defer the change trigger or the change will actually fire with the old value
+ el.prop('checked') === !!convertedValue || _.defer(function() { el.trigger('change') });
+ el.prop('checked', !!convertedValue);
break;
case 'file':
break;
View
2  Backbone.ModelBinder.min.js
@@ -1,4 +1,4 @@
// Backbone.ModelBinder v1.0.2
// (c) 2013 Bart Wood
// Distributed Under MIT License
-(function(e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","backbone"],e)}else{e(_,$,Backbone)}})(function(e,t,n){if(!n){throw"Please include Backbone.js before Backbone.ModelBinder.js"}n.ModelBinder=function(){e.bindAll(this)};n.ModelBinder.SetOptions=function(e){n.ModelBinder.options=e};n.ModelBinder.VERSION="1.0.2";n.ModelBinder.Constants={};n.ModelBinder.Constants.ModelToView="ModelToView";n.ModelBinder.Constants.ViewToModel="ViewToModel";e.extend(n.ModelBinder.prototype,{bind:function(e,n,r,i){this.unbind();this._model=e;this._rootEl=n;this._setOptions(i);if(!this._model)this._throwException("model must be specified");if(!this._rootEl)this._throwException("rootEl must be specified");if(r){this._attributeBindings=t.extend(true,{},r);this._initializeAttributeBindings();this._initializeElBindings()}else{this._initializeDefaultBindings()}this._bindModelToView();this._bindViewToModel()},bindCustomTriggers:function(e,t,n,r,i){this._triggers=n;this.bind(e,t,r,i)},unbind:function(){this._unbindModelToView();this._unbindViewToModel();if(this._attributeBindings){delete this._attributeBindings;this._attributeBindings=undefined}},_setOptions:function(t){this._options=e.extend({boundAttribute:"name"},n.ModelBinder.options,t);if(!this._options["modelSetOptions"]){this._options["modelSetOptions"]={}}this._options["modelSetOptions"].changeSource="ModelBinder";if(!this._options["changeTriggers"]){this._options["changeTriggers"]={"*":"change","[contenteditable]":"blur"}}if(!this._options["initialCopyDirection"]){this._options["initialCopyDirection"]=n.ModelBinder.Constants.ModelToView}},_initializeAttributeBindings:function(){var t,n,r,i,s;for(t in this._attributeBindings){n=this._attributeBindings[t];if(e.isString(n)){r={elementBindings:[{selector:n}]}}else if(e.isArray(n)){r={elementBindings:n}}else if(e.isObject(n)){r={elementBindings:[n]}}else{this._throwException("Unsupported type passed to Model Binder "+r)}for(i=0;i<r.elementBindings.length;i++){s=r.elementBindings[i];s.attributeBinding=r}r.attributeName=t;this._attributeBindings[t]=r}},_initializeDefaultBindings:function(){var e,n,r,i,s;this._attributeBindings={};n=t("["+this._options["boundAttribute"]+"]",this._rootEl);for(e=0;e<n.length;e++){r=n[e];i=t(r).attr(this._options["boundAttribute"]);if(!this._attributeBindings[i]){s={attributeName:i};s.elementBindings=[{attributeBinding:s,boundEls:[r]}];this._attributeBindings[i]=s}else{this._attributeBindings[i].elementBindings.push({attributeBinding:this._attributeBindings[i],boundEls:[r]})}}},_initializeElBindings:function(){var e,n,r,i,s,o,u;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(i.selector===""){s=t(this._rootEl)}else{s=t(i.selector,this._rootEl)}if(s.length===0){this._throwException("Bad binding found. No elements returned for binding selector "+i.selector)}else{i.boundEls=[];for(o=0;o<s.length;o++){u=s[o];i.boundEls.push(u)}}}}},_bindModelToView:function(){this._model.on("change",this._onModelChange,this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ModelToView){this.copyModelAttributesToView()}},copyModelAttributesToView:function(t){var n,r;for(n in this._attributeBindings){if(t===undefined||e.indexOf(t,n)!==-1){r=this._attributeBindings[n];this._copyModelToView(r)}}},copyViewValuesToModel:function(){var e,n,r,i,s,o;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(this._isBindingUserEditable(i)){if(this._isBindingRadioGroup(i)){o=this._getRadioButtonGroupCheckedEl(i);if(o){this._copyViewToModel(i,o)}}else{for(s=0;s<i.boundEls.length;s++){o=t(i.boundEls[s]);if(this._isElUserEditable(o)){this._copyViewToModel(i,o)}}}}}}},_unbindModelToView:function(){if(this._model){this._model.off("change",this._onModelChange);this._model=undefined}},_bindViewToModel:function(){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).delegate(n,e,this._onElChanged)},this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ViewToModel){this.copyViewValuesToModel()}},_unbindViewToModel:function(){if(this._options&&this._options["changeTriggers"]){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).undelegate(n,e,this._onElChanged)},this)}},_onElChanged:function(e){var n,r,i,s;n=t(e.target)[0];r=this._getElBindings(n);for(i=0;i<r.length;i++){s=r[i];if(this._isBindingUserEditable(s)){this._copyViewToModel(s,n)}}},_isBindingUserEditable:function(e){return e.elAttribute===undefined||e.elAttribute==="text"||e.elAttribute==="html"},_isElUserEditable:function(e){var t=e.attr("contenteditable");return t||e.is("input")||e.is("select")||e.is("textarea")},_isBindingRadioGroup:function(e){var n,r;var i=e.boundEls.length>0;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")!=="radio"){i=false;break}}return i},_getRadioButtonGroupCheckedEl:function(e){var n,r;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")==="radio"&&r.attr("checked")){return r}}return undefined},_getElBindings:function(e){var t,n,r,i,s,o;var u=[];for(t in this._attributeBindings){n=this._attributeBindings[t];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(o===e){u.push(i)}}}}return u},_onModelChange:function(){var e,t;for(e in this._model.changedAttributes()){t=this._attributeBindings[e];if(t){this._copyModelToView(t)}}},_copyModelToView:function(e){var r,i,s,o,u,a;u=this._model.get(e.attributeName);for(r=0;r<e.elementBindings.length;r++){i=e.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(!o._isSetting){a=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,i,u);this._setEl(t(o),i,a)}}}},_setEl:function(e,t,n){if(t.elAttribute){this._setElAttribute(e,t,n)}else{this._setElValue(e,n)}},_setElAttribute:function(t,r,i){switch(r.elAttribute){case"html":t.html(i);break;case"text":t.text(i);break;case"enabled":t.prop("disabled",!i);break;case"displayed":t[i?"show":"hide"]();break;case"hidden":t[i?"hide":"show"]();break;case"css":t.css(r.cssAttribute,i);break;case"class":var s=this._model.previous(r.attributeBinding.attributeName);var o=this._model.get(r.attributeBinding.attributeName);if(!e.isUndefined(s)||!e.isUndefined(o)){s=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,r,s);t.removeClass(s)}if(i){t.addClass(i)}break;default:t.attr(r.elAttribute,i)}},_setElValue:function(e,t){if(e.attr("type")){switch(e.attr("type")){case"radio":if(e.val()===t){e.prop("checked",true)}else{e.prop("checked",false)}break;case"checkbox":if(t){e.prop("checked",true)}else{e.prop("checked",false)}break;case"file":break;default:e.val(t)}}else if(e.is("input")||e.is("select")||e.is("textarea")){e.val(t||(t===0?"0":""))}else{e.text(t||(t===0?"0":""))}},_copyViewToModel:function(e,r){var i,s,o;if(!r._isSetting){r._isSetting=true;i=this._setModel(e,t(r));r._isSetting=false;if(i&&e.converter){s=this._model.get(e.attributeBinding.attributeName);o=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,e,s);this._setEl(t(r),e,o)}}},_getElValue:function(e,t){switch(t.attr("type")){case"checkbox":return t.prop("checked")?true:false;default:if(t.attr("contenteditable")!==undefined){return t.html()}else{return t.val()}}},_setModel:function(e,t){var r={};var i=this._getElValue(e,t);i=this._getConvertedValue(n.ModelBinder.Constants.ViewToModel,e,i);r[e.attributeBinding.attributeName]=i;return this._model.set(r,this._options["modelSetOptions"])},_getConvertedValue:function(e,t,n){if(t.converter){n=t.converter(e,n,t.attributeBinding.attributeName,this._model,t.boundEls)}return n},_throwException:function(e){if(this._options.suppressThrows){if(console&&console.error){console.error(e)}}else{throw e}}});n.ModelBinder.CollectionConverter=function(t){this._collection=t;if(!this._collection){throw"Collection must be defined"}e.bindAll(this,"convert")};e.extend(n.ModelBinder.CollectionConverter.prototype,{convert:function(e,t){if(e===n.ModelBinder.Constants.ModelToView){return t?t.id:undefined}else{return this._collection.get(t)}}});n.ModelBinder.createDefaultBindings=function(e,n,r,i){var s,o,u,a;var f={};s=t("["+n+"]",e);for(o=0;o<s.length;o++){u=s[o];a=t(u).attr(n);if(!f[a]){var l={selector:"["+n+'="'+a+'"]'};f[a]=l;if(r){f[a].converter=r}if(i){f[a].elAttribute=i}}}return f};n.ModelBinder.combineBindings=function(t,n){e.each(n,function(e,n){var r={selector:e.selector};if(e.converter){r.converter=e.converter}if(e.elAttribute){r.elAttribute=e.elAttribute}if(!t[n]){t[n]=r}else{t[n]=[t[n],r]}});return t};return n.ModelBinder})
+(function(e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","backbone"],e)}else{e(_,$,Backbone)}})(function(e,t,n){if(!n){throw"Please include Backbone.js before Backbone.ModelBinder.js"}n.ModelBinder=function(){e.bindAll(this)};n.ModelBinder.SetOptions=function(e){n.ModelBinder.options=e};n.ModelBinder.VERSION="1.0.2";n.ModelBinder.Constants={};n.ModelBinder.Constants.ModelToView="ModelToView";n.ModelBinder.Constants.ViewToModel="ViewToModel";e.extend(n.ModelBinder.prototype,{bind:function(e,n,r,i){this.unbind();this._model=e;this._rootEl=n;this._setOptions(i);if(!this._model)this._throwException("model must be specified");if(!this._rootEl)this._throwException("rootEl must be specified");if(r){this._attributeBindings=t.extend(true,{},r);this._initializeAttributeBindings();this._initializeElBindings()}else{this._initializeDefaultBindings()}this._bindModelToView();this._bindViewToModel()},bindCustomTriggers:function(e,t,n,r,i){this._triggers=n;this.bind(e,t,r,i)},unbind:function(){this._unbindModelToView();this._unbindViewToModel();if(this._attributeBindings){delete this._attributeBindings;this._attributeBindings=undefined}},_setOptions:function(t){this._options=e.extend({boundAttribute:"name"},n.ModelBinder.options,t);if(!this._options["modelSetOptions"]){this._options["modelSetOptions"]={}}this._options["modelSetOptions"].changeSource="ModelBinder";if(!this._options["changeTriggers"]){this._options["changeTriggers"]={"*":"change","[contenteditable]":"blur"}}if(!this._options["initialCopyDirection"]){this._options["initialCopyDirection"]=n.ModelBinder.Constants.ModelToView}},_initializeAttributeBindings:function(){var t,n,r,i,s;for(t in this._attributeBindings){n=this._attributeBindings[t];if(e.isString(n)){r={elementBindings:[{selector:n}]}}else if(e.isArray(n)){r={elementBindings:n}}else if(e.isObject(n)){r={elementBindings:[n]}}else{this._throwException("Unsupported type passed to Model Binder "+r)}for(i=0;i<r.elementBindings.length;i++){s=r.elementBindings[i];s.attributeBinding=r}r.attributeName=t;this._attributeBindings[t]=r}},_initializeDefaultBindings:function(){var e,n,r,i,s;this._attributeBindings={};n=t("["+this._options["boundAttribute"]+"]",this._rootEl);for(e=0;e<n.length;e++){r=n[e];i=t(r).attr(this._options["boundAttribute"]);if(!this._attributeBindings[i]){s={attributeName:i};s.elementBindings=[{attributeBinding:s,boundEls:[r]}];this._attributeBindings[i]=s}else{this._attributeBindings[i].elementBindings.push({attributeBinding:this._attributeBindings[i],boundEls:[r]})}}},_initializeElBindings:function(){var e,n,r,i,s,o,u;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(i.selector===""){s=t(this._rootEl)}else{s=t(i.selector,this._rootEl)}if(s.length===0){this._throwException("Bad binding found. No elements returned for binding selector "+i.selector)}else{i.boundEls=[];for(o=0;o<s.length;o++){u=s[o];i.boundEls.push(u)}}}}},_bindModelToView:function(){this._model.on("change",this._onModelChange,this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ModelToView){this.copyModelAttributesToView()}},copyModelAttributesToView:function(t){var n,r;for(n in this._attributeBindings){if(t===undefined||e.indexOf(t,n)!==-1){r=this._attributeBindings[n];this._copyModelToView(r)}}},copyViewValuesToModel:function(){var e,n,r,i,s,o;for(e in this._attributeBindings){n=this._attributeBindings[e];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];if(this._isBindingUserEditable(i)){if(this._isBindingRadioGroup(i)){o=this._getRadioButtonGroupCheckedEl(i);if(o){this._copyViewToModel(i,o)}}else{for(s=0;s<i.boundEls.length;s++){o=t(i.boundEls[s]);if(this._isElUserEditable(o)){this._copyViewToModel(i,o)}}}}}}},_unbindModelToView:function(){if(this._model){this._model.off("change",this._onModelChange);this._model=undefined}},_bindViewToModel:function(){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).delegate(n,e,this._onElChanged)},this);if(this._options["initialCopyDirection"]===n.ModelBinder.Constants.ViewToModel){this.copyViewValuesToModel()}},_unbindViewToModel:function(){if(this._options&&this._options["changeTriggers"]){e.each(this._options["changeTriggers"],function(e,n){t(this._rootEl).undelegate(n,e,this._onElChanged)},this)}},_onElChanged:function(e){var n,r,i,s;n=t(e.target)[0];r=this._getElBindings(n);for(i=0;i<r.length;i++){s=r[i];if(this._isBindingUserEditable(s)){this._copyViewToModel(s,n)}}},_isBindingUserEditable:function(e){return e.elAttribute===undefined||e.elAttribute==="text"||e.elAttribute==="html"},_isElUserEditable:function(e){var t=e.attr("contenteditable");return t||e.is("input")||e.is("select")||e.is("textarea")},_isBindingRadioGroup:function(e){var n,r;var i=e.boundEls.length>0;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")!=="radio"){i=false;break}}return i},_getRadioButtonGroupCheckedEl:function(e){var n,r;for(n=0;n<e.boundEls.length;n++){r=t(e.boundEls[n]);if(r.attr("type")==="radio"&&r.attr("checked")){return r}}return undefined},_getElBindings:function(e){var t,n,r,i,s,o;var u=[];for(t in this._attributeBindings){n=this._attributeBindings[t];for(r=0;r<n.elementBindings.length;r++){i=n.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(o===e){u.push(i)}}}}return u},_onModelChange:function(){var e,t;for(e in this._model.changedAttributes()){t=this._attributeBindings[e];if(t){this._copyModelToView(t)}}},_copyModelToView:function(e){var r,i,s,o,u,a;u=this._model.get(e.attributeName);for(r=0;r<e.elementBindings.length;r++){i=e.elementBindings[r];for(s=0;s<i.boundEls.length;s++){o=i.boundEls[s];if(!o._isSetting){a=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,i,u);this._setEl(t(o),i,a)}}}},_setEl:function(e,t,n){if(t.elAttribute){this._setElAttribute(e,t,n)}else{this._setElValue(e,n)}},_setElAttribute:function(t,r,i){switch(r.elAttribute){case"html":t.html(i);break;case"text":t.text(i);break;case"enabled":t.prop("disabled",!i);break;case"displayed":t[i?"show":"hide"]();break;case"hidden":t[i?"hide":"show"]();break;case"css":t.css(r.cssAttribute,i);break;case"class":var s=this._model.previous(r.attributeBinding.attributeName);var o=this._model.get(r.attributeBinding.attributeName);if(!e.isUndefined(s)||!e.isUndefined(o)){s=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,r,s);t.removeClass(s)}if(i){t.addClass(i)}break;default:t.attr(r.elAttribute,i)}},_setElValue:function(t,n){if(t.attr("type")){switch(t.attr("type")){case"radio":if(t.val()===n){t.prop("checked")||e.defer(function(){t.trigger("change")});t.prop("checked",true)}else{t.prop("checked",false)}break;case"checkbox":t.prop("checked")===!!n||e.defer(function(){t.trigger("change")});t.prop("checked",!!n);break;case"file":break;default:t.val(n)}}else if(t.is("input")||t.is("select")||t.is("textarea")){t.val(n||(n===0?"0":""))}else{t.text(n||(n===0?"0":""))}},_copyViewToModel:function(e,r){var i,s,o;if(!r._isSetting){r._isSetting=true;i=this._setModel(e,t(r));r._isSetting=false;if(i&&e.converter){s=this._model.get(e.attributeBinding.attributeName);o=this._getConvertedValue(n.ModelBinder.Constants.ModelToView,e,s);this._setEl(t(r),e,o)}}},_getElValue:function(e,t){switch(t.attr("type")){case"checkbox":return t.prop("checked")?true:false;default:if(t.attr("contenteditable")!==undefined){return t.html()}else{return t.val()}}},_setModel:function(e,t){var r={};var i=this._getElValue(e,t);i=this._getConvertedValue(n.ModelBinder.Constants.ViewToModel,e,i);r[e.attributeBinding.attributeName]=i;return this._model.set(r,this._options["modelSetOptions"])},_getConvertedValue:function(e,t,n){if(t.converter){n=t.converter(e,n,t.attributeBinding.attributeName,this._model,t.boundEls)}return n},_throwException:function(e){if(this._options.suppressThrows){if(console&&console.error){console.error(e)}}else{throw e}}});n.ModelBinder.CollectionConverter=function(t){this._collection=t;if(!this._collection){throw"Collection must be defined"}e.bindAll(this,"convert")};e.extend(n.ModelBinder.CollectionConverter.prototype,{convert:function(e,t){if(e===n.ModelBinder.Constants.ModelToView){return t?t.id:undefined}else{return this._collection.get(t)}}});n.ModelBinder.createDefaultBindings=function(e,n,r,i){var s,o,u,a;var f={};s=t("["+n+"]",e);for(o=0;o<s.length;o++){u=s[o];a=t(u).attr(n);if(!f[a]){var l={selector:"["+n+'="'+a+'"]'};f[a]=l;if(r){f[a].converter=r}if(i){f[a].elAttribute=i}}}return f};n.ModelBinder.combineBindings=function(t,n){e.each(n,function(e,n){var r={selector:e.selector};if(e.converter){r.converter=e.converter}if(e.elAttribute){r.elAttribute=e.elAttribute}if(!t[n]){t[n]=r}else{t[n]=[t[n],r]}});return t};return n.ModelBinder})
View
6 spec/javascripts/defaultBindings.spec.js
@@ -74,12 +74,12 @@ describe('default bindings', function(){
});
it('converter bindings', function(){
- var defaultConverter = function(direction, value){
+ var defaultConverter = function(direction, value, key){
if(direction === Backbone.ModelBinder.Constants.ModelToView){
- return 'XXX' + value;
+ return key === 'isActive'? value : 'XXX' + value;
}
else {
- return value.replace('XXX');
+ return _.isString(value)? value.replace('XXX') : value;
}
};
Something went wrong with that request. Please try again.