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 polymer; | 5 part of polymer; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Use this annotation to publish a field as an attribute. For example: | 8 * Use this annotation to publish a field as an attribute. For example: |
| 9 * | 9 * |
| 10 * class MyPlaybackElement extends PolymerElement { | 10 * class MyPlaybackElement extends PolymerElement { |
| 11 * // This will be available as an HTML attribute, for example: | 11 * // This will be available as an HTML attribute, for example: |
| 12 * // <my-playback volume="11"> | 12 * // <my-playback volume="11"> |
| 13 * @published double volume; | 13 * @published double volume; |
| 14 * } | 14 * } |
| 15 */ | 15 */ |
| 16 // TODO(jmesserly): does @published imply @observable or vice versa? | 16 // TODO(jmesserly): does @published imply @observable or vice versa? |
| 17 const published = const PublishedProperty(); | 17 const published = const PublishedProperty(); |
| 18 | 18 |
| 19 /** An annotation used to publish a field as an attribute. See [published]. */ | 19 /** An annotation used to publish a field as an attribute. See [published]. */ |
| 20 class PublishedProperty extends ObservableProperty { | 20 class PublishedProperty extends ObservableProperty { |
| 21 const PublishedProperty(); | 21 const PublishedProperty(); |
| 22 } | 22 } |
| 23 | 23 |
| 24 // TODO(jmesserly): make this the mixin so we can have Polymer type extensions, | 24 /** |
| 25 // and move the implementation of PolymerElement in here. Once done it will look | 25 * The mixin class for Polymer elements. It provides convenience features on top |
| 26 // like: | 26 * of the custom elements web standard. |
| 27 // abstract class Polymer { ... all the things ... } | 27 */ |
| 28 // typedef PolymerElement = HtmlElement with Polymer, Observable; | 28 abstract class Polymer implements Element { |
| 29 abstract class Polymer { | 29 // Fully ported from revision: |
| 30 // https://github.com/Polymer/polymer/blob/4dc481c11505991a7c43228d3797d28f212 67779 | |
| 31 // | |
| 32 // src/instance/attributes.js | |
| 33 // src/instance/base.js | |
| 34 // src/instance/events.js | |
| 35 // src/instance/mdv.js | |
| 36 // src/instance/properties.js | |
| 37 // src/instance/utils.js | |
| 38 // | |
| 39 // Not yet ported: | |
| 40 // src/instance/style.js -- blocked on ShadowCSS.shimPolyfillDirectives | |
| 41 | |
| 42 | |
| 30 // TODO(jmesserly): should this really be public? | 43 // TODO(jmesserly): should this really be public? |
| 31 /** Regular expression that matches data-bindings. */ | 44 /** Regular expression that matches data-bindings. */ |
| 32 static final bindPattern = new RegExp(r'\{\{([^{}]*)}}'); | 45 static final bindPattern = new RegExp(r'\{\{([^{}]*)}}'); |
| 33 | 46 |
| 34 /** | 47 /** |
| 35 * Like [document.register] but for Polymer elements. | 48 * Like [document.register] but for Polymer elements. |
| 36 * | 49 * |
| 37 * Use the [name] to specify custom elment's tag name, for example: | 50 * Use the [name] to specify custom elment's tag name, for example: |
| 38 * "fancy-button" if the tag is used as `<fancy-button>`. | 51 * "fancy-button" if the tag is used as `<fancy-button>`. |
| 39 * | 52 * |
| 40 * The [type] is the type to construct. If not supplied, it defaults to | 53 * The [type] is the type to construct. If not supplied, it defaults to |
| 41 * [PolymerElement]. | 54 * [PolymerElement]. |
| 42 */ | 55 */ |
| 43 // NOTE: this is called "element" in src/declaration/polymer-element.js, and | 56 // NOTE: this is called "element" in src/declaration/polymer-element.js, and |
| 44 // exported as "Polymer". | 57 // exported as "Polymer". |
| 45 static void register(String name, [Type type]) { | 58 static void register(String name, [Type type]) { |
| 46 //console.log('registering [' + name + ']'); | 59 //console.log('registering [' + name + ']'); |
| 47 if (type == null) type = PolymerElement; | 60 if (type == null) type = PolymerElement; |
| 48 _registerClassMirror(name, reflectClass(type)); | |
| 49 } | |
| 50 | 61 |
| 51 // TODO(jmesserly): we use ClassMirror internall for now, until it is possible | |
| 52 // to get from ClassMirror -> Type. | |
| 53 static void _registerClassMirror(String name, ClassMirror type) { | |
| 54 _typesByName[name] = type; | 62 _typesByName[name] = type; |
| 55 // notify the registrar waiting for 'name', if any | 63 // notify the registrar waiting for 'name', if any |
| 56 _notifyType(name); | 64 _notifyType(name); |
| 57 } | 65 } |
| 58 } | |
| 59 | |
| 60 /** | |
| 61 * The base class for Polymer elements. It provides convience features on top | |
| 62 * of the custom elements web standard. | |
| 63 */ | |
| 64 class PolymerElement extends CustomElement with ObservableMixin { | |
| 65 // Fully ported from revision: | |
| 66 // https://github.com/Polymer/polymer/blob/4dc481c11505991a7c43228d3797d28f212 67779 | |
| 67 // | |
| 68 // src/instance/attributes.js | |
| 69 // src/instance/base.js | |
| 70 // src/instance/events.js | |
| 71 // src/instance/mdv.js | |
| 72 // src/instance/properties.js | |
| 73 // src/instance/utils.js | |
| 74 // | |
| 75 // Not yet ported: | |
| 76 // src/instance/style.js -- blocked on ShadowCSS.shimPolyfillDirectives | |
| 77 | 66 |
| 78 /// The one syntax to rule them all. | 67 /// The one syntax to rule them all. |
| 79 static final BindingDelegate _polymerSyntax = new PolymerExpressions(); | 68 static final BindingDelegate _polymerSyntax = new PolymerExpressions(); |
| 80 | 69 |
| 81 static int _preparingElements = 0; | 70 static int _preparingElements = 0; |
| 82 | 71 |
| 83 PolymerDeclaration _declaration; | 72 PolymerDeclaration _declaration; |
| 84 | 73 |
| 85 /** The most derived `<polymer-element>` declaration for this element. */ | 74 /** The most derived `<polymer-element>` declaration for this element. */ |
| 86 PolymerDeclaration get declaration => _declaration; | 75 PolymerDeclaration get declaration => _declaration; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 113 * Gets the shadow root associated with the corresponding custom element. | 102 * Gets the shadow root associated with the corresponding custom element. |
| 114 * | 103 * |
| 115 * This is identical to [shadowRoot], unless there are multiple levels of | 104 * This is identical to [shadowRoot], unless there are multiple levels of |
| 116 * inheritance and they each have their own shadow root. For example, | 105 * inheritance and they each have their own shadow root. For example, |
| 117 * this can happen if the base class and subclass both have `<template>` tags | 106 * this can happen if the base class and subclass both have `<template>` tags |
| 118 * in their `<polymer-element>` tags. | 107 * in their `<polymer-element>` tags. |
| 119 */ | 108 */ |
| 120 // TODO(jmesserly): Polymer does not have this feature. Reconcile. | 109 // TODO(jmesserly): Polymer does not have this feature. Reconcile. |
| 121 ShadowRoot getShadowRoot(String customTagName) => _shadowRoots[customTagName]; | 110 ShadowRoot getShadowRoot(String customTagName) => _shadowRoots[customTagName]; |
| 122 | 111 |
| 123 ShadowRoot createShadowRoot([name]) { | |
| 124 if (name != null) { | |
| 125 throw new ArgumentError('name argument must not be supplied.'); | |
| 126 } | |
| 127 | |
| 128 // Provides ability to traverse from ShadowRoot to the host. | |
| 129 // TODO(jmessery): remove once we have this ability on the DOM. | |
| 130 final root = super.createShadowRoot(); | |
| 131 _shadowHost[root] = host; | |
| 132 return root; | |
| 133 } | |
| 134 | |
| 135 /** | 112 /** |
| 136 * Invoke [callback] in [wait], unless the job is re-registered, | 113 * Invoke [callback] in [wait], unless the job is re-registered, |
| 137 * which resets the timer. For example: | 114 * which resets the timer. For example: |
| 138 * | 115 * |
| 139 * _myJob = job(_myJob, callback, const Duration(milliseconds: 100)); | 116 * _myJob = job(_myJob, callback, const Duration(milliseconds: 100)); |
| 140 * | 117 * |
| 141 * Returns a job handle which can be used to re-register a job. | 118 * Returns a job handle which can be used to re-register a job. |
| 142 */ | 119 */ |
| 143 Job job(Job job, void callback(), Duration wait) => | 120 Job job(Job job, void callback(), Duration wait) => |
| 144 runJob(job, callback, wait); | 121 runJob(job, callback, wait); |
| 145 | 122 |
| 146 // TODO(jmesserly): I am not sure if we should have the | 123 // TODO(jmesserly): I am not sure if we should have the |
| 147 // created/createdCallback distinction. See post here: | 124 // created/createdCallback distinction. See post here: |
| 148 // https://groups.google.com/d/msg/polymer-dev/W0ZUpU5caIM/v5itFnvnehEJ | 125 // https://groups.google.com/d/msg/polymer-dev/W0ZUpU5caIM/v5itFnvnehEJ |
| 149 // Same issue with inserted and removed. | 126 // Same issue with inserted and removed. |
| 150 void created() { | 127 void initialize() { |
| 151 if (document.window != null || alwaysPrepare || _preparingElements > 0) { | 128 if (document.window != null || alwaysPrepare || _preparingElements > 0) { |
| 152 prepareElement(); | 129 prepareElement(); |
| 153 } | 130 } |
| 154 } | 131 } |
| 155 | 132 |
| 156 void prepareElement() { | 133 void prepareElement() { |
| 157 // Dart note: get the _declaration, which also marks _elementPrepared | 134 // Dart note: get the _declaration, which also marks _elementPrepared |
| 158 _declaration = _getDeclaration(reflect(this).type); | 135 _declaration = _getDeclaration(this.runtimeType); |
| 159 // do this first so we can observe changes during initialization | 136 // do this first so we can observe changes during initialization |
| 160 observeProperties(); | 137 observeProperties(); |
| 161 // install boilerplate attributes | 138 // install boilerplate attributes |
| 162 copyInstanceAttributes(); | 139 copyInstanceAttributes(); |
| 163 // process input attributes | 140 // process input attributes |
| 164 takeAttributes(); | 141 takeAttributes(); |
| 165 // add event listeners | 142 // add event listeners |
| 166 addHostListeners(); | 143 addHostListeners(); |
| 167 // guarantees that while preparing, any sub-elements will also be prepared | 144 // guarantees that while preparing, any sub-elements will also be prepared |
| 168 _preparingElements++; | 145 _preparingElements++; |
| 169 // process declarative resources | 146 // process declarative resources |
| 170 parseDeclarations(_declaration); | 147 parseDeclarations(_declaration); |
| 171 _preparingElements--; | 148 _preparingElements--; |
| 172 // user entry point | 149 // user entry point |
| 173 ready(); | 150 ready(); |
| 174 } | 151 } |
| 175 | 152 |
| 176 /** Called when [prepareElement] is finished. */ | 153 /** Called when [prepareElement] is finished. */ |
| 177 void ready() {} | 154 void ready() {} |
| 178 | 155 |
| 179 void inserted() { | 156 void enteredView() { |
| 180 if (!_elementPrepared) { | 157 if (!_elementPrepared) { |
| 181 prepareElement(); | 158 prepareElement(); |
| 182 } | 159 } |
| 183 cancelUnbindAll(preventCascade: true); | 160 cancelUnbindAll(preventCascade: true); |
| 184 } | 161 } |
| 185 | 162 |
| 186 void removed() { | 163 void leftView() { |
| 187 asyncUnbindAll(); | 164 asyncUnbindAll(); |
| 188 } | 165 } |
| 189 | 166 |
| 190 /** Recursive ancestral <element> initialization, oldest first. */ | 167 /** Recursive ancestral <element> initialization, oldest first. */ |
| 191 void parseDeclarations(PolymerDeclaration declaration) { | 168 void parseDeclarations(PolymerDeclaration declaration) { |
| 192 if (declaration != null) { | 169 if (declaration != null) { |
| 193 parseDeclarations(declaration.superDeclaration); | 170 parseDeclarations(declaration.superDeclaration); |
| 194 parseDeclaration(declaration.host); | 171 parseDeclaration(declaration); |
| 195 } | 172 } |
| 196 } | 173 } |
| 197 | 174 |
| 198 /** | 175 /** |
| 199 * Parse input `<polymer-element>` as needed, override for custom behavior. | 176 * Parse input `<polymer-element>` as needed, override for custom behavior. |
| 200 */ | 177 */ |
| 201 void parseDeclaration(Element elementElement) { | 178 void parseDeclaration(Element elementElement) { |
| 202 var root = shadowFromTemplate(fetchTemplate(elementElement)); | 179 var root = shadowFromTemplate(fetchTemplate(elementElement)); |
| 203 | 180 |
| 204 // Dart note: this is extra code compared to Polymer to support | 181 // Dart note: this is extra code compared to Polymer to support |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 216 Element fetchTemplate(Element elementElement) => | 193 Element fetchTemplate(Element elementElement) => |
| 217 elementElement.query('template'); | 194 elementElement.query('template'); |
| 218 | 195 |
| 219 /** Utility function that creates a shadow root from a `<template>`. */ | 196 /** Utility function that creates a shadow root from a `<template>`. */ |
| 220 ShadowRoot shadowFromTemplate(Element template) { | 197 ShadowRoot shadowFromTemplate(Element template) { |
| 221 if (template == null) return null; | 198 if (template == null) return null; |
| 222 // cache elder shadow root (if any) | 199 // cache elder shadow root (if any) |
| 223 var elderRoot = this.shadowRoot; | 200 var elderRoot = this.shadowRoot; |
| 224 // make a shadow root | 201 // make a shadow root |
| 225 var root = createShadowRoot(); | 202 var root = createShadowRoot(); |
| 203 | |
| 204 // Provides ability to traverse from ShadowRoot to the host. | |
| 205 // TODO(jmessery): remove once we have this ability on the DOM. | |
| 206 _shadowHost[root] = this; | |
| 207 | |
| 226 // migrate flag(s)( | 208 // migrate flag(s)( |
| 227 root.applyAuthorStyles = applyAuthorStyles; | 209 root.applyAuthorStyles = applyAuthorStyles; |
| 228 root.resetStyleInheritance = resetStyleInheritance; | 210 root.resetStyleInheritance = resetStyleInheritance; |
| 229 // stamp template | 211 // stamp template |
| 230 // which includes parsing and applying MDV bindings before being | 212 // which includes parsing and applying MDV bindings before being |
| 231 // inserted (to avoid {{}} in attribute values) | 213 // inserted (to avoid {{}} in attribute values) |
| 232 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. | 214 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. |
| 233 var dom = instanceTemplate(template); | 215 var dom = instanceTemplate(template); |
| 234 // append to shadow dom | 216 // append to shadow dom |
| 235 root.append(dom); | 217 root.append(dom); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 250 } | 232 } |
| 251 | 233 |
| 252 /** Locate nodes with id and store references to them in [$] hash. */ | 234 /** Locate nodes with id and store references to them in [$] hash. */ |
| 253 void marshalNodeReferences(ShadowRoot root) { | 235 void marshalNodeReferences(ShadowRoot root) { |
| 254 if (root == null) return; | 236 if (root == null) return; |
| 255 for (var n in root.queryAll('[id]')) { | 237 for (var n in root.queryAll('[id]')) { |
| 256 $[n.id] = n; | 238 $[n.id] = n; |
| 257 } | 239 } |
| 258 } | 240 } |
| 259 | 241 |
| 260 void attributeChanged(String name, String oldValue) { | 242 void attributeChanged(String name, String oldValue, String newValue) { |
|
blois
2013/10/11 22:40:12
Note that this is a new breaking change for Polyme
| |
| 261 if (name != 'class' && name != 'style') { | 243 if (name != 'class' && name != 'style') { |
| 262 attributeToProperty(name, attributes[name]); | 244 attributeToProperty(name, newValue); |
| 263 } | 245 } |
| 264 } | 246 } |
| 265 | 247 |
| 266 // TODO(jmesserly): use stream or future here? | 248 // TODO(jmesserly): use stream or future here? |
| 267 /** | 249 /** |
| 268 * Run the `listener` callback *once* | 250 * Run the `listener` callback *once* |
| 269 * when `node` changes, or when its children or subtree changes. | 251 * when `node` changes, or when its children or subtree changes. |
| 270 * | 252 * |
| 271 * | 253 * |
| 272 * See [MutationObserver] if you want to listen to a stream of | 254 * See [MutationObserver] if you want to listen to a stream of |
| 273 * changes. | 255 * changes. |
| 274 */ | 256 */ |
| 275 void onMutation(Node node, void listener(MutationObserver obs)) { | 257 void onMutation(Node node, void listener(MutationObserver obs)) { |
| 276 new MutationObserver((records, MutationObserver observer) { | 258 new MutationObserver((records, MutationObserver observer) { |
| 277 listener(observer); | 259 listener(observer); |
| 278 observer.disconnect(); | 260 observer.disconnect(); |
| 279 })..observe(node, childList: true, subtree: true); | 261 })..observe(node, childList: true, subtree: true); |
| 280 } | 262 } |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 399 if (property != null) { | 381 if (property != null) { |
| 400 unbind(name); | 382 unbind(name); |
| 401 // use n-way Polymer binding | 383 // use n-way Polymer binding |
| 402 var observer = bindProperty(property.simpleName, model, path); | 384 var observer = bindProperty(property.simpleName, model, path); |
| 403 // reflect bound property to attribute when binding | 385 // reflect bound property to attribute when binding |
| 404 // to ensure binding is not left on attribute if property | 386 // to ensure binding is not left on attribute if property |
| 405 // does not update due to not changing. | 387 // does not update due to not changing. |
| 406 reflectPropertyToAttribute(name); | 388 reflectPropertyToAttribute(name); |
| 407 return bindings[name] = observer; | 389 return bindings[name] = observer; |
| 408 } else { | 390 } else { |
| 409 return super.bind(name, model, path); | 391 // Cannot call super.bind because of |
| 392 // https://code.google.com/p/dart/issues/detail?id=13156 | |
| 393 // https://code.google.com/p/dart/issues/detail?id=12456 | |
| 394 return TemplateElement.mdvPackage(this).bind(name, model, path); | |
| 410 } | 395 } |
| 411 } | 396 } |
| 412 | 397 |
| 413 void asyncUnbindAll() { | 398 void asyncUnbindAll() { |
| 414 if (_unbound == true) return; | 399 if (_unbound == true) return; |
| 415 _unbindLog.info('[$localName] asyncUnbindAll'); | 400 _unbindLog.info('[$localName] asyncUnbindAll'); |
| 416 _unbindAllJob = job(_unbindAllJob, unbindAll, const Duration(seconds: 0)); | 401 _unbindAllJob = job(_unbindAllJob, unbindAll, const Duration(seconds: 0)); |
| 417 } | 402 } |
| 418 | 403 |
| 419 void unbindAll() { | 404 void unbindAll() { |
| 420 if (_unbound == true) return; | 405 if (_unbound == true) return; |
| 421 | 406 |
| 422 unbindAllProperties(); | 407 unbindAllProperties(); |
| 423 super.unbindAll(); | 408 // Cannot call super.bind because of |
| 409 // https://code.google.com/p/dart/issues/detail?id=13156 | |
| 410 // https://code.google.com/p/dart/issues/detail?id=12456 | |
| 411 TemplateElement.mdvPackage(this).unbindAll(); | |
| 412 | |
| 424 _unbindNodeTree(shadowRoot); | 413 _unbindNodeTree(shadowRoot); |
| 425 // TODO(sjmiles): must also unbind inherited shadow roots | 414 // TODO(sjmiles): must also unbind inherited shadow roots |
| 426 _unbound = true; | 415 _unbound = true; |
| 427 } | 416 } |
| 428 | 417 |
| 429 void cancelUnbindAll({bool preventCascade}) { | 418 void cancelUnbindAll({bool preventCascade}) { |
| 430 if (_unbound == true) { | 419 if (_unbound == true) { |
| 431 _unbindLog.warning( | 420 _unbindLog.warning( |
| 432 '[$localName] already unbound, cannot cancel unbindAll'); | 421 '[$localName] already unbound, cannot cancel unbindAll'); |
| 433 return; | 422 return; |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 669 } | 658 } |
| 670 self.invoke(methodName, args); | 659 self.invoke(methodName, args); |
| 671 | 660 |
| 672 if (log) _eventsLog.info('<<< [$localName]: dispatch $methodName'); | 661 if (log) _eventsLog.info('<<< [$localName]: dispatch $methodName'); |
| 673 | 662 |
| 674 // TODO(jmesserly): workaround for HTML events not supporting zones. | 663 // TODO(jmesserly): workaround for HTML events not supporting zones. |
| 675 performMicrotaskCheckpoint(); | 664 performMicrotaskCheckpoint(); |
| 676 } | 665 } |
| 677 | 666 |
| 678 void instanceEventListener(Event event) { | 667 void instanceEventListener(Event event) { |
| 679 _listenLocal(host, event); | 668 _listenLocal(this, event); |
| 680 } | 669 } |
| 681 | 670 |
| 682 // TODO(sjmiles): much of the below privatized only because of the vague | 671 // TODO(sjmiles): much of the below privatized only because of the vague |
| 683 // notion this code is too fiddly and we need to revisit the core feature | 672 // notion this code is too fiddly and we need to revisit the core feature |
| 684 void _listenLocal(Element host, Event event) { | 673 void _listenLocal(Polymer host, Event event) { |
| 685 // TODO(jmesserly): do we need this check? It was using cancelBubble, see: | 674 // TODO(jmesserly): do we need this check? It was using cancelBubble, see: |
| 686 // https://github.com/Polymer/polymer/issues/292 | 675 // https://github.com/Polymer/polymer/issues/292 |
| 687 if (!event.bubbles) return; | 676 if (!event.bubbles) return; |
| 688 | 677 |
| 689 bool log = _eventsLog.isLoggable(Level.INFO); | 678 bool log = _eventsLog.isLoggable(Level.INFO); |
| 690 if (log) _eventsLog.info('>>> [$localName]: listenLocal [${event.type}]'); | 679 if (log) _eventsLog.info('>>> [$localName]: listenLocal [${event.type}]'); |
| 691 | 680 |
| 692 final eventOn = '$_EVENT_PREFIX${_eventNameFromType(event.type)}'; | 681 final eventOn = '$_EVENT_PREFIX${_eventNameFromType(event.type)}'; |
| 693 if (event.path == null) { | 682 if (event.path == null) { |
| 694 _listenLocalNoEventPath(host, event, eventOn); | 683 _listenLocalNoEventPath(host, event, eventOn); |
| 695 } else { | 684 } else { |
| 696 _listenLocalEventPath(host, event, eventOn); | 685 _listenLocalEventPath(host, event, eventOn); |
| 697 } | 686 } |
| 698 | 687 |
| 699 if (log) _eventsLog.info('<<< [$localName]: listenLocal [${event.type}]'); | 688 if (log) _eventsLog.info('<<< [$localName]: listenLocal [${event.type}]'); |
| 700 } | 689 } |
| 701 | 690 |
| 702 static void _listenLocalEventPath(Element host, Event event, String eventOn) { | 691 static void _listenLocalEventPath(Polymer host, Event event, String eventOn) { |
| 703 var c = null; | 692 var c = null; |
| 704 for (var target in event.path) { | 693 for (var target in event.path) { |
| 705 // if we hit host, stop | 694 // if we hit host, stop |
| 706 if (identical(target, host)) return; | 695 if (identical(target, host)) return; |
| 707 | 696 |
| 708 // find a controller for the target, unless we already found `host` | 697 // find a controller for the target, unless we already found `host` |
| 709 // as a controller | 698 // as a controller |
| 710 c = identical(c, host) ? c : _findController(target); | 699 c = identical(c, host) ? c : _findController(target); |
| 711 | 700 |
| 712 // if we have a controller, dispatch the event, and stop if the handler | 701 // if we have a controller, dispatch the event, and stop if the handler |
| 713 // returns true | 702 // returns true |
| 714 if (c != null && _handleEvent(c, target, event, eventOn)) { | 703 if (c != null && _handleEvent(c, target, event, eventOn)) { |
| 715 return; | 704 return; |
| 716 } | 705 } |
| 717 } | 706 } |
| 718 } | 707 } |
| 719 | 708 |
| 720 // TODO(sorvell): remove when ShadowDOM polyfill supports event path. | 709 // TODO(sorvell): remove when ShadowDOM polyfill supports event path. |
| 721 // Note that _findController will not return the expected controller when the | 710 // Note that _findController will not return the expected controller when the |
| 722 // event target is a distributed node. This is because we cannot traverse | 711 // event target is a distributed node. This is because we cannot traverse |
| 723 // from a composed node to a node in shadowRoot. | 712 // from a composed node to a node in shadowRoot. |
| 724 // This will be addressed via an event path api | 713 // This will be addressed via an event path api |
| 725 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066 | 714 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066 |
| 726 static void _listenLocalNoEventPath(Element host, Event event, | 715 static void _listenLocalNoEventPath(Polymer host, Event event, |
| 727 String eventOn) { | 716 String eventOn) { |
| 728 | 717 |
| 729 if (_eventsLog.isLoggable(Level.INFO)) { | 718 if (_eventsLog.isLoggable(Level.INFO)) { |
| 730 _eventsLog.info('event.path() not supported for ${event.type}'); | 719 _eventsLog.info('event.path() not supported for ${event.type}'); |
| 731 } | 720 } |
| 732 | 721 |
| 733 var target = event.target; | 722 var target = event.target; |
| 734 var c = null; | 723 var c = null; |
| 735 // if we hit dirt or host, stop | 724 // if we hit dirt or host, stop |
| 736 while (target != null && target != host) { | 725 while (target != null && target != host) { |
| 737 // find a controller for target `t`, unless we already found `host` | 726 // find a controller for target `t`, unless we already found `host` |
| 738 // as a controller | 727 // as a controller |
| 739 c = identical(c, host) ? c : _findController(target); | 728 c = identical(c, host) ? c : _findController(target); |
| 740 | 729 |
| 741 // if we have a controller, dispatch the event, return 'true' if | 730 // if we have a controller, dispatch the event, return 'true' if |
| 742 // handler returns true | 731 // handler returns true |
| 743 if (c != null && _handleEvent(c, target, event, eventOn)) { | 732 if (c != null && _handleEvent(c, target, event, eventOn)) { |
| 744 return; | 733 return; |
| 745 } | 734 } |
| 746 target = target.parent; | 735 target = target.parent; |
| 747 } | 736 } |
| 748 } | 737 } |
| 749 | 738 |
| 750 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot | 739 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot |
| 751 // was created on a PolymerElement. | 740 // was created on a PolymerElement. |
| 752 static Element _findController(Node node) { | 741 static Polymer _findController(Node node) { |
| 753 while (node.parentNode != null) { | 742 while (node.parentNode != null) { |
| 754 node = node.parentNode; | 743 node = node.parentNode; |
| 755 } | 744 } |
| 756 return _shadowHost[node]; | 745 return _shadowHost[node]; |
| 757 } | 746 } |
| 758 | 747 |
| 759 static bool _handleEvent(Element ctrlr, Node node, Event event, | 748 static bool _handleEvent(Polymer ctrlr, Node node, Event event, |
| 760 String eventOn) { | 749 String eventOn) { |
| 761 | 750 |
| 762 // Note: local events are listened only in the shadow root. This dynamic | 751 // Note: local events are listened only in the shadow root. This dynamic |
| 763 // lookup is used to distinguish determine whether the target actually has a | 752 // lookup is used to distinguish determine whether the target actually has a |
| 764 // listener, and if so, to determine lazily what's the target method. | 753 // listener, and if so, to determine lazily what's the target method. |
| 765 var name = node is Element ? (node as Element).attributes[eventOn] : null; | 754 var name = node is Element ? (node as Element).attributes[eventOn] : null; |
| 766 if (name != null && _handleIfNotHandled(node, event)) { | 755 if (name != null && _handleIfNotHandled(node, event)) { |
| 767 if (_eventsLog.isLoggable(Level.INFO)) { | 756 if (_eventsLog.isLoggable(Level.INFO)) { |
| 768 _eventsLog.info('[${ctrlr.localName}] found handler name [$name]'); | 757 _eventsLog.info('[${ctrlr.localName}] found handler name [$name]'); |
| 769 } | 758 } |
| 770 var detail = event is CustomEvent ? | 759 var detail = event is CustomEvent ? |
| 771 (event as CustomEvent).detail : null; | 760 (event as CustomEvent).detail : null; |
| 772 | 761 |
| 773 if (node != null) { | 762 if (node != null) { |
| 774 // TODO(jmesserly): cache symbols? | 763 // TODO(jmesserly): cache symbols? |
| 775 ctrlr.xtag.dispatchMethod(new Symbol(name), [event, detail, node]); | 764 ctrlr.dispatchMethod(new Symbol(name), [event, detail, node]); |
| 776 } | 765 } |
| 777 } | 766 } |
| 778 | 767 |
| 779 // TODO(jmesserly): do we need this? It was using cancelBubble, see: | 768 // TODO(jmesserly): do we need this? It was using cancelBubble, see: |
| 780 // https://github.com/Polymer/polymer/issues/292 | 769 // https://github.com/Polymer/polymer/issues/292 |
| 781 return !event.bubbles; | 770 return !event.bubbles; |
| 782 } | 771 } |
| 783 | 772 |
| 784 // TODO(jmesserly): I don't understand this bit. It seems to be a duplicate | 773 // TODO(jmesserly): I don't understand this bit. It seems to be a duplicate |
| 785 // delivery prevention mechanism? | 774 // delivery prevention mechanism? |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 926 } | 915 } |
| 927 } | 916 } |
| 928 return type; | 917 return type; |
| 929 } | 918 } |
| 930 | 919 |
| 931 final Logger _observeLog = new Logger('polymer.observe'); | 920 final Logger _observeLog = new Logger('polymer.observe'); |
| 932 final Logger _eventsLog = new Logger('polymer.events'); | 921 final Logger _eventsLog = new Logger('polymer.events'); |
| 933 final Logger _unbindLog = new Logger('polymer.unbind'); | 922 final Logger _unbindLog = new Logger('polymer.unbind'); |
| 934 final Logger _bindLog = new Logger('polymer.bind'); | 923 final Logger _bindLog = new Logger('polymer.bind'); |
| 935 | 924 |
| 936 final Expando _shadowHost = new Expando<Element>(); | 925 final Expando _shadowHost = new Expando<Polymer>(); |
| 937 | 926 |
| 938 final Expando _eventHandledTable = new Expando<Set<Node>>(); | 927 final Expando _eventHandledTable = new Expando<Set<Node>>(); |
| 928 | |
| 929 /** | |
| 930 * Base class for PolymerElements deriving from HtmlElement. | |
| 931 * | |
| 932 * See [Polymer]. | |
| 933 */ | |
| 934 class PolymerElement extends HtmlElement with Polymer, ObservableMixin { | |
| 935 PolymerElement.created() : super.created() { | |
| 936 initialize(); | |
| 937 } | |
| 938 } | |
| OLD | NEW |