| 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 /// 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: | 8 /// |
| 9 * | 9 /// class MyPlaybackElement extends PolymerElement { |
| 10 * class MyPlaybackElement extends PolymerElement { | 10 /// // This will be available as an HTML attribute, for example: |
| 11 * // This will be available as an HTML attribute, for example: | 11 /// // <my-playback volume="11"> |
| 12 * // <my-playback volume="11"> | 12 /// @published double volume; |
| 13 * @published double volume; | 13 /// } |
| 14 * } | |
| 15 */ | |
| 16 const published = const PublishedProperty(); | 14 const published = const PublishedProperty(); |
| 17 | 15 |
| 18 /** An annotation used to publish a field as an attribute. See [published]. */ | 16 /// An annotation used to publish a field as an attribute. See [published]. |
| 19 class PublishedProperty extends ObservableProperty { | 17 class PublishedProperty extends ObservableProperty { |
| 20 const PublishedProperty(); | 18 const PublishedProperty(); |
| 21 } | 19 } |
| 22 | 20 |
| 23 /** | 21 /// Use this type to observe a property and have the method be called when it |
| 24 * Use this type to observe a property and have the method be called when it | 22 /// changes. For example: |
| 25 * changes. For example: | 23 /// |
| 26 * | 24 /// @ObserveProperty('foo.bar baz qux') |
| 27 * @ObserveProperty('foo.bar baz qux') | 25 /// validate() { |
| 28 * validate() { | 26 /// // use this.foo.bar, this.baz, and this.qux in validation |
| 29 * // use this.foo.bar, this.baz, and this.qux in validation | 27 /// ... |
| 30 * ... | 28 /// } |
| 31 * } | 29 /// |
| 32 * | 30 /// Note that you can observe a property path, and more than a single property |
| 33 * Note that you can observe a property path, and more than a single property | 31 /// can be specified in a space-delimited list or as a constant List. |
| 34 * can be specified in a space-delimited list or as a constant List. | |
| 35 */ | |
| 36 class ObserveProperty { | 32 class ObserveProperty { |
| 37 final _names; | 33 final _names; |
| 38 | 34 |
| 39 List<String> get names { | 35 List<String> get names { |
| 40 var n = _names; | 36 var n = _names; |
| 41 // TODO(jmesserly): the bogus '$n' is to workaround a dart2js bug, otherwise | 37 // TODO(jmesserly): the bogus '$n' is to workaround a dart2js bug, otherwise |
| 42 // it generates an incorrect call site. | 38 // it generates an incorrect call site. |
| 43 if (n is String) return '$n'.split(' '); | 39 if (n is String) return '$n'.split(' '); |
| 44 if (n is! Iterable) { | 40 if (n is! Iterable) { |
| 45 throw new UnsupportedError('ObserveProperty takes either an Iterable of ' | 41 throw new UnsupportedError('ObserveProperty takes either an Iterable of ' |
| 46 'names, or a space separated String, instead of `$n`.'); | 42 'names, or a space separated String, instead of `$n`.'); |
| 47 } | 43 } |
| 48 return n; | 44 return n; |
| 49 } | 45 } |
| 50 | 46 |
| 51 const ObserveProperty(this._names); | 47 const ObserveProperty(this._names); |
| 52 } | 48 } |
| 53 | 49 |
| 54 /** | 50 /// The mixin class for Polymer elements. It provides convenience features on |
| 55 * The mixin class for Polymer elements. It provides convenience features on top | 51 /// top of the custom elements web standard. |
| 56 * of the custom elements web standard. | 52 /// |
| 57 * | 53 /// If this class is used as a mixin, |
| 58 * If this class is used as a mixin, | 54 /// you must call `polymerCreated()` from the body of your constructor. |
| 59 * you must call `polymerCreated()` from the body of your constructor. | |
| 60 */ | |
| 61 abstract class Polymer implements Element, Observable, NodeBindExtension { | 55 abstract class Polymer implements Element, Observable, NodeBindExtension { |
| 62 // Fully ported from revision: | 56 // Fully ported from revision: |
| 63 // https://github.com/Polymer/polymer/blob/37eea00e13b9f86ab21c85a955585e8e423
7e3d2 | 57 // https://github.com/Polymer/polymer/blob/37eea00e13b9f86ab21c85a955585e8e423
7e3d2 |
| 64 // | 58 // |
| 65 // src/boot.js (static APIs on "Polymer" object) | 59 // src/boot.js (static APIs on "Polymer" object) |
| 66 // src/instance/attributes.js | 60 // src/instance/attributes.js |
| 67 // src/instance/base.js | 61 // src/instance/base.js |
| 68 // src/instance/events.js | 62 // src/instance/events.js |
| 69 // src/instance/mdv.js | 63 // src/instance/mdv.js |
| 70 // src/instance/properties.js | 64 // src/instance/properties.js |
| 71 // src/instance/style.js | 65 // src/instance/style.js |
| 72 // src/instance/utils.js | 66 // src/instance/utils.js |
| 73 | 67 |
| 74 // TODO(jmesserly): should this really be public? | 68 // TODO(jmesserly): should this really be public? |
| 75 /** Regular expression that matches data-bindings. */ | 69 /// Regular expression that matches data-bindings. |
| 76 static final bindPattern = new RegExp(r'\{\{([^{}]*)}}'); | 70 static final bindPattern = new RegExp(r'\{\{([^{}]*)}}'); |
| 77 | 71 |
| 78 /** | 72 /// Like [document.register] but for Polymer elements. |
| 79 * Like [document.register] but for Polymer elements. | 73 /// |
| 80 * | 74 /// Use the [name] to specify custom elment's tag name, for example: |
| 81 * Use the [name] to specify custom elment's tag name, for example: | 75 /// "fancy-button" if the tag is used as `<fancy-button>`. |
| 82 * "fancy-button" if the tag is used as `<fancy-button>`. | 76 /// |
| 83 * | 77 /// The [type] is the type to construct. If not supplied, it defaults to |
| 84 * The [type] is the type to construct. If not supplied, it defaults to | 78 /// [PolymerElement]. |
| 85 * [PolymerElement]. | |
| 86 */ | |
| 87 // NOTE: this is called "element" in src/declaration/polymer-element.js, and | 79 // NOTE: this is called "element" in src/declaration/polymer-element.js, and |
| 88 // exported as "Polymer". | 80 // exported as "Polymer". |
| 89 static void register(String name, [Type type]) { | 81 static void register(String name, [Type type]) { |
| 90 //console.log('registering [' + name + ']'); | 82 //console.log('registering [' + name + ']'); |
| 91 if (type == null) type = PolymerElement; | 83 if (type == null) type = PolymerElement; |
| 92 | 84 |
| 93 _typesByName[name] = type; | 85 _typesByName[name] = type; |
| 94 // notify the registrar waiting for 'name', if any | 86 // notify the registrar waiting for 'name', if any |
| 95 _notifyType(name); | 87 _notifyType(name); |
| 96 } | 88 } |
| 97 | 89 |
| 98 /// The one syntax to rule them all. | 90 /// The one syntax to rule them all. |
| 99 static final BindingDelegate _polymerSyntax = | 91 static final BindingDelegate _polymerSyntax = |
| 100 new _PolymerExpressionsWithEventDelegate(); | 92 new _PolymerExpressionsWithEventDelegate(); |
| 101 | 93 |
| 102 static int _preparingElements = 0; | 94 static int _preparingElements = 0; |
| 103 | 95 |
| 104 static final Completer _ready = new Completer(); | 96 static final Completer _ready = new Completer(); |
| 105 | 97 |
| 106 /** | 98 /// Future indicating that the Polymer library has been loaded and is ready |
| 107 * Future indicating that the Polymer library has been loaded and is ready | 99 /// for use. |
| 108 * for use. | |
| 109 */ | |
| 110 static Future get onReady => _ready.future; | 100 static Future get onReady => _ready.future; |
| 111 | 101 |
| 112 PolymerDeclaration _declaration; | 102 PolymerDeclaration _declaration; |
| 113 | 103 |
| 114 /** The most derived `<polymer-element>` declaration for this element. */ | 104 /// The most derived `<polymer-element>` declaration for this element. |
| 115 PolymerDeclaration get declaration => _declaration; | 105 PolymerDeclaration get declaration => _declaration; |
| 116 | 106 |
| 117 Map<String, StreamSubscription> _observers; | 107 Map<String, StreamSubscription> _observers; |
| 118 bool _unbound; // lazy-initialized | 108 bool _unbound; // lazy-initialized |
| 119 _Job _unbindAllJob; | 109 _Job _unbindAllJob; |
| 120 | 110 |
| 121 CompoundObserver _propertyObserver; | 111 CompoundObserver _propertyObserver; |
| 122 | 112 |
| 123 bool get _elementPrepared => _declaration != null; | 113 bool get _elementPrepared => _declaration != null; |
| 124 | 114 |
| 125 bool get applyAuthorStyles => false; | 115 bool get applyAuthorStyles => false; |
| 126 bool get resetStyleInheritance => false; | 116 bool get resetStyleInheritance => false; |
| 127 bool get alwaysPrepare => false; | 117 bool get alwaysPrepare => false; |
| 128 bool get preventDispose => false; | 118 bool get preventDispose => false; |
| 129 | 119 |
| 130 BindingDelegate syntax = _polymerSyntax; | 120 BindingDelegate syntax = _polymerSyntax; |
| 131 | 121 |
| 132 /** | 122 /// Shadow roots created by [parseElement]. See [getShadowRoot]. |
| 133 * Shadow roots created by [parseElement]. See [getShadowRoot]. | |
| 134 */ | |
| 135 final _shadowRoots = new HashMap<String, ShadowRoot>(); | 123 final _shadowRoots = new HashMap<String, ShadowRoot>(); |
| 136 | 124 |
| 137 /** Map of items in the shadow root(s) by their [Element.id]. */ | 125 /// Map of items in the shadow root(s) by their [Element.id]. |
| 138 // TODO(jmesserly): various issues: | 126 // TODO(jmesserly): various issues: |
| 139 // * wrap in UnmodifiableMapView? | 127 // * wrap in UnmodifiableMapView? |
| 140 // * should we have an object that implements noSuchMethod? | 128 // * should we have an object that implements noSuchMethod? |
| 141 // * should the map have a key order (e.g. LinkedHash or SplayTree)? | 129 // * should the map have a key order (e.g. LinkedHash or SplayTree)? |
| 142 // * should this be a live list? Polymer doesn't, maybe due to JS limitations? | 130 // * should this be a live list? Polymer doesn't, maybe due to JS limitations? |
| 143 // Note: this is observable to support $['someId'] being used in templates. | 131 // Note: this is observable to support $['someId'] being used in templates. |
| 144 // The template is stamped before $ is populated, so we need observation if | 132 // The template is stamped before $ is populated, so we need observation if |
| 145 // we want it to be usable in bindings. | 133 // we want it to be usable in bindings. |
| 146 @reflectable final Map<String, Element> $ = | 134 @reflectable final Map<String, Element> $ = |
| 147 new ObservableMap<String, Element>(); | 135 new ObservableMap<String, Element>(); |
| 148 | 136 |
| 149 /** | 137 /// Gets the shadow root associated with the corresponding custom element. |
| 150 * Gets the shadow root associated with the corresponding custom element. | 138 /// |
| 151 * | 139 /// This is identical to [shadowRoot], unless there are multiple levels of |
| 152 * This is identical to [shadowRoot], unless there are multiple levels of | 140 /// inheritance and they each have their own shadow root. For example, |
| 153 * inheritance and they each have their own shadow root. For example, | 141 /// this can happen if the base class and subclass both have `<template>` tags |
| 154 * this can happen if the base class and subclass both have `<template>` tags | 142 /// in their `<polymer-element>` tags. |
| 155 * in their `<polymer-element>` tags. | |
| 156 */ | |
| 157 // TODO(jmesserly): Polymer does not have this feature. Reconcile. | 143 // TODO(jmesserly): Polymer does not have this feature. Reconcile. |
| 158 ShadowRoot getShadowRoot(String customTagName) => _shadowRoots[customTagName]; | 144 ShadowRoot getShadowRoot(String customTagName) => _shadowRoots[customTagName]; |
| 159 | 145 |
| 160 /** | 146 /// If this class is used as a mixin, this method must be called from inside |
| 161 * If this class is used as a mixin, this method must be called from inside | 147 /// of the `created()` constructor. |
| 162 * of the `created()` constructor. | 148 /// |
| 163 * | 149 /// If this class is a superclass, calling `super.created()` is sufficient. |
| 164 * If this class is a superclass, calling `super.created()` is sufficient. | |
| 165 */ | |
| 166 void polymerCreated() { | 150 void polymerCreated() { |
| 167 if (this.ownerDocument.window != null || alwaysPrepare || | 151 if (this.ownerDocument.window != null || alwaysPrepare || |
| 168 _preparingElements > 0) { | 152 _preparingElements > 0) { |
| 169 prepareElement(); | 153 prepareElement(); |
| 170 } | 154 } |
| 171 } | 155 } |
| 172 | 156 |
| 173 /** Retrieves the custom element name by inspecting the host node. */ | 157 /// Retrieves the custom element name by inspecting the host node. |
| 174 String get _customTagName { | 158 String get _customTagName { |
| 175 var isAttr = attributes['is']; | 159 var isAttr = attributes['is']; |
| 176 return (isAttr == null || isAttr == '') ? localName : isAttr; | 160 return (isAttr == null || isAttr == '') ? localName : isAttr; |
| 177 } | 161 } |
| 178 | 162 |
| 179 void prepareElement() { | 163 void prepareElement() { |
| 180 // Dart note: get the _declaration, which also marks _elementPrepared | 164 // Dart note: get the _declaration, which also marks _elementPrepared |
| 181 _declaration = _getDeclaration(_customTagName); | 165 _declaration = _getDeclaration(_customTagName); |
| 182 // do this first so we can observe changes during initialization | 166 // do this first so we can observe changes during initialization |
| 183 observeProperties(); | 167 observeProperties(); |
| 184 // install boilerplate attributes | 168 // install boilerplate attributes |
| 185 copyInstanceAttributes(); | 169 copyInstanceAttributes(); |
| 186 // process input attributes | 170 // process input attributes |
| 187 takeAttributes(); | 171 takeAttributes(); |
| 188 // add event listeners | 172 // add event listeners |
| 189 addHostListeners(); | 173 addHostListeners(); |
| 190 // guarantees that while preparing, any | 174 // guarantees that while preparing, any |
| 191 // sub-elements are also prepared | 175 // sub-elements are also prepared |
| 192 _preparingElements++; | 176 _preparingElements++; |
| 193 // process declarative resources | 177 // process declarative resources |
| 194 parseDeclarations(_declaration); | 178 parseDeclarations(_declaration); |
| 195 // decrement semaphore | 179 // decrement semaphore |
| 196 _preparingElements--; | 180 _preparingElements--; |
| 197 // user entry point | 181 // user entry point |
| 198 ready(); | 182 ready(); |
| 199 } | 183 } |
| 200 | 184 |
| 201 /** Called when [prepareElement] is finished. */ | 185 /// Called when [prepareElement] is finished. |
| 202 void ready() {} | 186 void ready() {} |
| 203 | 187 |
| 204 void enteredView() { | 188 void enteredView() { |
| 205 if (!_elementPrepared) { | 189 if (!_elementPrepared) { |
| 206 prepareElement(); | 190 prepareElement(); |
| 207 } | 191 } |
| 208 cancelUnbindAll(preventCascade: true); | 192 cancelUnbindAll(preventCascade: true); |
| 209 } | 193 } |
| 210 | 194 |
| 211 void leftView() { | 195 void leftView() { |
| 212 if (!preventDispose) asyncUnbindAll(); | 196 if (!preventDispose) asyncUnbindAll(); |
| 213 } | 197 } |
| 214 | 198 |
| 215 /** Recursive ancestral <element> initialization, oldest first. */ | 199 /// Recursive ancestral <element> initialization, oldest first. |
| 216 void parseDeclarations(PolymerDeclaration declaration) { | 200 void parseDeclarations(PolymerDeclaration declaration) { |
| 217 if (declaration != null) { | 201 if (declaration != null) { |
| 218 parseDeclarations(declaration.superDeclaration); | 202 parseDeclarations(declaration.superDeclaration); |
| 219 parseDeclaration(declaration); | 203 parseDeclaration(declaration); |
| 220 } | 204 } |
| 221 } | 205 } |
| 222 | 206 |
| 223 /** | 207 /// Parse input `<polymer-element>` as needed, override for custom behavior. |
| 224 * Parse input `<polymer-element>` as needed, override for custom behavior. | |
| 225 */ | |
| 226 void parseDeclaration(Element elementElement) { | 208 void parseDeclaration(Element elementElement) { |
| 227 var template = fetchTemplate(elementElement); | 209 var template = fetchTemplate(elementElement); |
| 228 | 210 |
| 229 var root = null; | 211 var root = null; |
| 230 if (template != null) { | 212 if (template != null) { |
| 231 if (_declaration.attributes.containsKey('lightdom')) { | 213 if (_declaration.attributes.containsKey('lightdom')) { |
| 232 lightFromTemplate(template); | 214 lightFromTemplate(template); |
| 233 } else { | 215 } else { |
| 234 root = shadowFromTemplate(template); | 216 root = shadowFromTemplate(template); |
| 235 } | 217 } |
| 236 } | 218 } |
| 237 | 219 |
| 238 // Dart note: the following code is to support the getShadowRoot method. | 220 // Dart note: the following code is to support the getShadowRoot method. |
| 239 if (root is! ShadowRoot) return; | 221 if (root is! ShadowRoot) return; |
| 240 | 222 |
| 241 var name = elementElement.attributes['name']; | 223 var name = elementElement.attributes['name']; |
| 242 if (name == null) return; | 224 if (name == null) return; |
| 243 _shadowRoots[name] = root; | 225 _shadowRoots[name] = root; |
| 244 } | 226 } |
| 245 | 227 |
| 246 /** | 228 /// Return a shadow-root template (if desired), override for custom behavior. |
| 247 * Return a shadow-root template (if desired), override for custom behavior. | |
| 248 */ | |
| 249 Element fetchTemplate(Element elementElement) => | 229 Element fetchTemplate(Element elementElement) => |
| 250 elementElement.querySelector('template'); | 230 elementElement.querySelector('template'); |
| 251 | 231 |
| 252 /** | 232 /// Utility function that stamps a `<template>` into light-dom. |
| 253 * Utility function that stamps a `<template>` into light-dom. | |
| 254 */ | |
| 255 Node lightFromTemplate(Element template) { | 233 Node lightFromTemplate(Element template) { |
| 256 if (template == null) return null; | 234 if (template == null) return null; |
| 257 // stamp template | 235 // stamp template |
| 258 // which includes parsing and applying MDV bindings before being | 236 // which includes parsing and applying MDV bindings before being |
| 259 // inserted (to avoid {{}} in attribute values) | 237 // inserted (to avoid {{}} in attribute values) |
| 260 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. | 238 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. |
| 261 var dom = instanceTemplate(template); | 239 var dom = instanceTemplate(template); |
| 262 // append to shadow dom | 240 // append to shadow dom |
| 263 append(dom); | 241 append(dom); |
| 264 // perform post-construction initialization tasks on shadow root | 242 // perform post-construction initialization tasks on shadow root |
| 265 shadowRootReady(this, template); | 243 shadowRootReady(this, template); |
| 266 // return the created shadow root | 244 // return the created shadow root |
| 267 return dom; | 245 return dom; |
| 268 } | 246 } |
| 269 | 247 |
| 270 /** | 248 /// Utility function that creates a shadow root from a `<template>`. |
| 271 * Utility function that creates a shadow root from a `<template>`. | 249 /// |
| 272 * | 250 /// The base implementation will return a [ShadowRoot], but you can replace it |
| 273 * The base implementation will return a [ShadowRoot], but you can replace it | 251 /// with your own code and skip ShadowRoot creation. In that case, you should |
| 274 * with your own code and skip ShadowRoot creation. In that case, you should | 252 /// return `null`. |
| 275 * return `null`. | 253 /// |
| 276 * | 254 /// In your overridden method, you can use [instanceTemplate] to stamp the |
| 277 * In your overridden method, you can use [instanceTemplate] to stamp the | 255 /// template and initialize data binding, and [shadowRootReady] to intialize |
| 278 * template and initialize data binding, and [shadowRootReady] to intialize | 256 /// other Polymer features like event handlers. It is fine to call |
| 279 * other Polymer features like event handlers. It is fine to call | 257 /// shadowRootReady with a node other than a ShadowRoot such as with `this`. |
| 280 * shadowRootReady with a node something other than a ShadowRoot; for example, | |
| 281 * with this Node. | |
| 282 */ | |
| 283 ShadowRoot shadowFromTemplate(Element template) { | 258 ShadowRoot shadowFromTemplate(Element template) { |
| 284 if (template == null) return null; | 259 if (template == null) return null; |
| 285 // cache elder shadow root (if any) | 260 // cache elder shadow root (if any) |
| 286 var elderRoot = this.shadowRoot; | 261 var elderRoot = this.shadowRoot; |
| 287 // make a shadow root | 262 // make a shadow root |
| 288 var root = createShadowRoot(); | 263 var root = createShadowRoot(); |
| 289 | 264 |
| 290 // Provides ability to traverse from ShadowRoot to the host. | 265 // Provides ability to traverse from ShadowRoot to the host. |
| 291 // TODO(jmessery): remove once we have this ability on the DOM. | 266 // TODO(jmessery): remove once we have this ability on the DOM. |
| 292 _shadowHost[root] = this; | 267 _shadowHost[root] = this; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 308 } | 283 } |
| 309 | 284 |
| 310 void shadowRootReady(Node root, Element template) { | 285 void shadowRootReady(Node root, Element template) { |
| 311 // locate nodes with id and store references to them in this.$ hash | 286 // locate nodes with id and store references to them in this.$ hash |
| 312 marshalNodeReferences(root); | 287 marshalNodeReferences(root); |
| 313 // TODO(jmesserly): port this | 288 // TODO(jmesserly): port this |
| 314 // set up pointer gestures | 289 // set up pointer gestures |
| 315 // PointerGestures.register(root); | 290 // PointerGestures.register(root); |
| 316 } | 291 } |
| 317 | 292 |
| 318 /** Locate nodes with id and store references to them in [$] hash. */ | 293 /// Locate nodes with id and store references to them in [$] hash. |
| 319 void marshalNodeReferences(Node root) { | 294 void marshalNodeReferences(Node root) { |
| 320 if (root == null) return; | 295 if (root == null) return; |
| 321 for (var n in (root as dynamic).querySelectorAll('[id]')) { | 296 for (var n in (root as dynamic).querySelectorAll('[id]')) { |
| 322 $[n.id] = n; | 297 $[n.id] = n; |
| 323 } | 298 } |
| 324 } | 299 } |
| 325 | 300 |
| 326 void attributeChanged(String name, String oldValue, String newValue) { | 301 void attributeChanged(String name, String oldValue, String newValue) { |
| 327 if (name != 'class' && name != 'style') { | 302 if (name != 'class' && name != 'style') { |
| 328 attributeToProperty(name, newValue); | 303 attributeToProperty(name, newValue); |
| 329 } | 304 } |
| 330 } | 305 } |
| 331 | 306 |
| 332 // TODO(jmesserly): this could be a top level method. | 307 // TODO(jmesserly): this could be a top level method. |
| 333 /** | 308 /// Returns a future when `node` changes, or when its children or subtree |
| 334 * Returns a future when `node` changes, or when its children or subtree | 309 /// changes. |
| 335 * changes. | 310 /// |
| 336 * | 311 /// Use [MutationObserver] if you want to listen to a stream of changes. |
| 337 * Use [MutationObserver] if you want to listen to a stream of changes. | |
| 338 */ | |
| 339 Future<List<MutationRecord>> onMutation(Node node) { | 312 Future<List<MutationRecord>> onMutation(Node node) { |
| 340 var completer = new Completer(); | 313 var completer = new Completer(); |
| 341 new MutationObserver((mutations, observer) { | 314 new MutationObserver((mutations, observer) { |
| 342 observer.disconnect(); | 315 observer.disconnect(); |
| 343 completer.complete(mutations); | 316 completer.complete(mutations); |
| 344 })..observe(node, childList: true, subtree: true); | 317 })..observe(node, childList: true, subtree: true); |
| 345 return completer.future; | 318 return completer.future; |
| 346 } | 319 } |
| 347 | 320 |
| 348 void copyInstanceAttributes() { | 321 void copyInstanceAttributes() { |
| 349 _declaration._instanceAttributes.forEach((name, value) { | 322 _declaration._instanceAttributes.forEach((name, value) { |
| 350 attributes.putIfAbsent(name, () => value); | 323 attributes.putIfAbsent(name, () => value); |
| 351 }); | 324 }); |
| 352 } | 325 } |
| 353 | 326 |
| 354 void takeAttributes() { | 327 void takeAttributes() { |
| 355 if (_declaration._publishLC == null) return; | 328 if (_declaration._publishLC == null) return; |
| 356 attributes.forEach(attributeToProperty); | 329 attributes.forEach(attributeToProperty); |
| 357 } | 330 } |
| 358 | 331 |
| 359 /** | 332 /// If attribute [name] is mapped to a property, deserialize |
| 360 * If attribute [name] is mapped to a property, deserialize | 333 /// [value] into that property. |
| 361 * [value] into that property. | |
| 362 */ | |
| 363 void attributeToProperty(String name, String value) { | 334 void attributeToProperty(String name, String value) { |
| 364 // try to match this attribute to a property (attributes are | 335 // try to match this attribute to a property (attributes are |
| 365 // all lower-case, so this is case-insensitive search) | 336 // all lower-case, so this is case-insensitive search) |
| 366 var decl = propertyForAttribute(name); | 337 var decl = propertyForAttribute(name); |
| 367 if (decl == null) return; | 338 if (decl == null) return; |
| 368 | 339 |
| 369 // filter out 'mustached' values, these are to be | 340 // filter out 'mustached' values, these are to be |
| 370 // replaced with bound-data and are not yet values | 341 // replaced with bound-data and are not yet values |
| 371 // themselves. | 342 // themselves. |
| 372 if (value == null || value.contains(Polymer.bindPattern)) return; | 343 if (value == null || value.contains(Polymer.bindPattern)) return; |
| 373 | 344 |
| 374 final currentValue = smoke.read(this, decl.name); | 345 final currentValue = smoke.read(this, decl.name); |
| 375 | 346 |
| 376 // deserialize Boolean or Number values from attribute | 347 // deserialize Boolean or Number values from attribute |
| 377 var type = decl.type; | 348 var type = decl.type; |
| 378 if ((type == Object || type == dynamic) && currentValue != null) { | 349 if ((type == Object || type == dynamic) && currentValue != null) { |
| 379 // Attempt to infer field type from the current value. | 350 // Attempt to infer field type from the current value. |
| 380 type = currentValue.runtimeType; | 351 type = currentValue.runtimeType; |
| 381 } | 352 } |
| 382 final newValue = deserializeValue(value, currentValue, type); | 353 final newValue = deserializeValue(value, currentValue, type); |
| 383 | 354 |
| 384 // only act if the value has changed | 355 // only act if the value has changed |
| 385 if (!identical(newValue, currentValue)) { | 356 if (!identical(newValue, currentValue)) { |
| 386 // install new value (has side-effects) | 357 // install new value (has side-effects) |
| 387 smoke.write(this, decl.name, newValue); | 358 smoke.write(this, decl.name, newValue); |
| 388 } | 359 } |
| 389 } | 360 } |
| 390 | 361 |
| 391 /** Return the published property matching name, or null. */ | 362 /// Return the published property matching name, or null. |
| 392 // TODO(jmesserly): should we just return Symbol here? | 363 // TODO(jmesserly): should we just return Symbol here? |
| 393 smoke.Declaration propertyForAttribute(String name) { | 364 smoke.Declaration propertyForAttribute(String name) { |
| 394 final publishLC = _declaration._publishLC; | 365 final publishLC = _declaration._publishLC; |
| 395 if (publishLC == null) return null; | 366 if (publishLC == null) return null; |
| 396 //console.log('propertyForAttribute:', name, 'matches', match); | 367 //console.log('propertyForAttribute:', name, 'matches', match); |
| 397 return publishLC[name]; | 368 return publishLC[name]; |
| 398 } | 369 } |
| 399 | 370 |
| 400 /** | 371 /// Convert representation of [value] based on [type] and [currentValue]. |
| 401 * Convert representation of [value] based on [type] and [currentValue]. | |
| 402 */ | |
| 403 Object deserializeValue(String value, Object currentValue, Type type) => | 372 Object deserializeValue(String value, Object currentValue, Type type) => |
| 404 deserialize.deserializeValue(value, currentValue, type); | 373 deserialize.deserializeValue(value, currentValue, type); |
| 405 | 374 |
| 406 String serializeValue(Object value) { | 375 String serializeValue(Object value) { |
| 407 if (value == null) return null; | 376 if (value == null) return null; |
| 408 | 377 |
| 409 if (value is bool) { | 378 if (value is bool) { |
| 410 return _toBoolean(value) ? '' : null; | 379 return _toBoolean(value) ? '' : null; |
| 411 } else if (value is String || value is num) { | 380 } else if (value is String || value is num) { |
| 412 return '$value'; | 381 return '$value'; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 427 // TODO(sorvell): we should remove attr for all properties | 396 // TODO(sorvell): we should remove attr for all properties |
| 428 // that have undefined serialization; however, we will need to | 397 // that have undefined serialization; however, we will need to |
| 429 // refine the attr reflection system to achieve this; pica, for example, | 398 // refine the attr reflection system to achieve this; pica, for example, |
| 430 // relies on having inferredType object properties not removed as | 399 // relies on having inferredType object properties not removed as |
| 431 // attrs. | 400 // attrs. |
| 432 } else if (propValue is bool) { | 401 } else if (propValue is bool) { |
| 433 attributes.remove('$path'); | 402 attributes.remove('$path'); |
| 434 } | 403 } |
| 435 } | 404 } |
| 436 | 405 |
| 437 /** | 406 /// Creates the document fragment to use for each instance of the custom |
| 438 * Creates the document fragment to use for each instance of the custom | 407 /// element, given the `<template>` node. By default this is equivalent to: |
| 439 * element, given the `<template>` node. By default this is equivalent to: | 408 /// |
| 440 * | 409 /// templateBind(template).createInstance(this, polymerSyntax); |
| 441 * templateBind(template).createInstance(this, polymerSyntax); | 410 /// |
| 442 * | 411 /// Where polymerSyntax is a singleton `PolymerExpressions` instance from the |
| 443 * Where polymerSyntax is a singleton `PolymerExpressions` instance from the | 412 /// [polymer_expressions](https://pub.dartlang.org/packages/polymer_expression
s) |
| 444 * [polymer_expressions](https://pub.dartlang.org/packages/polymer_expressions
) | 413 /// package. |
| 445 * package. | 414 /// |
| 446 * | 415 /// You can override this method to change the instantiation behavior of the |
| 447 * You can override this method to change the instantiation behavior of the | 416 /// template, for example to use a different data-binding syntax. |
| 448 * template, for example to use a different data-binding syntax. | |
| 449 */ | |
| 450 DocumentFragment instanceTemplate(Element template) => | 417 DocumentFragment instanceTemplate(Element template) => |
| 451 templateBind(template).createInstance(this, syntax); | 418 templateBind(template).createInstance(this, syntax); |
| 452 | 419 |
| 453 // TODO(jmesserly): Polymer does not seem to implement the oneTime flag | 420 // TODO(jmesserly): Polymer does not seem to implement the oneTime flag |
| 454 // correctly. File bug. | 421 // correctly. File bug. |
| 455 Bindable bind(String name, Bindable bindable, {bool oneTime: false}) { | 422 Bindable bind(String name, Bindable bindable, {bool oneTime: false}) { |
| 456 // note: binding is a prepare signal. This allows us to be sure that any | 423 // note: binding is a prepare signal. This allows us to be sure that any |
| 457 // property changes that occur as a result of binding will be observed. | 424 // property changes that occur as a result of binding will be observed. |
| 458 if (!_elementPrepared) prepareElement(); | 425 if (!_elementPrepared) prepareElement(); |
| 459 | 426 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 | 501 |
| 535 static void _forNodeTree(Node node, void callback(Node node)) { | 502 static void _forNodeTree(Node node, void callback(Node node)) { |
| 536 if (node == null) return; | 503 if (node == null) return; |
| 537 | 504 |
| 538 callback(node); | 505 callback(node); |
| 539 for (var child = node.firstChild; child != null; child = child.nextNode) { | 506 for (var child = node.firstChild; child != null; child = child.nextNode) { |
| 540 _forNodeTree(child, callback); | 507 _forNodeTree(child, callback); |
| 541 } | 508 } |
| 542 } | 509 } |
| 543 | 510 |
| 544 /** Set up property observers. */ | 511 /// Set up property observers. |
| 545 void observeProperties() { | 512 void observeProperties() { |
| 546 final observe = _declaration._observe; | 513 final observe = _declaration._observe; |
| 547 final publish = _declaration._publish; | 514 final publish = _declaration._publish; |
| 548 | 515 |
| 549 // TODO(jmesserly): workaround for a dart2js compiler bug | 516 // TODO(jmesserly): workaround for a dart2js compiler bug |
| 550 bool hasObserved = observe != null; | 517 bool hasObserved = observe != null; |
| 551 | 518 |
| 552 if (hasObserved || publish != null) { | 519 if (hasObserved || publish != null) { |
| 553 var o = _propertyObserver = new CompoundObserver(); | 520 var o = _propertyObserver = new CompoundObserver(); |
| 554 if (hasObserved) { | 521 if (hasObserved) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 566 if (!hasObserved || !observe.containsKey(path)) { | 533 if (!hasObserved || !observe.containsKey(path)) { |
| 567 o.addPath(this, path); | 534 o.addPath(this, path); |
| 568 } | 535 } |
| 569 } | 536 } |
| 570 } | 537 } |
| 571 o.open(notifyPropertyChanges); | 538 o.open(notifyPropertyChanges); |
| 572 } | 539 } |
| 573 } | 540 } |
| 574 | 541 |
| 575 | 542 |
| 576 /** Responds to property changes on this element. */ | 543 /// Responds to property changes on this element. |
| 577 void notifyPropertyChanges(List newValues, Map oldValues, List paths) { | 544 void notifyPropertyChanges(List newValues, Map oldValues, List paths) { |
| 578 final observe = _declaration._observe; | 545 final observe = _declaration._observe; |
| 579 final publish = _declaration._publish; | 546 final publish = _declaration._publish; |
| 580 final called = new HashSet(); | 547 final called = new HashSet(); |
| 581 | 548 |
| 582 oldValues.forEach((i, oldValue) { | 549 oldValues.forEach((i, oldValue) { |
| 583 // note: paths is of form [object, path, object, path] | 550 // note: paths is of form [object, path, object, path] |
| 584 var path = paths[2 * i + 1]; | 551 var path = paths[2 * i + 1]; |
| 585 if (publish != null && publish.containsKey(path)) { | 552 if (publish != null && publish.containsKey(path)) { |
| 586 reflectPropertyToAttribute(path); | 553 reflectPropertyToAttribute(path); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 638 bool unbindProperty(String name) => unregisterObserver(name); | 605 bool unbindProperty(String name) => unregisterObserver(name); |
| 639 | 606 |
| 640 void unbindAllProperties() { | 607 void unbindAllProperties() { |
| 641 if (_propertyObserver != null) { | 608 if (_propertyObserver != null) { |
| 642 _propertyObserver.close(); | 609 _propertyObserver.close(); |
| 643 _propertyObserver = null; | 610 _propertyObserver = null; |
| 644 } | 611 } |
| 645 unregisterObservers(); | 612 unregisterObservers(); |
| 646 } | 613 } |
| 647 | 614 |
| 648 /** Bookkeeping observers for memory management. */ | 615 /// Bookkeeping observers for memory management. |
| 649 void registerObserver(String name, StreamSubscription sub) { | 616 void registerObserver(String name, StreamSubscription sub) { |
| 650 if (_observers == null) { | 617 if (_observers == null) { |
| 651 _observers = new Map<String, StreamSubscription>(); | 618 _observers = new Map<String, StreamSubscription>(); |
| 652 } | 619 } |
| 653 _observers[name] = sub; | 620 _observers[name] = sub; |
| 654 } | 621 } |
| 655 | 622 |
| 656 bool unregisterObserver(String name) { | 623 bool unregisterObserver(String name) { |
| 657 var sub = _observers.remove(name); | 624 var sub = _observers.remove(name); |
| 658 if (sub == null) return false; | 625 if (sub == null) return false; |
| 659 sub.cancel(); | 626 sub.cancel(); |
| 660 return true; | 627 return true; |
| 661 } | 628 } |
| 662 | 629 |
| 663 void unregisterObservers() { | 630 void unregisterObservers() { |
| 664 if (_observers == null) return; | 631 if (_observers == null) return; |
| 665 for (var sub in _observers.values) sub.cancel(); | 632 for (var sub in _observers.values) sub.cancel(); |
| 666 _observers.clear(); | 633 _observers.clear(); |
| 667 _observers = null; | 634 _observers = null; |
| 668 } | 635 } |
| 669 | 636 |
| 670 /** | 637 /// Bind a [property] in this object to a [path] in model. *Note* in Dart it |
| 671 * Bind a [property] in this object to a [path] in model. *Note* in Dart it | 638 /// is necessary to also define the field: |
| 672 * is necessary to also define the field: | 639 /// |
| 673 * | 640 /// var myProperty; |
| 674 * var myProperty; | 641 /// |
| 675 * | 642 /// ready() { |
| 676 * ready() { | 643 /// super.ready(); |
| 677 * super.ready(); | 644 /// bindProperty(#myProperty, this, 'myModel.path.to.otherProp'); |
| 678 * bindProperty(#myProperty, this, 'myModel.path.to.otherProp'); | 645 /// } |
| 679 * } | |
| 680 */ | |
| 681 Bindable bindProperty(Symbol name, Bindable bindable) { | 646 Bindable bindProperty(Symbol name, Bindable bindable) { |
| 682 // Dart note: normally we only reach this code when we know it's a | 647 // Dart note: normally we only reach this code when we know it's a |
| 683 // property, but if someone uses bindProperty directly they might get a | 648 // property, but if someone uses bindProperty directly they might get a |
| 684 // NoSuchMethodError either from the getField below, or from the setField | 649 // NoSuchMethodError either from the getField below, or from the setField |
| 685 // inside PolymerBinding. That doesn't seem unreasonable, but it's a slight | 650 // inside PolymerBinding. That doesn't seem unreasonable, but it's a slight |
| 686 // difference from Polymer.js behavior. | 651 // difference from Polymer.js behavior. |
| 687 | 652 |
| 688 if (_bindLog.isLoggable(Level.FINE)) { | 653 if (_bindLog.isLoggable(Level.FINE)) { |
| 689 _bindLog.fine('bindProperty: [$bindable] to [${localName}].[name]'); | 654 _bindLog.fine('bindProperty: [$bindable] to [${localName}].[name]'); |
| 690 } | 655 } |
| 691 | 656 |
| 692 // capture A's value if B's value is null or undefined, | 657 // capture A's value if B's value is null or undefined, |
| 693 // otherwise use B's value | 658 // otherwise use B's value |
| 694 // TODO(sorvell): need to review, can do with ObserverTransform | 659 // TODO(sorvell): need to review, can do with ObserverTransform |
| 695 var v = bindable.value; | 660 var v = bindable.value; |
| 696 if (v == null) { | 661 if (v == null) { |
| 697 bindable.value = reflect(this).getField(name).reflectee; | 662 bindable.value = reflect(this).getField(name).reflectee; |
| 698 } | 663 } |
| 699 | 664 |
| 700 // TODO(jmesserly): this will create another subscription. | 665 // TODO(jmesserly): this will create another subscription. |
| 701 // It would be nice to have this reuse our existing _propertyObserver | 666 // It would be nice to have this reuse our existing _propertyObserver |
| 702 // created by observeProperties, to avoid more observation overhead. | 667 // created by observeProperties, to avoid more observation overhead. |
| 703 return new _PolymerBinding(this, name, bindable); | 668 return new _PolymerBinding(this, name, bindable); |
| 704 } | 669 } |
| 705 | 670 |
| 706 /** Attach event listeners on the host (this) element. */ | 671 /// Attach event listeners on the host (this) element. |
| 707 void addHostListeners() { | 672 void addHostListeners() { |
| 708 var events = _declaration._eventDelegates; | 673 var events = _declaration._eventDelegates; |
| 709 if (events.isEmpty) return; | 674 if (events.isEmpty) return; |
| 710 | 675 |
| 711 if (_eventsLog.isLoggable(Level.FINE)) { | 676 if (_eventsLog.isLoggable(Level.FINE)) { |
| 712 _eventsLog.fine('[$localName] addHostListeners: $events'); | 677 _eventsLog.fine('[$localName] addHostListeners: $events'); |
| 713 } | 678 } |
| 714 addNodeListeners(this, events.keys, hostEventListener); | 679 addNodeListeners(this, events.keys, hostEventListener); |
| 715 } | 680 } |
| 716 | 681 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 745 } | 710 } |
| 746 | 711 |
| 747 if (log) { | 712 if (log) { |
| 748 _eventsLog.fine('<<< [$localName]: hostEventListener(${event.type})'); | 713 _eventsLog.fine('<<< [$localName]: hostEventListener(${event.type})'); |
| 749 } | 714 } |
| 750 } | 715 } |
| 751 | 716 |
| 752 String findEventDelegate(Event event) => | 717 String findEventDelegate(Event event) => |
| 753 _declaration._eventDelegates[_eventNameFromType(event.type)]; | 718 _declaration._eventDelegates[_eventNameFromType(event.type)]; |
| 754 | 719 |
| 755 /** | 720 /// Calls [methodOrCallback] with [args] if it is a closure, otherwise, treat |
| 756 * Calls [methodOrCallback] with [args] if it is a closure, otherwise, treat | 721 /// it as a method name in [object], and invoke it. |
| 757 * it as a method name in [object], and invoke it. | |
| 758 */ | |
| 759 void dispatchMethod(object, callbackOrMethod, List args) { | 722 void dispatchMethod(object, callbackOrMethod, List args) { |
| 760 bool log = _eventsLog.isLoggable(Level.FINE); | 723 bool log = _eventsLog.isLoggable(Level.FINE); |
| 761 if (log) _eventsLog.fine('>>> [$localName]: dispatch $callbackOrMethod'); | 724 if (log) _eventsLog.fine('>>> [$localName]: dispatch $callbackOrMethod'); |
| 762 | 725 |
| 763 if (callbackOrMethod is Function) { | 726 if (callbackOrMethod is Function) { |
| 764 int maxArgs = smoke.maxArgs(callbackOrMethod); | 727 int maxArgs = smoke.maxArgs(callbackOrMethod); |
| 765 if (maxArgs == -1) { | 728 if (maxArgs == -1) { |
| 766 _eventsLog.warning( | 729 _eventsLog.warning( |
| 767 'invalid callback: expected callback of 0, 1, 2, or 3 arguments'); | 730 'invalid callback: expected callback of 0, 1, 2, or 3 arguments'); |
| 768 } | 731 } |
| 769 args.length = maxArgs; | 732 args.length = maxArgs; |
| 770 Function.apply(callbackOrMethod, args); | 733 Function.apply(callbackOrMethod, args); |
| 771 } else if (callbackOrMethod is String) { | 734 } else if (callbackOrMethod is String) { |
| 772 smoke.invoke(object, smoke.nameToSymbol(callbackOrMethod), args, | 735 smoke.invoke(object, smoke.nameToSymbol(callbackOrMethod), args, |
| 773 adjust: true); | 736 adjust: true); |
| 774 } else { | 737 } else { |
| 775 _eventsLog.warning('invalid callback'); | 738 _eventsLog.warning('invalid callback'); |
| 776 } | 739 } |
| 777 | 740 |
| 778 if (log) _eventsLog.info('<<< [$localName]: dispatch $callbackOrMethod'); | 741 if (log) _eventsLog.info('<<< [$localName]: dispatch $callbackOrMethod'); |
| 779 } | 742 } |
| 780 | 743 |
| 781 /** | 744 /// Bind events via attributes of the form `on-eventName`. This method can be |
| 782 * Bind events via attributes of the form `on-eventName`. This method can be | 745 /// use to hooks into the model syntax and adds event listeners as needed. By |
| 783 * use to hooks into the model syntax and adds event listeners as needed. By | 746 /// default, binding paths are always method names on the root model, the |
| 784 * default, binding paths are always method names on the root model, the | 747 /// custom element in which the node exists. Adding a '@' in the path directs |
| 785 * custom element in which the node exists. Adding a '@' in the path directs | 748 /// the event binding to use the model path as the event listener. In both |
| 786 * the event binding to use the model path as the event listener. In both | 749 /// cases, the actual listener is attached to a generic method which evaluates |
| 787 * cases, the actual listener is attached to a generic method which evaluates | 750 /// the bound path at event execution time. |
| 788 * the bound path at event execution time. | |
| 789 */ | |
| 790 // from src/instance/event.js#prepareBinding | 751 // from src/instance/event.js#prepareBinding |
| 791 static PrepareBindingFunction prepareBinding(String path, String name, node) { | 752 static PrepareBindingFunction prepareBinding(String path, String name, node) { |
| 792 | 753 |
| 793 // provide an event-binding callback. | 754 // provide an event-binding callback. |
| 794 return (model, node, oneTime) { | 755 return (model, node, oneTime) { |
| 795 if (_eventsLog.isLoggable(Level.FINE)) { | 756 if (_eventsLog.isLoggable(Level.FINE)) { |
| 796 _eventsLog.fine('event: [$node].$name => [$model].$path())'); | 757 _eventsLog.fine('event: [$node].$name => [$model].$path())'); |
| 797 } | 758 } |
| 798 var eventName = _removeEventPrefix(name); | 759 var eventName = _removeEventPrefix(name); |
| 799 // TODO(sigmund): polymer.js dropped event translations. reconcile? | 760 // TODO(sigmund): polymer.js dropped event translations. reconcile? |
| 800 var translated = _eventTranslations[eventName]; | 761 var translated = _eventTranslations[eventName]; |
| 801 eventName = translated != null ? translated : eventName; | 762 eventName = translated != null ? translated : eventName; |
| 802 | 763 |
| 803 return new _EventBindable(node, eventName, model, path); | 764 return new _EventBindable(node, eventName, model, path); |
| 804 }; | 765 }; |
| 805 } | 766 } |
| 806 | 767 |
| 807 /** Call [methodName] method on this object with [args]. */ | 768 /// Call [methodName] method on this object with [args]. |
| 808 invokeMethod(Symbol methodName, List args) => | 769 invokeMethod(Symbol methodName, List args) => |
| 809 smoke.invoke(this, methodName, args, adjust: true); | 770 smoke.invoke(this, methodName, args, adjust: true); |
| 810 | 771 |
| 811 /** | 772 /// Invokes a function asynchronously. |
| 812 * Invokes a function asynchronously. | 773 /// This will call `Platform.flush()` and then return a `new Timer` |
| 813 * This will call `Platform.flush()` and then return a `new Timer` | 774 /// with the provided [method] and [timeout]. |
| 814 * with the provided [method] and [timeout]. | 775 /// |
| 815 * | 776 /// If you would prefer to run the callback using |
| 816 * If you would prefer to run the callback using | 777 /// [window.requestAnimationFrame], see the [async] method. |
| 817 * [window.requestAnimationFrame], see the [async] method. | |
| 818 */ | |
| 819 // Dart note: "async" is split into 2 methods so it can have a sensible type | 778 // Dart note: "async" is split into 2 methods so it can have a sensible type |
| 820 // signatures. Also removed the various features that don't make sense in a | 779 // signatures. Also removed the various features that don't make sense in a |
| 821 // Dart world, like binding to "this" and taking arguments list. | 780 // Dart world, like binding to "this" and taking arguments list. |
| 822 Timer asyncTimer(void method(), Duration timeout) { | 781 Timer asyncTimer(void method(), Duration timeout) { |
| 823 // when polyfilling Object.observe, ensure changes | 782 // when polyfilling Object.observe, ensure changes |
| 824 // propagate before executing the async method | 783 // propagate before executing the async method |
| 825 scheduleMicrotask(Observable.dirtyCheck); | 784 scheduleMicrotask(Observable.dirtyCheck); |
| 826 return new Timer(timeout, method); | 785 return new Timer(timeout, method); |
| 827 } | 786 } |
| 828 | 787 |
| 829 /** | 788 /// Invokes a function asynchronously. |
| 830 * Invokes a function asynchronously. | 789 /// This will call `Platform.flush()` and then call |
| 831 * This will call `Platform.flush()` and then call | 790 /// [window.requestAnimationFrame] with the provided [method] and return the |
| 832 * [window.requestAnimationFrame] with the provided [method] and return the | 791 /// result. |
| 833 * result. | 792 /// |
| 834 * | 793 /// If you would prefer to run the callback after a given duration, see |
| 835 * If you would prefer to run the callback after a given duration, see | 794 /// the [asyncTimer] method. |
| 836 * the [asyncTimer] method. | |
| 837 */ | |
| 838 int async(RequestAnimationFrameCallback method) { | 795 int async(RequestAnimationFrameCallback method) { |
| 839 // when polyfilling Object.observe, ensure changes | 796 // when polyfilling Object.observe, ensure changes |
| 840 // propagate before executing the async method | 797 // propagate before executing the async method |
| 841 scheduleMicrotask(Observable.dirtyCheck); | 798 scheduleMicrotask(Observable.dirtyCheck); |
| 842 return window.requestAnimationFrame(method); | 799 return window.requestAnimationFrame(method); |
| 843 } | 800 } |
| 844 | 801 |
| 845 /** | 802 /// Fire a [CustomEvent] targeting [toNode], or this if toNode is not |
| 846 * Fire a [CustomEvent] targeting [toNode], or this if toNode is not | 803 /// supplied. Returns the [detail] object. |
| 847 * supplied. Returns the [detail] object. | |
| 848 */ | |
| 849 Object fire(String type, {Object detail, Node toNode, bool canBubble}) { | 804 Object fire(String type, {Object detail, Node toNode, bool canBubble}) { |
| 850 var node = toNode != null ? toNode : this; | 805 var node = toNode != null ? toNode : this; |
| 851 //log.events && console.log('[%s]: sending [%s]', node.localName, inType); | 806 //log.events && console.log('[%s]: sending [%s]', node.localName, inType); |
| 852 node.dispatchEvent(new CustomEvent( | 807 node.dispatchEvent(new CustomEvent( |
| 853 type, | 808 type, |
| 854 canBubble: canBubble != null ? canBubble : true, | 809 canBubble: canBubble != null ? canBubble : true, |
| 855 detail: detail | 810 detail: detail |
| 856 )); | 811 )); |
| 857 return detail; | 812 return detail; |
| 858 } | 813 } |
| 859 | 814 |
| 860 /** | 815 /// Fire an event asynchronously. See [async] and [fire]. |
| 861 * Fire an event asynchronously. See [async] and [fire]. | |
| 862 */ | |
| 863 asyncFire(String type, {Object detail, Node toNode, bool canBubble}) { | 816 asyncFire(String type, {Object detail, Node toNode, bool canBubble}) { |
| 864 // TODO(jmesserly): I'm not sure this method adds much in Dart, it's easy to | 817 // TODO(jmesserly): I'm not sure this method adds much in Dart, it's easy to |
| 865 // add "() =>" | 818 // add "() =>" |
| 866 async((x) => fire( | 819 async((x) => fire( |
| 867 type, detail: detail, toNode: toNode, canBubble: canBubble)); | 820 type, detail: detail, toNode: toNode, canBubble: canBubble)); |
| 868 } | 821 } |
| 869 | 822 |
| 870 /** | 823 /// Remove [className] from [old], add class to [anew], if they exist. |
| 871 * Remove [className] from [old], add class to [anew], if they exist. | |
| 872 */ | |
| 873 void classFollows(Element anew, Element old, String className) { | 824 void classFollows(Element anew, Element old, String className) { |
| 874 if (old != null) { | 825 if (old != null) { |
| 875 old.classes.remove(className); | 826 old.classes.remove(className); |
| 876 } | 827 } |
| 877 if (anew != null) { | 828 if (anew != null) { |
| 878 anew.classes.add(className); | 829 anew.classes.add(className); |
| 879 } | 830 } |
| 880 } | 831 } |
| 881 | 832 |
| 882 /** | 833 /// Installs external stylesheets and <style> elements with the attribute |
| 883 * Installs external stylesheets and <style> elements with the attribute | 834 /// polymer-scope='controller' into the scope of element. This is intended |
| 884 * polymer-scope='controller' into the scope of element. This is intended | 835 /// to be a called during custom element construction. Note, this incurs a |
| 885 * to be a called during custom element construction. Note, this incurs a | 836 /// per instance cost and should be used sparingly. |
| 886 * per instance cost and should be used sparingly. | 837 /// |
| 887 * | 838 /// The need for this type of styling should go away when the shadowDOM spec |
| 888 * The need for this type of styling should go away when the shadowDOM spec | 839 /// addresses these issues: |
| 889 * addresses these issues: | 840 /// |
| 890 * | 841 /// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21391 |
| 891 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21391 | 842 /// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21390 |
| 892 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21390 | 843 /// https://www.w3.org/Bugs/Public/show_bug.cgi?id=21389 |
| 893 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21389 | 844 /// |
| 894 * | 845 /// @param element The custom element instance into whose controller (parent) |
| 895 * @param element The custom element instance into whose controller (parent) | 846 /// scope styles will be installed. |
| 896 * scope styles will be installed. | 847 /// @param elementElement The <element> containing controller styles. |
| 897 * @param elementElement The <element> containing controller styles. | |
| 898 */ | |
| 899 // TODO(sorvell): remove when spec issues are addressed | 848 // TODO(sorvell): remove when spec issues are addressed |
| 900 void installControllerStyles() { | 849 void installControllerStyles() { |
| 901 var scope = findStyleController(); | 850 var scope = findStyleController(); |
| 902 if (scope != null && scopeHasElementStyle(scope, _STYLE_CONTROLLER_SCOPE)) { | 851 if (scope != null && scopeHasElementStyle(scope, _STYLE_CONTROLLER_SCOPE)) { |
| 903 // allow inherited controller styles | 852 // allow inherited controller styles |
| 904 var decl = _declaration; | 853 var decl = _declaration; |
| 905 var cssText = new StringBuffer(); | 854 var cssText = new StringBuffer(); |
| 906 while (decl != null) { | 855 while (decl != null) { |
| 907 cssText.write(decl.cssTextForScope(_STYLE_CONTROLLER_SCOPE)); | 856 cssText.write(decl.cssTextForScope(_STYLE_CONTROLLER_SCOPE)); |
| 908 decl = decl.superDeclaration; | 857 decl = decl.superDeclaration; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 946 var clone = new StyleElement()..text = style.text; | 895 var clone = new StyleElement()..text = style.text; |
| 947 | 896 |
| 948 var attr = style.attributes[_STYLE_SCOPE_ATTRIBUTE]; | 897 var attr = style.attributes[_STYLE_SCOPE_ATTRIBUTE]; |
| 949 if (attr != null) { | 898 if (attr != null) { |
| 950 clone.attributes[_STYLE_SCOPE_ATTRIBUTE] = attr; | 899 clone.attributes[_STYLE_SCOPE_ATTRIBUTE] = attr; |
| 951 } | 900 } |
| 952 | 901 |
| 953 scope.append(clone); | 902 scope.append(clone); |
| 954 } | 903 } |
| 955 | 904 |
| 956 /** | 905 /// Prevents flash of unstyled content |
| 957 * Prevents flash of unstyled content | 906 /// This is the list of selectors for veiled elements |
| 958 * This is the list of selectors for veiled elements | |
| 959 */ | |
| 960 static List<Element> veiledElements = ['body']; | 907 static List<Element> veiledElements = ['body']; |
| 961 | 908 |
| 962 /** Apply unveil class. */ | 909 /// Apply unveil class. |
| 963 static void unveilElements() { | 910 static void unveilElements() { |
| 964 window.requestAnimationFrame((_) { | 911 window.requestAnimationFrame((_) { |
| 965 var nodes = document.querySelectorAll('.$_VEILED_CLASS'); | 912 var nodes = document.querySelectorAll('.$_VEILED_CLASS'); |
| 966 for (var node in nodes) { | 913 for (var node in nodes) { |
| 967 (node.classes)..add(_UNVEIL_CLASS)..remove(_VEILED_CLASS); | 914 (node.classes)..add(_UNVEIL_CLASS)..remove(_VEILED_CLASS); |
| 968 } | 915 } |
| 969 // NOTE: depends on transition end event to remove 'unveil' class. | 916 // NOTE: depends on transition end event to remove 'unveil' class. |
| 970 if (nodes.isNotEmpty) { | 917 if (nodes.isNotEmpty) { |
| 971 window.onTransitionEnd.first.then((_) { | 918 window.onTransitionEnd.first.then((_) { |
| 972 for (var node in nodes) { | 919 for (var node in nodes) { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1030 | 977 |
| 1031 final Logger _observeLog = new Logger('polymer.observe'); | 978 final Logger _observeLog = new Logger('polymer.observe'); |
| 1032 final Logger _eventsLog = new Logger('polymer.events'); | 979 final Logger _eventsLog = new Logger('polymer.events'); |
| 1033 final Logger _unbindLog = new Logger('polymer.unbind'); | 980 final Logger _unbindLog = new Logger('polymer.unbind'); |
| 1034 final Logger _bindLog = new Logger('polymer.bind'); | 981 final Logger _bindLog = new Logger('polymer.bind'); |
| 1035 | 982 |
| 1036 final Expando _shadowHost = new Expando<Polymer>(); | 983 final Expando _shadowHost = new Expando<Polymer>(); |
| 1037 | 984 |
| 1038 final Expando _eventHandledTable = new Expando<Set<Node>>(); | 985 final Expando _eventHandledTable = new Expando<Set<Node>>(); |
| 1039 | 986 |
| 1040 /** | 987 /// Base class for PolymerElements deriving from HtmlElement. |
| 1041 * Base class for PolymerElements deriving from HtmlElement. | 988 /// |
| 1042 * | 989 /// See [Polymer]. |
| 1043 * See [Polymer]. | |
| 1044 */ | |
| 1045 class PolymerElement extends HtmlElement with Polymer, Observable { | 990 class PolymerElement extends HtmlElement with Polymer, Observable { |
| 1046 PolymerElement.created() : super.created() { | 991 PolymerElement.created() : super.created() { |
| 1047 polymerCreated(); | 992 polymerCreated(); |
| 1048 } | 993 } |
| 1049 } | 994 } |
| 1050 | 995 |
| 1051 class _PropertyValue { | 996 class _PropertyValue { |
| 1052 Object oldValue, newValue; | 997 Object oldValue, newValue; |
| 1053 _PropertyValue(this.oldValue); | 998 _PropertyValue(this.oldValue); |
| 1054 } | 999 } |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1102 if (_sub != null) { | 1047 if (_sub != null) { |
| 1103 if (_eventsLog.isLoggable(Level.FINE)) { | 1048 if (_eventsLog.isLoggable(Level.FINE)) { |
| 1104 _eventsLog.fine( | 1049 _eventsLog.fine( |
| 1105 'event.remove: [$_node].$_eventName => [$_model].$_path())'); | 1050 'event.remove: [$_node].$_eventName => [$_model].$_path())'); |
| 1106 } | 1051 } |
| 1107 _sub.cancel(); | 1052 _sub.cancel(); |
| 1108 _sub = null; | 1053 _sub = null; |
| 1109 } | 1054 } |
| 1110 } | 1055 } |
| 1111 } | 1056 } |
| OLD | NEW |