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 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 391 if (property != null) { | 373 if (property != null) { |
| 392 unbind(name); | 374 unbind(name); |
| 393 // use n-way Polymer binding | 375 // use n-way Polymer binding |
| 394 var observer = bindProperty(property.simpleName, model, path); | 376 var observer = bindProperty(property.simpleName, model, path); |
| 395 // reflect bound property to attribute when binding | 377 // reflect bound property to attribute when binding |
| 396 // to ensure binding is not left on attribute if property | 378 // to ensure binding is not left on attribute if property |
| 397 // does not update due to not changing. | 379 // does not update due to not changing. |
| 398 reflectPropertyToAttribute(name); | 380 reflectPropertyToAttribute(name); |
| 399 return bindings[name] = observer; | 381 return bindings[name] = observer; |
| 400 } else { | 382 } else { |
| 401 return super.bind(name, model, path); | 383 // Cannot call super.bind because of |
| 384 // https://code.google.com/p/dart/issues/detail?id=13156 | |
| 385 // https://code.google.com/p/dart/issues/detail?id=12456 | |
| 386 return TemplateElement.mdvPackage(this).bind(name, model, path); | |
| 402 } | 387 } |
| 403 } | 388 } |
| 404 | 389 |
| 405 void asyncUnbindAll() { | 390 void asyncUnbindAll() { |
| 406 if (_unbound == true) return; | 391 if (_unbound == true) return; |
| 407 _unbindLog.info('[$localName] asyncUnbindAll'); | 392 _unbindLog.info('[$localName] asyncUnbindAll'); |
| 408 _unbindAllJob = job(_unbindAllJob, unbindAll, const Duration(seconds: 0)); | 393 _unbindAllJob = job(_unbindAllJob, unbindAll, const Duration(seconds: 0)); |
| 409 } | 394 } |
| 410 | 395 |
| 411 void unbindAll() { | 396 void unbindAll() { |
| 412 if (_unbound == true) return; | 397 if (_unbound == true) return; |
| 413 | 398 |
| 414 unbindAllProperties(); | 399 unbindAllProperties(); |
| 415 super.unbindAll(); | 400 // Cannot call super.bind because of |
| 401 // https://code.google.com/p/dart/issues/detail?id=13156 | |
| 402 // https://code.google.com/p/dart/issues/detail?id=12456 | |
| 403 TemplateElement.mdvPackage(this).unbindAll(); | |
| 404 | |
| 416 _unbindNodeTree(shadowRoot); | 405 _unbindNodeTree(shadowRoot); |
| 417 // TODO(sjmiles): must also unbind inherited shadow roots | 406 // TODO(sjmiles): must also unbind inherited shadow roots |
| 418 _unbound = true; | 407 _unbound = true; |
| 419 } | 408 } |
| 420 | 409 |
| 421 void cancelUnbindAll({bool preventCascade}) { | 410 void cancelUnbindAll({bool preventCascade}) { |
| 422 if (_unbound == true) { | 411 if (_unbound == true) { |
| 423 _unbindLog.warning( | 412 _unbindLog.warning( |
| 424 '[$localName] already unbound, cannot cancel unbindAll'); | 413 '[$localName] already unbound, cannot cancel unbindAll'); |
| 425 return; | 414 return; |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 661 } | 650 } |
| 662 self.invoke(methodName, args); | 651 self.invoke(methodName, args); |
| 663 | 652 |
| 664 if (log) _eventsLog.info('<<< [$localName]: dispatch $methodName'); | 653 if (log) _eventsLog.info('<<< [$localName]: dispatch $methodName'); |
| 665 | 654 |
| 666 // TODO(jmesserly): workaround for HTML events not supporting zones. | 655 // TODO(jmesserly): workaround for HTML events not supporting zones. |
| 667 performMicrotaskCheckpoint(); | 656 performMicrotaskCheckpoint(); |
| 668 } | 657 } |
| 669 | 658 |
| 670 void instanceEventListener(Event event) { | 659 void instanceEventListener(Event event) { |
| 671 _listenLocal(host, event); | 660 _listenLocal(this, event); |
| 672 } | 661 } |
| 673 | 662 |
| 674 // TODO(sjmiles): much of the below privatized only because of the vague | 663 // TODO(sjmiles): much of the below privatized only because of the vague |
| 675 // notion this code is too fiddly and we need to revisit the core feature | 664 // notion this code is too fiddly and we need to revisit the core feature |
| 676 void _listenLocal(Element host, Event event) { | 665 void _listenLocal(Polymer host, Event event) { |
| 677 // TODO(jmesserly): do we need this check? It was using cancelBubble, see: | 666 // TODO(jmesserly): do we need this check? It was using cancelBubble, see: |
| 678 // https://github.com/Polymer/polymer/issues/292 | 667 // https://github.com/Polymer/polymer/issues/292 |
| 679 if (!event.bubbles) return; | 668 if (!event.bubbles) return; |
| 680 | 669 |
| 681 bool log = _eventsLog.isLoggable(Level.INFO); | 670 bool log = _eventsLog.isLoggable(Level.INFO); |
| 682 if (log) _eventsLog.info('>>> [$localName]: listenLocal [${event.type}]'); | 671 if (log) _eventsLog.info('>>> [$localName]: listenLocal [${event.type}]'); |
| 683 | 672 |
| 684 final eventOn = '$_EVENT_PREFIX${_eventNameFromType(event.type)}'; | 673 final eventOn = '$_EVENT_PREFIX${_eventNameFromType(event.type)}'; |
| 685 if (event.path == null) { | 674 if (event.path == null) { |
| 686 _listenLocalNoEventPath(host, event, eventOn); | 675 _listenLocalNoEventPath(host, event, eventOn); |
| 687 } else { | 676 } else { |
| 688 _listenLocalEventPath(host, event, eventOn); | 677 _listenLocalEventPath(host, event, eventOn); |
| 689 } | 678 } |
| 690 | 679 |
| 691 if (log) _eventsLog.info('<<< [$localName]: listenLocal [${event.type}]'); | 680 if (log) _eventsLog.info('<<< [$localName]: listenLocal [${event.type}]'); |
| 692 } | 681 } |
| 693 | 682 |
| 694 static void _listenLocalEventPath(Element host, Event event, String eventOn) { | 683 static void _listenLocalEventPath(Polymer host, Event event, String eventOn) { |
| 695 var c = null; | 684 var c = null; |
| 696 for (var target in event.path) { | 685 for (var target in event.path) { |
| 697 // if we hit host, stop | 686 // if we hit host, stop |
| 698 if (identical(target, host)) return; | 687 if (identical(target, host)) return; |
| 699 | 688 |
| 700 // find a controller for the target, unless we already found `host` | 689 // find a controller for the target, unless we already found `host` |
| 701 // as a controller | 690 // as a controller |
| 702 c = identical(c, host) ? c : _findController(target); | 691 c = identical(c, host) ? c : _findController(target); |
| 703 | 692 |
| 704 // if we have a controller, dispatch the event, and stop if the handler | 693 // if we have a controller, dispatch the event, and stop if the handler |
| 705 // returns true | 694 // returns true |
| 706 if (c != null && _handleEvent(c, target, event, eventOn)) { | 695 if (c != null && _handleEvent(c, target, event, eventOn)) { |
| 707 return; | 696 return; |
| 708 } | 697 } |
| 709 } | 698 } |
| 710 } | 699 } |
| 711 | 700 |
| 712 // TODO(sorvell): remove when ShadowDOM polyfill supports event path. | 701 // TODO(sorvell): remove when ShadowDOM polyfill supports event path. |
| 713 // Note that _findController will not return the expected controller when the | 702 // Note that _findController will not return the expected controller when the |
| 714 // event target is a distributed node. This is because we cannot traverse | 703 // event target is a distributed node. This is because we cannot traverse |
| 715 // from a composed node to a node in shadowRoot. | 704 // from a composed node to a node in shadowRoot. |
| 716 // This will be addressed via an event path api | 705 // This will be addressed via an event path api |
| 717 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066 | 706 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066 |
| 718 static void _listenLocalNoEventPath(Element host, Event event, | 707 static void _listenLocalNoEventPath(Polymer host, Event event, |
| 719 String eventOn) { | 708 String eventOn) { |
| 720 | 709 |
| 721 if (_eventsLog.isLoggable(Level.INFO)) { | 710 if (_eventsLog.isLoggable(Level.INFO)) { |
| 722 _eventsLog.info('event.path() not supported for ${event.type}'); | 711 _eventsLog.info('event.path() not supported for ${event.type}'); |
| 723 } | 712 } |
| 724 | 713 |
| 725 var target = event.target; | 714 var target = event.target; |
| 726 var c = null; | 715 var c = null; |
| 727 // if we hit dirt or host, stop | 716 // if we hit dirt or host, stop |
| 728 while (target != null && target != host) { | 717 while (target != null && target != host) { |
| 729 // find a controller for target `t`, unless we already found `host` | 718 // find a controller for target `t`, unless we already found `host` |
| 730 // as a controller | 719 // as a controller |
| 731 c = identical(c, host) ? c : _findController(target); | 720 c = identical(c, host) ? c : _findController(target); |
| 732 | 721 |
| 733 // if we have a controller, dispatch the event, return 'true' if | 722 // if we have a controller, dispatch the event, return 'true' if |
| 734 // handler returns true | 723 // handler returns true |
| 735 if (c != null && _handleEvent(c, target, event, eventOn)) { | 724 if (c != null && _handleEvent(c, target, event, eventOn)) { |
| 736 return; | 725 return; |
| 737 } | 726 } |
| 738 target = target.parent; | 727 target = target.parent; |
| 739 } | 728 } |
| 740 } | 729 } |
| 741 | 730 |
| 742 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot | 731 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot |
| 743 // was created on a PolymerElement. | 732 // was created on a PolymerElement. |
| 744 static Element _findController(Node node) { | 733 static Polymer _findController(Node node) { |
| 745 while (node.parentNode != null) { | 734 while (node.parentNode != null) { |
| 746 node = node.parentNode; | 735 node = node.parentNode; |
| 747 } | 736 } |
| 748 return _shadowHost[node]; | 737 return _shadowHost[node]; |
| 749 } | 738 } |
| 750 | 739 |
| 751 static bool _handleEvent(Element ctrlr, Node node, Event event, | 740 static bool _handleEvent(Polymer ctrlr, Node node, Event event, |
| 752 String eventOn) { | 741 String eventOn) { |
| 753 | 742 |
| 754 // Note: local events are listened only in the shadow root. This dynamic | 743 // Note: local events are listened only in the shadow root. This dynamic |
| 755 // lookup is used to distinguish determine whether the target actually has a | 744 // lookup is used to distinguish determine whether the target actually has a |
| 756 // listener, and if so, to determine lazily what's the target method. | 745 // listener, and if so, to determine lazily what's the target method. |
| 757 var name = node is Element ? (node as Element).attributes[eventOn] : null; | 746 var name = node is Element ? (node as Element).attributes[eventOn] : null; |
| 758 if (name != null && _handleIfNotHandled(node, event)) { | 747 if (name != null && _handleIfNotHandled(node, event)) { |
| 759 if (_eventsLog.isLoggable(Level.INFO)) { | 748 if (_eventsLog.isLoggable(Level.INFO)) { |
| 760 _eventsLog.info('[${ctrlr.localName}] found handler name [$name]'); | 749 _eventsLog.info('[${ctrlr.localName}] found handler name [$name]'); |
| 761 } | 750 } |
| 762 var detail = event is CustomEvent ? | 751 var detail = event is CustomEvent ? |
| 763 (event as CustomEvent).detail : null; | 752 (event as CustomEvent).detail : null; |
| 764 | 753 |
| 765 if (node != null) { | 754 if (node != null) { |
| 766 // TODO(jmesserly): cache symbols? | 755 // TODO(jmesserly): cache symbols? |
| 767 ctrlr.xtag.dispatchMethod(new Symbol(name), [event, detail, node]); | 756 ctrlr.dispatchMethod(new Symbol(name), [event, detail, node]); |
| 768 } | 757 } |
| 769 } | 758 } |
| 770 | 759 |
| 771 // TODO(jmesserly): do we need this? It was using cancelBubble, see: | 760 // TODO(jmesserly): do we need this? It was using cancelBubble, see: |
| 772 // https://github.com/Polymer/polymer/issues/292 | 761 // https://github.com/Polymer/polymer/issues/292 |
| 773 return !event.bubbles; | 762 return !event.bubbles; |
| 774 } | 763 } |
| 775 | 764 |
| 776 // TODO(jmesserly): I don't understand this bit. It seems to be a duplicate | 765 // TODO(jmesserly): I don't understand this bit. It seems to be a duplicate |
| 777 // delivery prevention mechanism? | 766 // delivery prevention mechanism? |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 918 } | 907 } |
| 919 } | 908 } |
| 920 return type; | 909 return type; |
| 921 } | 910 } |
| 922 | 911 |
| 923 final Logger _observeLog = new Logger('polymer.observe'); | 912 final Logger _observeLog = new Logger('polymer.observe'); |
| 924 final Logger _eventsLog = new Logger('polymer.events'); | 913 final Logger _eventsLog = new Logger('polymer.events'); |
| 925 final Logger _unbindLog = new Logger('polymer.unbind'); | 914 final Logger _unbindLog = new Logger('polymer.unbind'); |
| 926 final Logger _bindLog = new Logger('polymer.bind'); | 915 final Logger _bindLog = new Logger('polymer.bind'); |
| 927 | 916 |
| 928 final Expando _shadowHost = new Expando<Element>(); | 917 final Expando _shadowHost = new Expando<Polymer>(); |
| 929 | 918 |
| 930 final Expando _eventHandledTable = new Expando<Set<Node>>(); | 919 final Expando _eventHandledTable = new Expando<Set<Node>>(); |
| 920 | |
| 921 /** | |
| 922 * Base class for PolymerElements deriving from HtmlElement. | |
| 923 * | |
| 924 * See [Polymer]. | |
| 925 */ | |
| 926 class PolymerElement extends HtmlElement with Polymer, ObservableMixin { | |
| 927 factory PolymerElement() => null; | |
|
Jennifer Messerly
2013/10/10 22:35:17
throw?
blois
2013/10/11 22:40:12
Should actually be removed, no longer needed with
| |
| 928 | |
| 929 PolymerElement.created() : super.created() { | |
| 930 initialize(); | |
| 931 } | |
| 932 } | |
| OLD | NEW |