Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(808)

Side by Side Diff: packages/template_binding/lib/js/node_bind.js

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
2 // This code may only be used under the BSD style license found at http://polyme r.github.io/LICENSE.txt
3 // The complete set of authors may be found at http://polymer.github.io/AUTHORS. txt
4 // The complete set of contributors may be found at http://polymer.github.io/CON TRIBUTORS.txt
5 // Code distributed by Google as part of the polymer project is also
6 // subject to an additional IP rights grant found at http://polymer.github.io/PA TENTS.txt
7
8 (function(global) {
9 'use strict';
10
11 var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
12
13 function getTreeScope(node) {
14 while (node.parentNode) {
15 node = node.parentNode;
16 }
17
18 return typeof node.getElementById === 'function' ? node : null;
19 }
20
21 Node.prototype.bind = function(name, observable) {
22 console.error('Unhandled binding to Node: ', this, name, observable);
23 };
24
25 Node.prototype.bindFinished = function() {};
26
27 function updateBindings(node, name, binding) {
28 var bindings = node.bindings_;
29 if (!bindings)
30 bindings = node.bindings_ = {};
31
32 if (bindings[name])
33 binding[name].close();
34
35 return bindings[name] = binding;
36 }
37
38 function returnBinding(node, name, binding) {
39 return binding;
40 }
41
42 function sanitizeValue(value) {
43 return value == null ? '' : value;
44 }
45
46 function updateText(node, value) {
47 node.data = sanitizeValue(value);
48 }
49
50 function textBinding(node) {
51 return function(value) {
52 return updateText(node, value);
53 };
54 }
55
56 var maybeUpdateBindings = returnBinding;
57
58 Object.defineProperty(Platform, 'enableBindingsReflection', {
59 get: function() {
60 return maybeUpdateBindings === updateBindings;
61 },
62 set: function(enable) {
63 maybeUpdateBindings = enable ? updateBindings : returnBinding;
64 return enable;
65 },
66 configurable: true
67 });
68
69 Text.prototype.bind = function(name, value, oneTime) {
70 if (name !== 'textContent')
71 return Node.prototype.bind.call(this, name, value, oneTime);
72
73 if (oneTime)
74 return updateText(this, value);
75
76 var observable = value;
77 updateText(this, observable.open(textBinding(this)));
78 return maybeUpdateBindings(this, name, observable);
79 }
80
81 function updateAttribute(el, name, conditional, value) {
82 if (conditional) {
83 if (value)
84 el.setAttribute(name, '');
85 else
86 el.removeAttribute(name);
87 return;
88 }
89
90 el.setAttribute(name, sanitizeValue(value));
91 }
92
93 function attributeBinding(el, name, conditional) {
94 return function(value) {
95 updateAttribute(el, name, conditional, value);
96 };
97 }
98
99 Element.prototype.bind = function(name, value, oneTime) {
100 var conditional = name[name.length - 1] == '?';
101 if (conditional) {
102 this.removeAttribute(name);
103 name = name.slice(0, -1);
104 }
105
106 if (oneTime)
107 return updateAttribute(this, name, conditional, value);
108
109
110 var observable = value;
111 updateAttribute(this, name, conditional,
112 observable.open(attributeBinding(this, name, conditional)));
113
114 return maybeUpdateBindings(this, name, observable);
115 };
116
117 var checkboxEventType;
118 (function() {
119 // Attempt to feature-detect which event (change or click) is fired first
120 // for checkboxes.
121 var div = document.createElement('div');
122 var checkbox = div.appendChild(document.createElement('input'));
123 checkbox.setAttribute('type', 'checkbox');
124 var first;
125 var count = 0;
126 checkbox.addEventListener('click', function(e) {
127 count++;
128 first = first || 'click';
129 });
130 checkbox.addEventListener('change', function() {
131 count++;
132 first = first || 'change';
133 });
134
135 var event = document.createEvent('MouseEvent');
136 event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
137 false, false, false, 0, null);
138 checkbox.dispatchEvent(event);
139 // WebKit/Blink don't fire the change event if the element is outside the
140 // document, so assume 'change' for that case.
141 checkboxEventType = count == 1 ? 'change' : first;
142 })();
143
144 function getEventForInputType(element) {
145 switch (element.type) {
146 case 'checkbox':
147 return checkboxEventType;
148 case 'radio':
149 case 'select-multiple':
150 case 'select-one':
151 return 'change';
152 case 'range':
153 if (/Trident|MSIE/.test(navigator.userAgent))
154 return 'change';
155 default:
156 return 'input';
157 }
158 }
159
160 function updateInput(input, property, value, santizeFn) {
161 input[property] = (santizeFn || sanitizeValue)(value);
162 }
163
164 function inputBinding(input, property, santizeFn) {
165 return function(value) {
166 return updateInput(input, property, value, santizeFn);
167 }
168 }
169
170 function noop() {}
171
172 function bindInputEvent(input, property, observable, postEventFn) {
173 var eventType = getEventForInputType(input);
174
175 function eventHandler() {
176 observable.setValue(input[property]);
177 observable.discardChanges();
178 (postEventFn || noop)(input);
179 Platform.performMicrotaskCheckpoint();
180 }
181 input.addEventListener(eventType, eventHandler);
182
183 return {
184 close: function() {
185 input.removeEventListener(eventType, eventHandler);
186 observable.close();
187 },
188
189 observable_: observable
190 }
191 }
192
193 function booleanSanitize(value) {
194 return Boolean(value);
195 }
196
197 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
198 // Returns an array containing all radio buttons other than |element| that
199 // have the same |name|, either in the form that |element| belongs to or,
200 // if no form, in the document tree to which |element| belongs.
201 //
202 // This implementation is based upon the HTML spec definition of a
203 // "radio button group":
204 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
205 //
206 function getAssociatedRadioButtons(element) {
207 if (element.form) {
208 return filter(element.form.elements, function(el) {
209 return el != element &&
210 el.tagName == 'INPUT' &&
211 el.type == 'radio' &&
212 el.name == element.name;
213 });
214 } else {
215 var treeScope = getTreeScope(element);
216 if (!treeScope)
217 return [];
218 var radios = treeScope.querySelectorAll(
219 'input[type="radio"][name="' + element.name + '"]');
220 return filter(radios, function(el) {
221 return el != element && !el.form;
222 });
223 }
224 }
225
226 function checkedPostEvent(input) {
227 // Only the radio button that is getting checked gets an event. We
228 // therefore find all the associated radio buttons and update their
229 // check binding manually.
230 if (input.tagName === 'INPUT' &&
231 input.type === 'radio') {
232 getAssociatedRadioButtons(input).forEach(function(radio) {
233 var checkedBinding = radio.bindings_.checked;
234 if (checkedBinding) {
235 // Set the value directly to avoid an infinite call stack.
236 checkedBinding.observable_.setValue(false);
237 }
238 });
239 }
240 }
241
242 HTMLInputElement.prototype.bind = function(name, value, oneTime) {
243 if (name !== 'value' && name !== 'checked')
244 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
245
246 this.removeAttribute(name);
247 var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
248 var postEventFn = name == 'checked' ? checkedPostEvent : noop;
249
250 if (oneTime)
251 return updateInput(this, name, value, sanitizeFn);
252
253
254 var observable = value;
255 var binding = bindInputEvent(this, name, observable, postEventFn);
256 updateInput(this, name,
257 observable.open(inputBinding(this, name, sanitizeFn)),
258 sanitizeFn);
259
260 // Checkboxes may need to update bindings of other checkboxes.
261 return updateBindings(this, name, binding);
262 }
263
264 HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
265 if (name !== 'value')
266 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
267
268 this.removeAttribute('value');
269
270 if (oneTime)
271 return updateInput(this, 'value', value);
272
273 var observable = value;
274 var binding = bindInputEvent(this, 'value', observable);
275 updateInput(this, 'value',
276 observable.open(inputBinding(this, 'value', sanitizeValue)));
277 return maybeUpdateBindings(this, name, binding);
278 }
279
280 function updateOption(option, value) {
281 var parentNode = option.parentNode;;
282 var select;
283 var selectBinding;
284 var oldValue;
285 if (parentNode instanceof HTMLSelectElement &&
286 parentNode.bindings_ &&
287 parentNode.bindings_.value) {
288 select = parentNode;
289 selectBinding = select.bindings_.value;
290 oldValue = select.value;
291 }
292
293 option.value = sanitizeValue(value);
294
295 if (select && select.value != oldValue) {
296 selectBinding.observable_.setValue(select.value);
297 selectBinding.observable_.discardChanges();
298 Platform.performMicrotaskCheckpoint();
299 }
300 }
301
302 function optionBinding(option) {
303 return function(value) {
304 updateOption(option, value);
305 }
306 }
307
308 HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
309 if (name !== 'value')
310 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
311
312 this.removeAttribute('value');
313
314 if (oneTime)
315 return updateOption(this, value);
316
317 var observable = value;
318 var binding = bindInputEvent(this, 'value', observable);
319 updateOption(this, observable.open(optionBinding(this)));
320 return maybeUpdateBindings(this, name, binding);
321 }
322
323 HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
324 if (name === 'selectedindex')
325 name = 'selectedIndex';
326
327 if (name !== 'selectedIndex' && name !== 'value')
328 return HTMLElement.prototype.bind.call(this, name, value, oneTime);
329
330 this.removeAttribute(name);
331
332 if (oneTime)
333 return updateInput(this, name, value);
334
335 var observable = value;
336 var binding = bindInputEvent(this, name, observable);
337 updateInput(this, name,
338 observable.open(inputBinding(this, name)));
339
340 // Option update events may need to access select bindings.
341 return updateBindings(this, name, binding);
342 }
343 })(this);
OLDNEW
« no previous file with comments | « packages/template_binding/lib/js/microtask.js ('k') | packages/template_binding/lib/js/observe.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698