Merge pull request #28 from mikeric/unbinding-listeners
Ability to unbind the listeners applied from a Rivets.Binding.
Showing
4 changed files
with
41 additions
and
4 deletions
1 | # Rivets.js | 1 | # Rivets.js |
2 | 2 | ||
3 | Rivets.js is a declarative data binding facility that plays well with existing frameworks such as [Backbone.js](http://backbonejs.org), [Spine.js](http://spinejs.com) and [Stapes.js](http://hay.github.com/stapes/). It aims to be lightweight (1.2KB minified and gzipped), extensible, and configurable to work with any event-driven model. | 3 | Rivets.js is a declarative data binding facility that plays well with existing frameworks such as [Backbone.js](http://backbonejs.org), [Spine.js](http://spinejs.com) and [Stapes.js](http://hay.github.com/stapes/). It aims to be lightweight (1.4KB minified and gzipped), extensible, and configurable to work with any event-driven model. |
4 | 4 | ||
5 | --- | 5 | --- |
6 | 6 | ||
... | @@ -43,8 +43,12 @@ Rivets.js is model interface-agnostic, meaning it can work with any event-driven | ... | @@ -43,8 +43,12 @@ Rivets.js is model interface-agnostic, meaning it can work with any event-driven |
43 | rivets.configure({ | 43 | rivets.configure({ |
44 | adapter: { | 44 | adapter: { |
45 | subscribe: function(obj, keypath, callback) { | 45 | subscribe: function(obj, keypath, callback) { |
46 | obj.on('change:' + keypath, function(m, v) { callback(v) }); | 46 | callback.wrapped = function(m, v) { callback(v) }; |
47 | obj.on('change:' + keypath, callback.wrapped); | ||
47 | }, | 48 | }, |
49 | unsubscribe: function(obj, keypath, callback) { | ||
50 | obj.off('change:' + keypath, callback.wrapped); | ||
51 | } | ||
48 | read: function(obj, keypath) { | 52 | read: function(obj, keypath) { |
49 | return obj.get(keypath); | 53 | return obj.get(keypath); |
50 | }, | 54 | }, | ... | ... |
... | @@ -20,6 +20,8 @@ | ... | @@ -20,6 +20,8 @@ |
20 | this.model = model; | 20 | this.model = model; |
21 | this.keypath = keypath; | 21 | this.keypath = keypath; |
22 | this.formatters = formatters != null ? formatters : []; | 22 | this.formatters = formatters != null ? formatters : []; |
23 | this.unbind = __bind(this.unbind, this); | ||
24 | |||
23 | this.publish = __bind(this.publish, this); | 25 | this.publish = __bind(this.publish, this); |
24 | 26 | ||
25 | this.bind = __bind(this.bind, this); | 27 | this.bind = __bind(this.bind, this); |
... | @@ -64,6 +66,13 @@ | ... | @@ -64,6 +66,13 @@ |
64 | return Rivets.config.adapter.publish(this.model, this.keypath, getInputValue(el)); | 66 | return Rivets.config.adapter.publish(this.model, this.keypath, getInputValue(el)); |
65 | }; | 67 | }; |
66 | 68 | ||
69 | Binding.prototype.unbind = function() { | ||
70 | Rivets.config.adapter.unsubscribe(this.model, this.keypath, this.set); | ||
71 | if (this.bindType === "bidirectional") { | ||
72 | return this.el.removeEventListener('change', this.publish); | ||
73 | } | ||
74 | }; | ||
75 | |||
67 | return Binding; | 76 | return Binding; |
68 | 77 | ||
69 | })(); | 78 | })(); |
... | @@ -73,6 +82,8 @@ | ... | @@ -73,6 +82,8 @@ |
73 | function View(el, models) { | 82 | function View(el, models) { |
74 | this.el = el; | 83 | this.el = el; |
75 | this.models = models; | 84 | this.models = models; |
85 | this.unbind = __bind(this.unbind, this); | ||
86 | |||
76 | this.bind = __bind(this.bind, this); | 87 | this.bind = __bind(this.bind, this); |
77 | 88 | ||
78 | this.build = __bind(this.build, this); | 89 | this.build = __bind(this.build, this); |
... | @@ -154,6 +165,17 @@ | ... | @@ -154,6 +165,17 @@ |
154 | return _results; | 165 | return _results; |
155 | }; | 166 | }; |
156 | 167 | ||
168 | View.prototype.unbind = function() { | ||
169 | var binding, _i, _len, _ref, _results; | ||
170 | _ref = this.bindings; | ||
171 | _results = []; | ||
172 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { | ||
173 | binding = _ref[_i]; | ||
174 | _results.push(binding.unbind()); | ||
175 | } | ||
176 | return _results; | ||
177 | }; | ||
178 | |||
157 | return View; | 179 | return View; |
158 | 180 | ||
159 | })(); | 181 | })(); | ... | ... |
1 | (function(){var a,b,c,d,e,f,g,h,i=function(a,b){return function(){return a.apply(b,arguments)}},j=[].indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(b in this&&this[b]===a)return b;return-1};a={},String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),a.Binding=function(){function c(c,d,f,g,h,j){this.el=c,this.type=d,this.bindType=f,this.model=g,this.keypath=h,this.formatters=j!=null?j:[],this.publish=i(this.publish,this),this.bind=i(this.bind,this),this.set=i(this.set,this),this.bindType==="event"?this.routine=e(this.type):this.routine=a.routines[this.type]||b(this.type)}return c.prototype.set=function(b){var c,d,e,f;f=this.formatters;for(d=0,e=f.length;d<e;d++)c=f[d],b=a.config.formatters[c](b);return this.bindType==="event"?(this.routine(this.el,b,this.currentListener),this.currentListener=b):this.routine(this.el,b)},c.prototype.bind=function(){a.config.adapter.subscribe(this.model,this.keypath,this.set),a.config.preloadData&&this.set(a.config.adapter.read(this.model,this.keypath));if(this.bindType==="bidirectional")return d(this.el,"change",this.publish)},c.prototype.publish=function(b){var c;return c=b.target||b.srcElement,a.config.adapter.publish(this.model,this.keypath,f(c))},c}(),a.View=function(){function b(a,b){this.el=a,this.models=b,this.bind=i(this.bind,this),this.build=i(this.build,this),this.bindingRegExp=i(this.bindingRegExp,this),this.el.jquery&&(this.el=this.el.get(0)),this.build()}return b.prototype.bindingRegExp=function(){var b;return b=a.config.prefix,b?new RegExp("^data-"+b+"-"):/^data-/},b.prototype.build=function(){var b,d,e,f,g,h,i,k,l,m,n,o,p,q,r;this.bindings=[],e=this.bindingRegExp(),f=/^on-/,q=this.el.getElementsByTagName("*"),r=[];for(o=0,p=q.length;o<p;o++)i=q[o],r.push(function(){var o,p,q,r;q=i.attributes,r=[];for(o=0,p=q.length;o<p;o++)b=q[o],e.test(b.name)?(d="attribute",n=b.name.replace(e,""),m=function(){var a,c,d,e;d=b.value.split("|"),e=[];for(a=0,c=d.length;a<c;a++)l=d[a],e.push(l.trim());return e}(),k=m.shift().split("."),h=this.models[k.shift()],g=k.join("."),f.test(n)?(n=n.replace(f,""),d="event"):j.call(c,n)>=0&&(d="bidirectional"),r.push(this.bindings.push(new a.Binding(i,n,d,h,g,m)))):r.push(void 0);return r}.call(this));return r},b.prototype.bind=function(){var a,b,c,d,e;d=this.bindings,e=[];for(b=0,c=d.length;b<c;b++)a=d[b],e.push(a.bind());return e},b}(),d=function(a,b,c){return window.addEventListener?a.addEventListener(b,c):a.attachEvent(b,c)},h=function(a,b,c){return window.removeEventListener?a.removeEventListener(b,c):a.detachEvent(b,c)},f=function(a){switch(a.type){case"text":case"textarea":case"password":case"select-one":return a.value;case"checkbox":case"radio":return a.checked}},e=function(a){return function(b,c,e){c&&d(b,a,c);if(e)return h(b,a,e)}},b=function(a){return function(b,c){return c?b.setAttribute(a,c):b.removeAttribute(a)}},c=["value","checked","unchecked","selected","unselected"],a.routines={enabled:function(a,b){return a.disabled=!b},disabled:function(a,b){return a.disabled=!!b},checked:function(a,b){return a.checked=!!b},unchecked:function(a,b){return a.checked=!b},selected:function(a,b){return a.selected=!!b},unselected:function(a,b){return a.selected=!b},show:function(a,b){return a.style.display=b?"":"none"},hide:function(a,b){return a.style.display=b?"none":""},html:function(a,b){return a.innerHTML=b||""},value:function(a,b){return a.value=b},text:function(a,b){return a.innerText!=null?a.innerText=b||"":a.textContent=b||""}},a.config={preloadData:!0},g={configure:function(b){var c,d,e;b==null&&(b={}),e=[];for(c in b)d=b[c],e.push(a.config[c]=d);return e},register:function(b,c){return a.routines[b]=c},bind:function(b,c){var d;return c==null&&(c={}),d=new a.View(b,c),d.bind(),d}},typeof module!="undefined"&&module!==null?module.exports=g:this.rivets=g}).call(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | (function(){var a,b,c,d,e,f,g,h,i=function(a,b){return function(){return a.apply(b,arguments)}},j=[].indexOf||function(a){for(var b=0,c=this.length;b<c;b++)if(b in this&&this[b]===a)return b;return-1};a={},String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),a.Binding=function(){function c(c,d,f,g,h,j){this.el=c,this.type=d,this.bindType=f,this.model=g,this.keypath=h,this.formatters=j!=null?j:[],this.unbind=i(this.unbind,this),this.publish=i(this.publish,this),this.bind=i(this.bind,this),this.set=i(this.set,this),this.bindType==="event"?this.routine=e(this.type):this.routine=a.routines[this.type]||b(this.type)}return c.prototype.set=function(b){var c,d,e,f;f=this.formatters;for(d=0,e=f.length;d<e;d++)c=f[d],b=a.config.formatters[c](b);return this.bindType==="event"?(this.routine(this.el,b,this.currentListener),this.currentListener=b):this.routine(this.el,b)},c.prototype.bind=function(){a.config.adapter.subscribe(this.model,this.keypath,this.set),a.config.preloadData&&this.set(a.config.adapter.read(this.model,this.keypath));if(this.bindType==="bidirectional")return d(this.el,"change",this.publish)},c.prototype.publish=function(b){var c;return c=b.target||b.srcElement,a.config.adapter.publish(this.model,this.keypath,f(c))},c.prototype.unbind=function(){a.config.adapter.unsubscribe(this.model,this.keypath,this.set);if(this.bindType==="bidirectional")return this.el.removeEventListener("change",this.publish)},c}(),a.View=function(){function b(a,b){this.el=a,this.models=b,this.unbind=i(this.unbind,this),this.bind=i(this.bind,this),this.build=i(this.build,this),this.bindingRegExp=i(this.bindingRegExp,this),this.el.jquery&&(this.el=this.el.get(0)),this.build()}return b.prototype.bindingRegExp=function(){var b;return b=a.config.prefix,b?new RegExp("^data-"+b+"-"):/^data-/},b.prototype.build=function(){var b,d,e,f,g,h,i,k,l,m,n,o,p,q,r;this.bindings=[],e=this.bindingRegExp(),f=/^on-/,q=this.el.getElementsByTagName("*"),r=[];for(o=0,p=q.length;o<p;o++)i=q[o],r.push(function(){var o,p,q,r;q=i.attributes,r=[];for(o=0,p=q.length;o<p;o++)b=q[o],e.test(b.name)?(d="attribute",n=b.name.replace(e,""),m=function(){var a,c,d,e;d=b.value.split("|"),e=[];for(a=0,c=d.length;a<c;a++)l=d[a],e.push(l.trim());return e}(),k=m.shift().split("."),h=this.models[k.shift()],g=k.join("."),f.test(n)?(n=n.replace(f,""),d="event"):j.call(c,n)>=0&&(d="bidirectional"),r.push(this.bindings.push(new a.Binding(i,n,d,h,g,m)))):r.push(void 0);return r}.call(this));return r},b.prototype.bind=function(){var a,b,c,d,e;d=this.bindings,e=[];for(b=0,c=d.length;b<c;b++)a=d[b],e.push(a.bind());return e},b.prototype.unbind=function(){var a,b,c,d,e;d=this.bindings,e=[];for(b=0,c=d.length;b<c;b++)a=d[b],e.push(a.unbind());return e},b}(),d=function(a,b,c){return window.addEventListener?a.addEventListener(b,c):a.attachEvent(b,c)},h=function(a,b,c){return window.removeEventListener?a.removeEventListener(b,c):a.detachEvent(b,c)},f=function(a){switch(a.type){case"text":case"textarea":case"password":case"select-one":return a.value;case"checkbox":case"radio":return a.checked}},e=function(a){return function(b,c,e){c&&d(b,a,c);if(e)return h(b,a,e)}},b=function(a){return function(b,c){return c?b.setAttribute(a,c):b.removeAttribute(a)}},c=["value","checked","unchecked","selected","unselected"],a.routines={enabled:function(a,b){return a.disabled=!b},disabled:function(a,b){return a.disabled=!!b},checked:function(a,b){return a.checked=!!b},unchecked:function(a,b){return a.checked=!b},selected:function(a,b){return a.selected=!!b},unselected:function(a,b){return a.selected=!b},show:function(a,b){return a.style.display=b?"":"none"},hide:function(a,b){return a.style.display=b?"none":""},html:function(a,b){return a.innerHTML=b||""},value:function(a,b){return a.value=b},text:function(a,b){return a.innerText!=null?a.innerText=b||"":a.textContent=b||""}},a.config={preloadData:!0},g={configure:function(b){var c,d,e;b==null&&(b={}),e=[];for(c in b)d=b[c],e.push(a.config[c]=d);return e},register:function(b,c){return a.routines[b]=c},bind:function(b,c){var d;return c==null&&(c={}),d=new a.View(b,c),d.bind(),d}},typeof module!="undefined"&&module!==null?module.exports=g:this.rivets=g}).call(this); | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -49,6 +49,13 @@ class Rivets.Binding | ... | @@ -49,6 +49,13 @@ class Rivets.Binding |
49 | el = e.target or e.srcElement | 49 | el = e.target or e.srcElement |
50 | Rivets.config.adapter.publish @model, @keypath, getInputValue el | 50 | Rivets.config.adapter.publish @model, @keypath, getInputValue el |
51 | 51 | ||
52 | # Unsubscribes from the model and the element. | ||
53 | unbind: => | ||
54 | Rivets.config.adapter.unsubscribe @model, @keypath, @set | ||
55 | |||
56 | if @bindType is "bidirectional" | ||
57 | @el.removeEventListener 'change', @publish | ||
58 | |||
52 | # A collection of bindings for a parent element. | 59 | # A collection of bindings for a parent element. |
53 | class Rivets.View | 60 | class Rivets.View |
54 | # The parent DOM element and the model objects for binding are passed into the | 61 | # The parent DOM element and the model objects for binding are passed into the |
... | @@ -89,6 +96,10 @@ class Rivets.View | ... | @@ -89,6 +96,10 @@ class Rivets.View |
89 | bind: => | 96 | bind: => |
90 | binding.bind() for binding in @bindings | 97 | binding.bind() for binding in @bindings |
91 | 98 | ||
99 | # Unbinds all of the current bindings for this view. | ||
100 | unbind: => | ||
101 | binding.unbind() for binding in @bindings | ||
102 | |||
92 | # Cross-browser event binding | 103 | # Cross-browser event binding |
93 | bindEvent = (el, event, fn) -> | 104 | bindEvent = (el, event, fn) -> |
94 | # Check to see if addEventListener is available. | 105 | # Check to see if addEventListener is available. |
... | @@ -118,7 +129,7 @@ eventBinding = (event) -> (el, bind, unbind) -> | ... | @@ -118,7 +129,7 @@ eventBinding = (event) -> (el, bind, unbind) -> |
118 | unbindEvent el, event, unbind if unbind | 129 | unbindEvent el, event, unbind if unbind |
119 | 130 | ||
120 | # Returns an attribute binding routine for the specified attribute. This is what | 131 | # Returns an attribute binding routine for the specified attribute. This is what |
121 | # `registerBinding` falls back to when there is no routine for the binding type. | 132 | # is used when there are no matching routines for an identifier. |
122 | attributeBinding = (attr) -> (el, value) -> | 133 | attributeBinding = (attr) -> (el, value) -> |
123 | if value then el.setAttribute attr, value else el.removeAttribute attr | 134 | if value then el.setAttribute attr, value else el.removeAttribute attr |
124 | 135 | ... | ... |
-
Please register or sign in to post a comment