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

Side by Side Diff: pkg/mdv/lib/src/input_bindings.dart

Issue 34453003: Rename mdv -> template_binding, remove experiemntal apis from dart:html (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 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 | Annotate | Revision Log
« no previous file with comments | « pkg/mdv/lib/src/element.dart ('k') | pkg/mdv/lib/src/input_element.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of mdv;
6
7 abstract class _InputBinding extends NodeBinding {
8 StreamSubscription _eventSub;
9
10 _InputBinding(node, name, model, path): super(node, name, model, path) {
11 _eventSub = _getStreamForInputType(node).listen(nodeValueChanged);
12 }
13
14 void boundValueChanged(newValue);
15
16 void nodeValueChanged(e);
17
18 void close() {
19 if (closed) return;
20 _eventSub.cancel();
21 super.close();
22 }
23
24 static EventStreamProvider<Event> _checkboxEventType = () {
25 // Attempt to feature-detect which event (change or click) is fired first
26 // for checkboxes.
27 var div = new DivElement();
28 var checkbox = div.append(new InputElement());
29 checkbox.type = 'checkbox';
30 var fired = [];
31 checkbox.onClick.listen((e) {
32 fired.add(Element.clickEvent);
33 });
34 checkbox.onChange.listen((e) {
35 fired.add(Element.changeEvent);
36 });
37 checkbox.dispatchEvent(new MouseEvent('click', view: window));
38 // WebKit/Blink don't fire the change event if the element is outside the
39 // document, so assume 'change' for that case.
40 return fired.length == 1 ? Element.changeEvent : fired.first;
41 }();
42
43 static Stream<Event> _getStreamForInputType(element) {
44 switch (element.type) {
45 case 'checkbox':
46 return _checkboxEventType.forTarget(element);
47 case 'radio':
48 case 'select-multiple':
49 case 'select-one':
50 return element.onChange;
51 default:
52 return element.onInput;
53 }
54 }
55 }
56
57 class _ValueBinding extends _InputBinding {
58 _ValueBinding(node, model, path) : super(node, 'value', model, path);
59
60 get node => super.node;
61
62 void boundValueChanged(newValue) {
63 // Note: node can be an InputElement or TextAreaElement. Both have "value".
64 (node as dynamic).value = sanitizeBoundValue(newValue);
65 }
66
67 void nodeValueChanged(e) {
68 value = (node as dynamic).value;
69 }
70 }
71
72 class _CheckedBinding extends _InputBinding {
73 _CheckedBinding(node, model, path) : super(node, 'checked', model, path);
74
75 InputElement get node => super.node;
76
77 void boundValueChanged(newValue) {
78 node.checked = _toBoolean(newValue);
79 }
80
81 void nodeValueChanged(e) {
82 value = node.checked;
83
84 // Only the radio button that is getting checked gets an event. We
85 // therefore find all the associated radio buttons and update their
86 // CheckedBinding manually.
87 if (node is InputElement && node.type == 'radio') {
88 for (var r in _getAssociatedRadioButtons(node)) {
89 var checkedBinding = r.bindings['checked'];
90 if (checkedBinding != null) {
91 // Set the value directly to avoid an infinite call stack.
92 checkedBinding.value = false;
93 }
94 }
95 }
96 }
97
98 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
99 // Returns an array containing all radio buttons other than |element| that
100 // have the same |name|, either in the form that |element| belongs to or,
101 // if no form, in the document tree to which |element| belongs.
102 //
103 // This implementation is based upon the HTML spec definition of a
104 // "radio button group":
105 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
106 //
107 static Iterable _getAssociatedRadioButtons(element) {
108 if (!_isNodeInDocument(element)) return [];
109 if (element.form != null) {
110 return element.form.nodes.where((el) {
111 return el != element &&
112 el is InputElement &&
113 el.type == 'radio' &&
114 el.name == element.name;
115 });
116 } else {
117 var radios = element.ownerDocument.queryAll(
118 'input[type="radio"][name="${element.name}"]');
119 return radios.where((el) => el != element && el.form == null);
120 }
121 }
122
123 // TODO(jmesserly): polyfill document.contains API instead of doing it here
124 static bool _isNodeInDocument(Node node) {
125 // On non-IE this works:
126 // return node.ownerDocument.contains(node);
127 var document = node.document;
128 if (node == document || node.parentNode == document) return true;
129 return document.documentElement.contains(node);
130 }
131 }
132
133 class _SelectedIndexBinding extends _InputBinding {
134 _SelectedIndexBinding(node, model, path)
135 : super(node, 'selectedIndex', model, path);
136
137 SelectElement get node => super.node;
138
139 void boundValueChanged(value) {
140 var newValue = _toInt(value);
141 if (newValue <= node.length) {
142 node.selectedIndex = newValue;
143 return;
144 }
145
146 // The binding may wish to bind to an <option> which has not yet been
147 // produced by a child <template>. Furthermore, we may need to wait for
148 // <optgroup> iterating and then for <option>.
149 //
150 // Unlike the JavaScript MDV, we don't have a special "Object.observe" event
151 // loop to schedule on. (See the the "ensureScheduled" function:
152 // https://github.com/Polymer/mdv/commit/9a51ad7ed74a292bf71662cea28acbd151f f65c8)
153 //
154 // Instead we use scheduleMicrotask. Each <template repeat> needs a delay of
155 // 2:
156 // * once to happen after the child _TemplateIterator is created
157 // * once to be after _TemplateIterator.inputs CompoundBinding resolve
158 // And then we need to do this delay sequence twice:
159 // * once for OPTGROUP
160 // * once for OPTION.
161 // The resulting 2 * 2 is our maxRetries.
162 var maxRetries = 4;
163 delaySetSelectedIndex() {
164 if (newValue > node.length && --maxRetries >= 0) {
165 scheduleMicrotask(delaySetSelectedIndex);
166 } else {
167 node.selectedIndex = newValue;
168 }
169 }
170
171 scheduleMicrotask(delaySetSelectedIndex);
172 }
173
174 void nodeValueChanged(e) {
175 value = node.selectedIndex;
176 }
177
178 // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from
179 // one type to another (e.g. value-as-number) and whether it is useful to
180 // have something like a int/num binding converter (either as a base class or
181 // a wrapper).
182 static int _toInt(value) {
183 if (value is String) return int.parse(value, onError: (_) => null);
184 return value is int ? value : null;
185 }
186 }
OLDNEW
« no previous file with comments | « pkg/mdv/lib/src/element.dart ('k') | pkg/mdv/lib/src/input_element.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698