Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of mdv; | 5 part of mdv; |
| 6 | 6 |
| 7 abstract class _InputBinding { | 7 abstract class _InputBinding extends NodeBinding { |
| 8 // InputElement or SelectElement | |
| 9 final element; | |
| 10 PathObserver binding; | |
| 11 StreamSubscription _pathSub; | |
| 12 StreamSubscription _eventSub; | 8 StreamSubscription _eventSub; |
| 13 | 9 |
| 14 _InputBinding(this.element, model, String path) { | 10 _InputBinding(node, name, model, path): super(node, name, model, path) { |
| 15 binding = new PathObserver(model, path); | 11 _eventSub = _getStreamForInputType(node).listen(nodeValueChanged); |
| 16 _pathSub = binding.bindSync(valueChanged); | |
| 17 _eventSub = _getStreamForInputType(element).listen(updateBinding); | |
| 18 } | 12 } |
| 19 | 13 |
| 20 void valueChanged(newValue); | 14 void boundValueChanged(newValue); |
| 21 | 15 |
| 22 void updateBinding(e); | 16 void nodeValueChanged(e); |
| 23 | 17 |
| 24 void unbind() { | 18 void close() { |
| 25 binding = null; | 19 if (closed) return; |
| 26 _pathSub.cancel(); | |
| 27 _eventSub.cancel(); | 20 _eventSub.cancel(); |
| 21 super.close(); | |
| 28 } | 22 } |
| 29 | 23 |
| 30 static EventStreamProvider<Event> _checkboxEventType = () { | 24 static EventStreamProvider<Event> _checkboxEventType = () { |
| 31 // Attempt to feature-detect which event (change or click) is fired first | 25 // Attempt to feature-detect which event (change or click) is fired first |
| 32 // for checkboxes. | 26 // for checkboxes. |
| 33 var div = new DivElement(); | 27 var div = new DivElement(); |
| 34 var checkbox = div.append(new InputElement()); | 28 var checkbox = div.append(new InputElement()); |
| 35 checkbox.type = 'checkbox'; | 29 checkbox.type = 'checkbox'; |
| 36 var fired = []; | 30 var fired = []; |
| 37 checkbox.onClick.listen((e) { | 31 checkbox.onClick.listen((e) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 54 case 'select-multiple': | 48 case 'select-multiple': |
| 55 case 'select-one': | 49 case 'select-one': |
| 56 return element.onChange; | 50 return element.onChange; |
| 57 default: | 51 default: |
| 58 return element.onInput; | 52 return element.onInput; |
| 59 } | 53 } |
| 60 } | 54 } |
| 61 } | 55 } |
| 62 | 56 |
| 63 class _ValueBinding extends _InputBinding { | 57 class _ValueBinding extends _InputBinding { |
| 64 _ValueBinding(element, model, path) : super(element, model, path); | 58 _ValueBinding(node, model, path) : super(node, 'value', model, path); |
| 65 | 59 |
| 66 void valueChanged(value) { | 60 get node => super.node; |
|
Siggi Cherem (dart-lang)
2013/07/25 16:24:12
remove this getter or the 'as dynamic' below?
| |
| 67 // Note: element can be an InputElement or TextAreaElement. | 61 |
| 68 element.value = value == null ? '' : '$value'; | 62 void boundValueChanged(newValue) { |
| 63 // Note: node can be an InputElement or TextAreaElement. Both have "value". | |
| 64 (node as dynamic).value = sanitizeBoundValue(newValue); | |
| 69 } | 65 } |
| 70 | 66 |
| 71 void updateBinding(e) { | 67 void nodeValueChanged(e) { |
| 72 binding.value = element.value; | 68 value = (node as dynamic).value; |
| 73 } | 69 } |
| 74 } | 70 } |
| 75 | 71 |
| 76 class _CheckedBinding extends _InputBinding { | 72 class _CheckedBinding extends _InputBinding { |
| 77 _CheckedBinding(element, model, path) : super(element, model, path); | 73 _CheckedBinding(node, model, path) : super(node, 'checked', model, path); |
| 78 | 74 |
| 79 InputElement get element => super.element; | 75 InputElement get node => super.node; |
| 80 | 76 |
| 81 void valueChanged(value) { | 77 void boundValueChanged(newValue) { |
| 82 element.checked = _toBoolean(value); | 78 node.checked = _toBoolean(newValue); |
| 83 } | 79 } |
| 84 | 80 |
| 85 void updateBinding(e) { | 81 void nodeValueChanged(e) { |
| 86 binding.value = element.checked; | 82 value = node.checked; |
| 87 | 83 |
| 88 // Only the radio button that is getting checked gets an event. We | 84 // Only the radio button that is getting checked gets an event. We |
| 89 // therefore find all the associated radio buttons and update their | 85 // therefore find all the associated radio buttons and update their |
| 90 // CheckedBinding manually. | 86 // CheckedBinding manually. |
| 91 if (element is InputElement && element.type == 'radio') { | 87 if (node is InputElement && node.type == 'radio') { |
| 92 for (var r in _getAssociatedRadioButtons(element)) { | 88 for (var r in _getAssociatedRadioButtons(node)) { |
| 93 var checkedBinding = _mdv(r)._checkedBinding; | 89 var checkedBinding = r.bindings['checked']; |
| 94 if (checkedBinding != null) { | 90 if (checkedBinding != null) { |
| 95 // Set the value directly to avoid an infinite call stack. | 91 // Set the value directly to avoid an infinite call stack. |
| 96 checkedBinding.binding.value = false; | 92 checkedBinding.value = false; |
| 97 } | 93 } |
| 98 } | 94 } |
| 99 } | 95 } |
| 100 } | 96 } |
| 101 | 97 |
| 102 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'. | 98 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'. |
| 103 // Returns an array containing all radio buttons other than |element| that | 99 // Returns an array containing all radio buttons other than |element| that |
| 104 // have the same |name|, either in the form that |element| belongs to or, | 100 // have the same |name|, either in the form that |element| belongs to or, |
| 105 // if no form, in the document tree to which |element| belongs. | 101 // if no form, in the document tree to which |element| belongs. |
| 106 // | 102 // |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 128 static bool _isNodeInDocument(Node node) { | 124 static bool _isNodeInDocument(Node node) { |
| 129 // On non-IE this works: | 125 // On non-IE this works: |
| 130 // return node.document.contains(node); | 126 // return node.document.contains(node); |
| 131 var document = node.document; | 127 var document = node.document; |
| 132 if (node == document || node.parentNode == document) return true; | 128 if (node == document || node.parentNode == document) return true; |
| 133 return document.documentElement.contains(node); | 129 return document.documentElement.contains(node); |
| 134 } | 130 } |
| 135 } | 131 } |
| 136 | 132 |
| 137 class _SelectedIndexBinding extends _InputBinding { | 133 class _SelectedIndexBinding extends _InputBinding { |
| 138 _SelectedIndexBinding(element, model, path) : super(element, model, path); | 134 _SelectedIndexBinding(node, model, path) |
| 135 : super(node, 'selectedIndex', model, path); | |
| 139 | 136 |
| 140 SelectElement get element => super.element; | 137 SelectElement get node => super.node; |
| 141 | 138 |
| 142 void valueChanged(value) { | 139 void boundValueChanged(value) { |
| 143 var newValue = _toInt(value); | 140 var newValue = _toInt(value); |
| 144 if (newValue <= element.length) { | 141 if (newValue <= node.length) { |
| 145 element.selectedIndex = newValue; | 142 node.selectedIndex = newValue; |
| 146 return; | 143 return; |
| 147 } | 144 } |
| 148 | 145 |
| 149 // The binding may wish to bind to an <option> which has not yet been | 146 // The binding may wish to bind to an <option> which has not yet been |
| 150 // produced by a child <template>. Furthermore, we may need to wait for | 147 // produced by a child <template>. Furthermore, we may need to wait for |
| 151 // <optgroup> iterating and then for <option>. | 148 // <optgroup> iterating and then for <option>. |
| 152 // | 149 // |
| 153 // Unlike the JavaScript MDV, we don't have a special "Object.observe" event | 150 // Unlike the JavaScript MDV, we don't have a special "Object.observe" event |
| 154 // loop to schedule on. (See the the "ensureScheduled" function: | 151 // loop to schedule on. (See the the "ensureScheduled" function: |
| 155 // https://github.com/Polymer/mdv/commit/9a51ad7ed74a292bf71662cea28acbd151f f65c8) | 152 // https://github.com/Polymer/mdv/commit/9a51ad7ed74a292bf71662cea28acbd151f f65c8) |
| 156 // | 153 // |
| 157 // Instead we use runAsync. Each <template repeat> needs a delay of 2: | 154 // Instead we use runAsync. Each <template repeat> needs a delay of 2: |
| 158 // * once to happen after the child _TemplateIterator is created | 155 // * once to happen after the child _TemplateIterator is created |
| 159 // * once to be after _TemplateIterator.inputs CompoundBinding resolve | 156 // * once to be after _TemplateIterator.inputs CompoundBinding resolve |
| 160 // And then we need to do this delay sequence twice: | 157 // And then we need to do this delay sequence twice: |
| 161 // * once for OPTGROUP | 158 // * once for OPTGROUP |
| 162 // * once for OPTION. | 159 // * once for OPTION. |
| 163 // The resulting 2 * 2 is our maxRetries. | 160 // The resulting 2 * 2 is our maxRetries. |
| 164 var maxRetries = 4; | 161 var maxRetries = 4; |
| 165 delaySetSelectedIndex() { | 162 delaySetSelectedIndex() { |
| 166 if (newValue > element.length && --maxRetries >= 0) { | 163 if (newValue > node.length && --maxRetries >= 0) { |
| 167 runAsync(delaySetSelectedIndex); | 164 runAsync(delaySetSelectedIndex); |
| 168 } else { | 165 } else { |
| 169 element.selectedIndex = newValue; | 166 node.selectedIndex = newValue; |
| 170 } | 167 } |
| 171 } | 168 } |
| 172 | 169 |
| 173 runAsync(delaySetSelectedIndex); | 170 runAsync(delaySetSelectedIndex); |
| 174 } | 171 } |
| 175 | 172 |
| 176 void updateBinding(e) { | 173 void nodeValueChanged(e) { |
| 177 binding.value = element.selectedIndex; | 174 value = node.selectedIndex; |
| 178 } | 175 } |
| 179 | 176 |
| 180 // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from | 177 // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from |
| 181 // one type to another (e.g. value-as-number) and whether it is useful to | 178 // one type to another (e.g. value-as-number) and whether it is useful to |
| 182 // have something like a int/num binding converter (either as a base class or | 179 // have something like a int/num binding converter (either as a base class or |
| 183 // a wrapper). | 180 // a wrapper). |
| 184 static int _toInt(value) { | 181 static int _toInt(value) { |
| 185 if (value is String) return int.parse(value, onError: (_) => null); | 182 if (value is String) return int.parse(value, onError: (_) => null); |
| 186 return value is int ? value : null; | 183 return value is int ? value : null; |
| 187 } | 184 } |
| 188 } | 185 } |
| OLD | NEW |