crude support for events
Showing
2 changed files
with
96 additions
and
25 deletions
1 | // Generated by CoffeeScript 1.3.3 | 1 | // Generated by CoffeeScript 1.3.3 |
2 | (function() { | 2 | (function() { |
3 | var Rivets, attributeBinding, bidirectionals, getInputValue, rivets, | 3 | var Rivets, attributeBinding, bidirectionals, bindEvent, eventBinding, getInputValue, rivets, unbindEvent, |
4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | 4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, |
5 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | 5 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; |
6 | 6 | ||
... | @@ -14,9 +14,10 @@ | ... | @@ -14,9 +14,10 @@ |
14 | 14 | ||
15 | Rivets.Binding = (function() { | 15 | Rivets.Binding = (function() { |
16 | 16 | ||
17 | function Binding(el, type, model, keypath, formatters) { | 17 | function Binding(el, type, bindType, model, keypath, formatters) { |
18 | this.el = el; | 18 | this.el = el; |
19 | this.type = type; | 19 | this.type = type; |
20 | this.bindType = bindType; | ||
20 | this.model = model; | 21 | this.model = model; |
21 | this.keypath = keypath; | 22 | this.keypath = keypath; |
22 | this.formatters = formatters != null ? formatters : []; | 23 | this.formatters = formatters != null ? formatters : []; |
... | @@ -26,7 +27,11 @@ | ... | @@ -26,7 +27,11 @@ |
26 | 27 | ||
27 | this.set = __bind(this.set, this); | 28 | this.set = __bind(this.set, this); |
28 | 29 | ||
29 | this.routine = Rivets.routines[this.type] || attributeBinding(this.type); | 30 | if (this.bindType === "event") { |
31 | this.routine = eventBinding(this.type); | ||
32 | } else { | ||
33 | this.routine = Rivets.routines[this.type] || attributeBinding(this.type); | ||
34 | } | ||
30 | } | 35 | } |
31 | 36 | ||
32 | Binding.prototype.set = function(value) { | 37 | Binding.prototype.set = function(value) { |
... | @@ -36,21 +41,21 @@ | ... | @@ -36,21 +41,21 @@ |
36 | formatter = _ref[_i]; | 41 | formatter = _ref[_i]; |
37 | value = Rivets.config.formatters[formatter](value); | 42 | value = Rivets.config.formatters[formatter](value); |
38 | } | 43 | } |
39 | return this.routine(this.el, value); | 44 | if (this.bindType === "event") { |
45 | this.routine(this.el, value, this.currentListener); | ||
46 | return this.currentListener = value; | ||
47 | } else { | ||
48 | return this.routine(this.el, value); | ||
49 | } | ||
40 | }; | 50 | }; |
41 | 51 | ||
42 | Binding.prototype.bind = function() { | 52 | Binding.prototype.bind = function() { |
43 | var _ref; | ||
44 | Rivets.config.adapter.subscribe(this.model, this.keypath, this.set); | 53 | Rivets.config.adapter.subscribe(this.model, this.keypath, this.set); |
45 | if (Rivets.config.preloadData) { | 54 | if (Rivets.config.preloadData) { |
46 | this.set(Rivets.config.adapter.read(this.model, this.keypath)); | 55 | this.set(Rivets.config.adapter.read(this.model, this.keypath)); |
47 | } | 56 | } |
48 | if (_ref = this.type, __indexOf.call(bidirectionals, _ref) >= 0) { | 57 | if (this.bindType === "bidirectional") { |
49 | if (window.addEventListener) { | 58 | return bindEvent(this.el, 'change', this.publish); |
50 | return this.el.addEventListener('change', this.publish); | ||
51 | } else { | ||
52 | return this.el.attachEvent('change', this.publish); | ||
53 | } | ||
54 | } | 59 | } |
55 | }; | 60 | }; |
56 | 61 | ||
... | @@ -92,9 +97,10 @@ | ... | @@ -92,9 +97,10 @@ |
92 | }; | 97 | }; |
93 | 98 | ||
94 | View.prototype.build = function() { | 99 | View.prototype.build = function() { |
95 | var attribute, bindingRegExp, keypath, model, node, path, pipe, pipes, type, _i, _len, _ref, _results; | 100 | var attribute, bindType, bindingRegExp, eventRegExp, keypath, model, node, path, pipe, pipes, type, _i, _len, _ref, _results; |
96 | this.bindings = []; | 101 | this.bindings = []; |
97 | bindingRegExp = this.bindingRegExp(); | 102 | bindingRegExp = this.bindingRegExp(); |
103 | eventRegExp = /^event-/; | ||
98 | _ref = this.el.getElementsByTagName('*'); | 104 | _ref = this.el.getElementsByTagName('*'); |
99 | _results = []; | 105 | _results = []; |
100 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { | 106 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { |
... | @@ -106,6 +112,7 @@ | ... | @@ -106,6 +112,7 @@ |
106 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { | 112 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { |
107 | attribute = _ref1[_j]; | 113 | attribute = _ref1[_j]; |
108 | if (bindingRegExp.test(attribute.name)) { | 114 | if (bindingRegExp.test(attribute.name)) { |
115 | bindType = "attribute"; | ||
109 | type = attribute.name.replace(bindingRegExp, ''); | 116 | type = attribute.name.replace(bindingRegExp, ''); |
110 | pipes = (function() { | 117 | pipes = (function() { |
111 | var _k, _len2, _ref2, _results2; | 118 | var _k, _len2, _ref2, _results2; |
... | @@ -120,7 +127,13 @@ | ... | @@ -120,7 +127,13 @@ |
120 | path = pipes.shift().split('.'); | 127 | path = pipes.shift().split('.'); |
121 | model = this.models[path.shift()]; | 128 | model = this.models[path.shift()]; |
122 | keypath = path.join('.'); | 129 | keypath = path.join('.'); |
123 | _results1.push(this.bindings.push(new Rivets.Binding(node, type, model, keypath, pipes))); | 130 | if (eventRegExp.test(type)) { |
131 | type = type.replace(eventRegExp, ''); | ||
132 | bindType = "event"; | ||
133 | } else if (__indexOf.call(bidirectionals, type) >= 0) { | ||
134 | bindType = "bidirectional"; | ||
135 | } | ||
136 | _results1.push(this.bindings.push(new Rivets.Binding(node, type, bindType, model, keypath, pipes))); | ||
124 | } else { | 137 | } else { |
125 | _results1.push(void 0); | 138 | _results1.push(void 0); |
126 | } | 139 | } |
... | @@ -146,6 +159,22 @@ | ... | @@ -146,6 +159,22 @@ |
146 | 159 | ||
147 | })(); | 160 | })(); |
148 | 161 | ||
162 | bindEvent = function(el, event, fn) { | ||
163 | if (window.addEventListener) { | ||
164 | return el.addEventListener(event, fn); | ||
165 | } else { | ||
166 | return el.attachEvent(event, fn); | ||
167 | } | ||
168 | }; | ||
169 | |||
170 | unbindEvent = function(el, event, fn) { | ||
171 | if (window.removeEventListener) { | ||
172 | return el.removeEventListener(event, fn); | ||
173 | } else { | ||
174 | return el.detachEvent(event, fn); | ||
175 | } | ||
176 | }; | ||
177 | |||
149 | getInputValue = function(el) { | 178 | getInputValue = function(el) { |
150 | switch (el.type) { | 179 | switch (el.type) { |
151 | case 'text': | 180 | case 'text': |
... | @@ -159,6 +188,17 @@ | ... | @@ -159,6 +188,17 @@ |
159 | } | 188 | } |
160 | }; | 189 | }; |
161 | 190 | ||
191 | eventBinding = function(event) { | ||
192 | return function(el, bind, unbind) { | ||
193 | if (bind) { | ||
194 | bindEvent(el, event, bind); | ||
195 | } | ||
196 | if (unbind) { | ||
197 | return unbindEvent(el, event, unbind); | ||
198 | } | ||
199 | }; | ||
200 | }; | ||
201 | |||
162 | attributeBinding = function(attr) { | 202 | attributeBinding = function(attr) { |
163 | return function(el, value) { | 203 | return function(el, value) { |
164 | if (value) { | 204 | if (value) { | ... | ... |
... | @@ -14,8 +14,11 @@ class Rivets.Binding | ... | @@ -14,8 +14,11 @@ class Rivets.Binding |
14 | # All information about the binding is passed into the constructor; the DOM | 14 | # All information about the binding is passed into the constructor; the DOM |
15 | # element, the routine identifier, the model object and the keypath at which | 15 | # element, the routine identifier, the model object and the keypath at which |
16 | # to listen for changes. | 16 | # to listen for changes. |
17 | constructor: (@el, @type, @model, @keypath, @formatters = []) -> | 17 | constructor: (@el, @type, @bindType, @model, @keypath, @formatters = []) -> |
18 | @routine = Rivets.routines[@type] || attributeBinding @type | 18 | if @bindType is "event" |
19 | @routine = eventBinding @type | ||
20 | else | ||
21 | @routine = Rivets.routines[@type] || attributeBinding @type | ||
19 | 22 | ||
20 | # Sets the value for the binding. This Basically just runs the binding routine | 23 | # Sets the value for the binding. This Basically just runs the binding routine |
21 | # with the suplied value and applies any formatters. | 24 | # with the suplied value and applies any formatters. |
... | @@ -23,7 +26,11 @@ class Rivets.Binding | ... | @@ -23,7 +26,11 @@ class Rivets.Binding |
23 | for formatter in @formatters | 26 | for formatter in @formatters |
24 | value = Rivets.config.formatters[formatter] value | 27 | value = Rivets.config.formatters[formatter] value |
25 | 28 | ||
26 | @routine @el, value | 29 | if @bindType is "event" |
30 | @routine @el, value, @currentListener | ||
31 | @currentListener = value | ||
32 | else | ||
33 | @routine @el, value | ||
27 | 34 | ||
28 | # Subscribes to the model for changes at the specified keypath. Bi-directional | 35 | # Subscribes to the model for changes at the specified keypath. Bi-directional |
29 | # routines will also listen for changes on the element to propagate them back | 36 | # routines will also listen for changes on the element to propagate them back |
... | @@ -34,13 +41,8 @@ class Rivets.Binding | ... | @@ -34,13 +41,8 @@ class Rivets.Binding |
34 | if Rivets.config.preloadData | 41 | if Rivets.config.preloadData |
35 | @set Rivets.config.adapter.read @model, @keypath | 42 | @set Rivets.config.adapter.read @model, @keypath |
36 | 43 | ||
37 | if @type in bidirectionals | 44 | if @bindType is "bidirectional" |
38 | # Check to see if addEventListener is available. | 45 | bindEvent @el, 'change', @publish |
39 | if window.addEventListener | ||
40 | @el.addEventListener 'change', @publish | ||
41 | else | ||
42 | # Assume we are in IE and use attachEvent. | ||
43 | @el.attachEvent 'change', @publish | ||
44 | 46 | ||
45 | # Publishes the value currently set on the input element back to the model. | 47 | # Publishes the value currently set on the input element back to the model. |
46 | publish: (e) => | 48 | publish: (e) => |
... | @@ -64,28 +66,57 @@ class Rivets.View | ... | @@ -64,28 +66,57 @@ class Rivets.View |
64 | build: => | 66 | build: => |
65 | @bindings = [] | 67 | @bindings = [] |
66 | bindingRegExp = @bindingRegExp() | 68 | bindingRegExp = @bindingRegExp() |
67 | 69 | eventRegExp = /^event-/ | |
68 | for node in @el.getElementsByTagName '*' | 70 | for node in @el.getElementsByTagName '*' |
69 | for attribute in node.attributes | 71 | for attribute in node.attributes |
70 | if bindingRegExp.test attribute.name | 72 | if bindingRegExp.test attribute.name |
73 | bindType = "attribute" | ||
71 | type = attribute.name.replace bindingRegExp, '' | 74 | type = attribute.name.replace bindingRegExp, '' |
72 | pipes = (pipe.trim() for pipe in attribute.value.split '|') | 75 | pipes = (pipe.trim() for pipe in attribute.value.split '|') |
73 | path = pipes.shift().split '.' | 76 | path = pipes.shift().split '.' |
74 | model = @models[path.shift()] | 77 | model = @models[path.shift()] |
75 | keypath = path.join '.' | 78 | keypath = path.join '.' |
76 | 79 | ||
77 | @bindings.push new Rivets.Binding node, type, model, keypath, pipes | 80 | if eventRegExp.test type |
81 | type = type.replace eventRegExp, '' | ||
82 | bindType = "event" | ||
83 | else if type in bidirectionals | ||
84 | bindType = "bidirectional" | ||
85 | |||
86 | @bindings.push new Rivets.Binding node, type, bindType, model, keypath, pipes | ||
78 | 87 | ||
79 | # Binds all of the current bindings for this view. | 88 | # Binds all of the current bindings for this view. |
80 | bind: => | 89 | bind: => |
81 | binding.bind() for binding in @bindings | 90 | binding.bind() for binding in @bindings |
82 | 91 | ||
92 | # Cross-browser event binding | ||
93 | bindEvent = (el, event, fn) -> | ||
94 | # Check to see if addEventListener is available. | ||
95 | if window.addEventListener | ||
96 | el.addEventListener event, fn | ||
97 | else | ||
98 | # Assume we are in IE and use attachEvent. | ||
99 | el.attachEvent event, fn | ||
100 | |||
101 | unbindEvent = (el, event, fn) -> | ||
102 | # Check to see if addEventListener is available. | ||
103 | if window.removeEventListener | ||
104 | el.removeEventListener event, fn | ||
105 | else | ||
106 | # Assume we are in IE and use attachEvent. | ||
107 | el.detachEvent event, fn | ||
108 | |||
83 | # Returns the current input value for the specified element. | 109 | # Returns the current input value for the specified element. |
84 | getInputValue = (el) -> | 110 | getInputValue = (el) -> |
85 | switch el.type | 111 | switch el.type |
86 | when 'text', 'textarea', 'password', 'select-one' then el.value | 112 | when 'text', 'textarea', 'password', 'select-one' then el.value |
87 | when 'checkbox', 'radio' then el.checked | 113 | when 'checkbox', 'radio' then el.checked |
88 | 114 | ||
115 | # Returns an element binding routine for the specified attribute. | ||
116 | eventBinding = (event) -> (el, bind, unbind) -> | ||
117 | bindEvent el, event, bind if bind | ||
118 | unbindEvent el, event, unbind if unbind | ||
119 | |||
89 | # Returns an attribute binding routine for the specified attribute. This is what | 120 | # Returns an attribute binding routine for the specified attribute. This is what |
90 | # `registerBinding` falls back to when there is no routine for the binding type. | 121 | # `registerBinding` falls back to when there is no routine for the binding type. |
91 | attributeBinding = (attr) -> (el, value) -> | 122 | attributeBinding = (attr) -> (el, value) -> | ... | ... |
-
Please register or sign in to post a comment