2edbd420 by Michael Richards

Merge pull request #25 from wearefractal/master

Support for events + build script
2 parents 3a172296 b1a1aea0
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) {
......
...@@ -11,5 +11,8 @@ ...@@ -11,5 +11,8 @@
11 "repository" : { 11 "repository" : {
12 "type" : "git", 12 "type" : "git",
13 "url" : "https://github.com/mikeric/rivets.git" 13 "url" : "https://github.com/mikeric/rivets.git"
14 },
15 "scripts" : {
16 "build" : "coffee -o lib -c src"
14 } 17 }
15 } 18 }
......
...@@ -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) ->
......