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 /// Use this annotation to publish a property as an attribute. | 7 /// Use this annotation to publish a property as an attribute. |
| 8 /// | 8 /// |
| 9 /// You can also use [PublishedProperty] to provide additional information, | 9 /// You can also use [PublishedProperty] to provide additional information, |
| 10 /// such as automatically syncing the property back to the attribute. | 10 /// such as automatically syncing the property back to the attribute. |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 static void register(String name, [Type type]) { | 161 static void register(String name, [Type type]) { |
| 162 //console.log('registering [' + name + ']'); | 162 //console.log('registering [' + name + ']'); |
| 163 if (type == null) type = PolymerElement; | 163 if (type == null) type = PolymerElement; |
| 164 | 164 |
| 165 _typesByName[name] = type; | 165 _typesByName[name] = type; |
| 166 | 166 |
| 167 // Dart note: here we notify JS of the element registration. We don't pass | 167 // Dart note: here we notify JS of the element registration. We don't pass |
| 168 // the Dart type because we will handle that in PolymerDeclaration. | 168 // the Dart type because we will handle that in PolymerDeclaration. |
| 169 // See _hookJsPolymerDeclaration for how this is done. | 169 // See _hookJsPolymerDeclaration for how this is done. |
| 170 (js.context['Polymer'] as JsFunction).apply([name]); | 170 (js.context['Polymer'] as JsFunction).apply([name]); |
| 171 (js.context['HTMLElement']['register'] as JsFunction).apply( | |
|
Siggi Cherem (dart-lang)
2014/12/04 00:32:18
I can't believe they monkey patch HTMLElement for
| |
| 172 [name, js.context['HTMLElement']['prototype']]); | |
| 171 } | 173 } |
| 172 | 174 |
| 173 /// Register a custom element that has no associated `<polymer-element>`. | 175 /// Register a custom element that has no associated `<polymer-element>`. |
| 174 /// Unlike [register] this will always perform synchronous registration and | 176 /// Unlike [register] this will always perform synchronous registration and |
| 175 /// by the time this method returns the element will be available using | 177 /// by the time this method returns the element will be available using |
| 176 /// [document.createElement] or by modifying the HTML to include the element. | 178 /// [document.createElement] or by modifying the HTML to include the element. |
| 177 static void registerSync(String name, Type type, | 179 static void registerSync(String name, Type type, |
| 178 {String extendsTag, Document doc, Node template}) { | 180 {String extendsTag, Document doc, Node template}) { |
| 179 | 181 |
| 180 // Our normal registration, this will queue up the name->type association. | 182 // Our normal registration, this will queue up the name->type association. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 191 // new JsObject.fromBrowserObject(poly).callMethod('init') | 193 // new JsObject.fromBrowserObject(poly).callMethod('init') |
| 192 // | 194 // |
| 193 // However doing it that way hits an issue with JS-interop in IE10: we get a | 195 // However doing it that way hits an issue with JS-interop in IE10: we get a |
| 194 // JsObject that wraps something other than `poly`, due to improper caching. | 196 // JsObject that wraps something other than `poly`, due to improper caching. |
| 195 // By reusing _polymerElementProto that we used for 'register', we can | 197 // By reusing _polymerElementProto that we used for 'register', we can |
| 196 // then call apply on it to invoke init() with the correct `this` pointer. | 198 // then call apply on it to invoke init() with the correct `this` pointer. |
| 197 JsFunction init = _polymerElementProto['init']; | 199 JsFunction init = _polymerElementProto['init']; |
| 198 init.apply([], thisArg: poly); | 200 init.apply([], thisArg: poly); |
| 199 } | 201 } |
| 200 | 202 |
| 201 // Note: these are from src/declaration/import.js | 203 // Warning for when people try to use `importElements` or `import`. |
| 202 // For now proxy to the JS methods, because we want to share the loader with | 204 static const String _DYNAMIC_IMPORT_WARNING = 'Dynamically loading html ' |
| 203 // polymer.js for interop purposes. | 205 'imports has very limited support right now in dart, see ' |
| 206 'http://dartbug.com/17873.'; | |
| 207 | |
| 208 /// Loads the set of HTMLImports contained in `node`. Returns a future that | |
| 209 /// resolves when all the imports have been loaded. This method can be used to | |
| 210 /// lazily load imports. For example, given a template: | |
| 211 /// | |
| 212 /// <template> | |
| 213 /// <link rel="import" href="my-import1.html"> | |
| 214 /// <link rel="import" href="my-import2.html"> | |
| 215 /// </template> | |
| 216 /// | |
| 217 /// Polymer.importElements(template.content) | |
| 218 /// .then((_) => print('imports lazily loaded')); | |
| 219 /// | |
| 220 /// Dart Note: This has very limited support in dart, http://dartbug.com/17873 | |
| 221 // Dart Note: From src/lib/import.js For now proxy to the JS methods, | |
| 222 // because we want to share the loader with polymer.js for interop purposes. | |
| 204 static Future importElements(Node elementOrFragment) { | 223 static Future importElements(Node elementOrFragment) { |
| 224 print(_DYNAMIC_IMPORT_WARNING); | |
| 205 var completer = new Completer(); | 225 var completer = new Completer(); |
| 206 js.context['Polymer'].callMethod('importElements', | 226 js.context['Polymer'].callMethod('importElements', |
| 207 [elementOrFragment, () => completer.complete()]); | 227 [elementOrFragment, () => completer.complete()]); |
| 208 return completer.future; | 228 return completer.future; |
| 209 } | 229 } |
| 210 | 230 |
| 211 static Future importUrls(List urls) { | 231 /// Loads an HTMLImport for each url specified in the `urls` array. Notifies |
| 232 /// when all the imports have loaded by calling the `callback` function | |
| 233 /// argument. This method can be used to lazily load imports. For example, | |
| 234 /// For example, | |
| 235 /// | |
| 236 /// Polymer.import(['my-import1.html', 'my-import2.html']) | |
| 237 /// .then((_) => print('imports lazily loaded')); | |
| 238 /// | |
| 239 /// Dart Note: This has very limited support in dart, http://dartbug.com/17873 | |
| 240 // Dart Note: From src/lib/import.js. For now proxy to the JS methods, | |
| 241 // because we want to share the loader with polymer.js for interop purposes. | |
| 242 static Future import(List urls) { | |
| 243 print(_DYNAMIC_IMPORT_WARNING); | |
| 212 var completer = new Completer(); | 244 var completer = new Completer(); |
| 213 js.context['Polymer'].callMethod('importUrls', | 245 js.context['Polymer'].callMethod('import', |
| 214 [urls, () => completer.complete()]); | 246 [urls, () => completer.complete()]); |
| 215 return completer.future; | 247 return completer.future; |
| 216 } | 248 } |
| 217 | 249 |
| 250 /// Deprecated: Use `import` instead. | |
| 251 @deprecated | |
| 252 static Future importUrls(List urls) { | |
| 253 return import(urls); | |
| 254 } | |
| 255 | |
| 218 static final Completer _onReady = new Completer(); | 256 static final Completer _onReady = new Completer(); |
| 219 | 257 |
| 220 /// Future indicating that the Polymer library has been loaded and is ready | 258 /// Future indicating that the Polymer library has been loaded and is ready |
| 221 /// for use. | 259 /// for use. |
| 222 static Future get onReady => _onReady.future; | 260 static Future get onReady => _onReady.future; |
| 223 | 261 |
| 224 /// Returns a list of elements that have had polymer-elements created but | 262 /// Returns a list of elements that have had polymer-elements created but |
| 225 /// are not yet ready to register. The list is an array of element | 263 /// are not yet ready to register. The list is an array of element |
| 226 /// definitions. | 264 /// definitions. |
| 227 static List<Element> get waitingFor => | 265 static List<Element> get waitingFor => |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 357 /// If this class is a superclass, calling `super.created()` is sufficient. | 395 /// If this class is a superclass, calling `super.created()` is sufficient. |
| 358 void polymerCreated() { | 396 void polymerCreated() { |
| 359 var t = nodeBind(this).templateInstance; | 397 var t = nodeBind(this).templateInstance; |
| 360 if (t != null && t.model != null) { | 398 if (t != null && t.model != null) { |
| 361 window.console.warn('Attributes on $_name were data bound ' | 399 window.console.warn('Attributes on $_name were data bound ' |
| 362 'prior to Polymer upgrading the element. This may result in ' | 400 'prior to Polymer upgrading the element. This may result in ' |
| 363 'incorrect binding types.'); | 401 'incorrect binding types.'); |
| 364 } | 402 } |
| 365 prepareElement(); | 403 prepareElement(); |
| 366 if (!isTemplateStagingDocument(ownerDocument)) { | 404 if (!isTemplateStagingDocument(ownerDocument)) { |
| 367 makeElementReady(); | 405 _makeElementReady(); |
| 368 } | 406 } |
| 369 } | 407 } |
| 370 | 408 |
| 371 /// *Deprecated* use [shadowRoots] instead. | 409 /// *Deprecated* use [shadowRoots] instead. |
| 372 @deprecated | 410 @deprecated |
| 373 ShadowRoot getShadowRoot(String customTagName) => shadowRoots[customTagName]; | 411 ShadowRoot getShadowRoot(String customTagName) => shadowRoots[customTagName]; |
| 374 | 412 |
| 375 void prepareElement() { | 413 void prepareElement() { |
| 376 if (_elementPrepared) { | 414 if (_elementPrepared) { |
| 377 window.console.warn('Element already prepared: $_name'); | 415 window.console.warn('Element already prepared: $_name'); |
| 378 return; | 416 return; |
| 379 } | 417 } |
| 380 _initJsObject(); | 418 _initJsObject(); |
| 381 // Dart note: get the corresponding <polymer-element> declaration. | 419 // Dart note: get the corresponding <polymer-element> declaration. |
| 382 _element = _getDeclaration(_name); | 420 _element = _getDeclaration(_name); |
| 383 // install property storage | 421 // install property storage |
| 384 createPropertyObserver(); | 422 createPropertyObserver(); |
| 385 openPropertyObserver(); | 423 openPropertyObserver(); |
| 386 // install boilerplate attributes | 424 // install boilerplate attributes |
| 387 copyInstanceAttributes(); | 425 copyInstanceAttributes(); |
| 388 // process input attributes | 426 // process input attributes |
| 389 takeAttributes(); | 427 takeAttributes(); |
| 390 // add event listeners | 428 // add event listeners |
| 391 addHostListeners(); | 429 addHostListeners(); |
| 392 } | 430 } |
| 393 | 431 |
| 394 /// Initialize JS interop for this element. For now we just initialize the | 432 /// Initialize JS interop for this element. For now we just initialize the |
| 395 // JsObject, but in the future we could also initialize JS APIs here. | 433 /// JsObject, but in the future we could also initialize JS APIs here. |
| 396 _initJsObject() { | 434 _initJsObject() { |
| 397 _jsElem = new JsObject.fromBrowserObject(this); | 435 _jsElem = new JsObject.fromBrowserObject(this); |
| 398 } | 436 } |
| 399 | 437 |
| 400 makeElementReady() { | 438 /// Deprecated: This is no longer a public method. |
| 439 @deprecated | |
| 440 makeElementReady() => _makeElementReady(); | |
| 441 | |
| 442 _makeElementReady() { | |
| 401 if (_readied) return; | 443 if (_readied) return; |
| 402 _readied = true; | 444 _readied = true; |
| 403 createComputedProperties(); | 445 createComputedProperties(); |
| 404 | 446 |
| 405 // TODO(sorvell): We could create an entry point here | |
| 406 // for the user to compute property values. | |
| 407 // process declarative resources | |
| 408 parseDeclarations(_element); | 447 parseDeclarations(_element); |
| 409 // TODO(sorvell): CE polyfill uses unresolved attribute to simulate | 448 // NOTE: Support use of the `unresolved` attribute to help polyfill |
| 410 // :unresolved; remove this attribute to be compatible with native | 449 // custom elements' `:unresolved` feature. |
| 411 // CE. | |
| 412 attributes.remove('unresolved'); | 450 attributes.remove('unresolved'); |
| 413 // user entry point | 451 // user entry point |
| 414 _readyLog.info(() => '[$this]: ready'); | 452 _readyLog.info(() => '[$this]: ready'); |
| 415 ready(); | 453 ready(); |
| 416 } | 454 } |
| 417 | 455 |
| 418 /// Called when [prepareElement] is finished, which means that the element's | 456 /// Lifecycle method called when the element has populated it's `shadowRoot`, |
| 419 /// shadowRoot has been created, its event listeners have been setup, | 457 /// prepared data-observation, and made itself ready for API interaction. |
| 420 /// attributes have been reflected to properties, and property observers have | 458 /// To wait until the element has been attached to the default view, use |
| 421 /// been setup. To wait until the element has been attached to the default | 459 /// [attached] or [domReady]. |
| 422 /// view, use [attached] or [domReady]. | |
| 423 void ready() {} | 460 void ready() {} |
| 424 | 461 |
| 425 /// domReady can be used to access elements in dom (descendants, | 462 /// Implement to access custom elements in dom descendants, ancestors, |
| 426 /// ancestors, siblings) such that the developer is enured to upgrade | 463 /// or siblings. Because custom elements upgrade in document order, |
| 427 /// ordering. If the element definitions have loaded, domReady | 464 /// elements accessed in `ready` or `attached` may not be upgraded. When |
| 428 /// can be used to access upgraded elements. | 465 /// `domReady` is called, all registered custom elements are guaranteed |
| 429 /// | 466 /// to have been upgraded. |
| 430 /// To use, override this method in your element. | |
| 431 void domReady() {} | 467 void domReady() {} |
| 432 | 468 |
| 433 void attached() { | 469 void attached() { |
| 434 if (!_elementPrepared) { | 470 if (!_elementPrepared) { |
| 435 // Dart specific message for a common issue. | 471 // Dart specific message for a common issue. |
| 436 throw new StateError('polymerCreated was not called for custom element ' | 472 throw new StateError('polymerCreated was not called for custom element ' |
| 437 '$_name, this should normally be done in the .created() if Polymer ' | 473 '$_name, this should normally be done in the .created() if Polymer ' |
| 438 'is used as a mixin.'); | 474 'is used as a mixin.'); |
| 439 } | 475 } |
| 440 | 476 |
| 441 cancelUnbindAll(); | 477 cancelUnbindAll(); |
| 442 if (!hasBeenAttached) { | 478 if (!hasBeenAttached) { |
| 443 _hasBeenAttached = true; | 479 _hasBeenAttached = true; |
| 444 async((_) => domReady()); | 480 async((_) => domReady()); |
| 445 } | 481 } |
| 446 } | 482 } |
| 447 | 483 |
| 448 void detached() { | 484 void detached() { |
| 449 if (!preventDispose) asyncUnbindAll(); | 485 if (!preventDispose) asyncUnbindAll(); |
| 450 } | 486 } |
| 451 | 487 |
| 452 /// Recursive ancestral <element> initialization, oldest first. | 488 /// Walks the prototype-chain of this element and allows specific |
| 489 /// classes a chance to process static declarations. | |
| 490 /// | |
| 491 /// In particular, each polymer-element has it's own `template`. | |
| 492 /// `parseDeclarations` is used to accumulate all element `template`s | |
| 493 /// from an inheritance chain. | |
| 494 /// | |
| 495 /// `parseDeclaration` static methods implemented in the chain are called | |
| 496 /// recursively, oldest first, with the `<polymer-element>` associated | |
| 497 /// with the current prototype passed as an argument. | |
| 498 /// | |
| 499 /// An element may override this method to customize shadow-root generation. | |
| 453 void parseDeclarations(PolymerDeclaration declaration) { | 500 void parseDeclarations(PolymerDeclaration declaration) { |
| 454 if (declaration != null) { | 501 if (declaration != null) { |
| 455 parseDeclarations(declaration.superDeclaration); | 502 parseDeclarations(declaration.superDeclaration); |
| 456 parseDeclaration(declaration.element); | 503 parseDeclaration(declaration.element); |
| 457 } | 504 } |
| 458 } | 505 } |
| 459 | 506 |
| 460 /// Parse input `<polymer-element>` as needed, override for custom behavior. | 507 /// Perform init-time actions based on static information in the |
| 508 /// `<polymer-element>` instance argument. | |
| 509 /// | |
| 510 /// For example, the standard implementation locates the template associated | |
| 511 /// with the given `<polymer-element>` and stamps it into a shadow-root to | |
| 512 /// implement shadow inheritance. | |
| 513 /// | |
| 514 /// An element may override this method for custom behavior. | |
| 461 void parseDeclaration(Element elementElement) { | 515 void parseDeclaration(Element elementElement) { |
| 462 var template = fetchTemplate(elementElement); | 516 var template = fetchTemplate(elementElement); |
| 463 | 517 |
| 464 if (template != null) { | 518 if (template != null) { |
| 465 var root = shadowFromTemplate(template); | 519 var root = shadowFromTemplate(template); |
| 466 | 520 |
| 467 var name = elementElement.attributes['name']; | 521 var name = elementElement.attributes['name']; |
| 468 if (name == null) return; | 522 if (name == null) return; |
| 469 shadowRoots[name] = root; | 523 shadowRoots[name] = root; |
| 470 } | 524 } |
| 471 } | 525 } |
| 472 | 526 |
| 473 /// Return a shadow-root template (if desired), override for custom behavior. | 527 /// Given a `<polymer-element>`, find an associated template (if any) to be |
| 528 /// used for shadow-root generation. | |
| 529 /// | |
| 530 /// An element may override this method for custom behavior. | |
| 474 Element fetchTemplate(Element elementElement) => | 531 Element fetchTemplate(Element elementElement) => |
| 475 elementElement.querySelector('template'); | 532 elementElement.querySelector('template'); |
| 476 | 533 |
| 477 /// Utility function that stamps a `<template>` into light-dom. | 534 /// Utility function that stamps a `<template>` into light-dom. |
| 478 Node lightFromTemplate(Element template, [Node refNode]) { | 535 Node lightFromTemplate(Element template, [Node refNode]) { |
| 479 if (template == null) return null; | 536 if (template == null) return null; |
| 480 | 537 |
| 481 // TODO(sorvell): mark this element as an event controller so that | 538 // TODO(sorvell): mark this element as an event controller so that |
| 482 // event listeners on bound nodes inside it will be called on it. | 539 // event listeners on bound nodes inside it will be called on it. |
| 483 // Note, the expectation here is that events on all descendants | 540 // Note, the expectation here is that events on all descendants |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 554 /// Use [MutationObserver] if you want to listen to a stream of changes. | 611 /// Use [MutationObserver] if you want to listen to a stream of changes. |
| 555 Future<List<MutationRecord>> onMutation(Node node) { | 612 Future<List<MutationRecord>> onMutation(Node node) { |
| 556 var completer = new Completer(); | 613 var completer = new Completer(); |
| 557 new MutationObserver((mutations, observer) { | 614 new MutationObserver((mutations, observer) { |
| 558 observer.disconnect(); | 615 observer.disconnect(); |
| 559 completer.complete(mutations); | 616 completer.complete(mutations); |
| 560 })..observe(node, childList: true, subtree: true); | 617 })..observe(node, childList: true, subtree: true); |
| 561 return completer.future; | 618 return completer.future; |
| 562 } | 619 } |
| 563 | 620 |
| 621 // copy attributes defined in the element declaration to the instance | |
| 622 // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied | |
| 623 // to the element instance here. | |
| 564 void copyInstanceAttributes() { | 624 void copyInstanceAttributes() { |
| 565 _element._instanceAttributes.forEach((name, value) { | 625 _element._instanceAttributes.forEach((name, value) { |
| 566 attributes.putIfAbsent(name, () => value); | 626 attributes.putIfAbsent(name, () => value); |
| 567 }); | 627 }); |
| 568 } | 628 } |
| 569 | 629 |
| 570 void takeAttributes() { | 630 void takeAttributes() { |
| 571 if (_element._publishLC == null) return; | 631 if (_element._publishLC == null) return; |
| 572 attributes.forEach(attributeToProperty); | 632 attributes.forEach(attributeToProperty); |
| 573 } | 633 } |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 600 // install new value (has side-effects) | 660 // install new value (has side-effects) |
| 601 smoke.write(this, decl.name, newValue); | 661 smoke.write(this, decl.name, newValue); |
| 602 } | 662 } |
| 603 } | 663 } |
| 604 | 664 |
| 605 /// Return the published property matching name, or null. | 665 /// Return the published property matching name, or null. |
| 606 // TODO(jmesserly): should we just return Symbol here? | 666 // TODO(jmesserly): should we just return Symbol here? |
| 607 smoke.Declaration propertyForAttribute(String name) { | 667 smoke.Declaration propertyForAttribute(String name) { |
| 608 final publishLC = _element._publishLC; | 668 final publishLC = _element._publishLC; |
| 609 if (publishLC == null) return null; | 669 if (publishLC == null) return null; |
| 610 //console.log('propertyForAttribute:', name, 'matches', match); | |
| 611 return publishLC[name]; | 670 return publishLC[name]; |
| 612 } | 671 } |
| 613 | 672 |
| 614 /// Convert representation of [value] based on [type] and [currentValue]. | 673 /// Convert representation of [value] based on [type] and [currentValue]. |
| 615 Object deserializeValue(String value, Object currentValue, Type type) => | 674 Object deserializeValue(String value, Object currentValue, Type type) => |
| 616 deserialize.deserializeValue(value, currentValue, type); | 675 deserialize.deserializeValue(value, currentValue, type); |
| 617 | 676 |
| 618 String serializeValue(Object value) { | 677 String serializeValue(Object value) { |
| 619 if (value == null) return null; | 678 if (value == null) return null; |
| 620 | 679 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 659 var syntax = this.syntax; | 718 var syntax = this.syntax; |
| 660 var t = templateBind(template); | 719 var t = templateBind(template); |
| 661 if (syntax == null && t.bindingDelegate == null) { | 720 if (syntax == null && t.bindingDelegate == null) { |
| 662 syntax = element.syntax; | 721 syntax = element.syntax; |
| 663 } | 722 } |
| 664 var dom = t.createInstance(this, syntax); | 723 var dom = t.createInstance(this, syntax); |
| 665 _observers.addAll(getTemplateInstanceBindings(dom)); | 724 _observers.addAll(getTemplateInstanceBindings(dom)); |
| 666 return dom; | 725 return dom; |
| 667 } | 726 } |
| 668 | 727 |
| 728 /// Called by TemplateBinding/NodeBind to setup a binding to the given | |
| 729 /// property. It's overridden here to support property bindings in addition to | |
| 730 /// attribute bindings that are supported by default. | |
| 669 Bindable bind(String name, bindable, {bool oneTime: false}) { | 731 Bindable bind(String name, bindable, {bool oneTime: false}) { |
| 670 var decl = propertyForAttribute(name); | 732 var decl = propertyForAttribute(name); |
| 671 if (decl == null) { | 733 if (decl == null) { |
| 672 // Cannot call super.bind because template_binding is its own package | 734 // Cannot call super.bind because template_binding is its own package |
| 673 return nodeBindFallback(this).bind(name, bindable, oneTime: oneTime); | 735 return nodeBindFallback(this).bind(name, bindable, oneTime: oneTime); |
| 674 } else { | 736 } else { |
| 675 // use n-way Polymer binding | 737 // use n-way Polymer binding |
| 676 var observer = bindProperty(decl.name, bindable, oneTime: oneTime); | 738 var observer = bindProperty(decl.name, bindable, oneTime: oneTime); |
| 677 // NOTE: reflecting binding information is typically required only for | 739 // NOTE: reflecting binding information is typically required only for |
| 678 // tooling. It has a performance cost so it's opt-in in Node.bind. | 740 // tooling. It has a performance cost so it's opt-in in Node.bind. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 690 } | 752 } |
| 691 return observer; | 753 return observer; |
| 692 } | 754 } |
| 693 } | 755 } |
| 694 | 756 |
| 695 _recordBinding(String name, observer) { | 757 _recordBinding(String name, observer) { |
| 696 if (bindings == null) bindings = {}; | 758 if (bindings == null) bindings = {}; |
| 697 this.bindings[name] = observer; | 759 this.bindings[name] = observer; |
| 698 } | 760 } |
| 699 | 761 |
| 700 bindFinished() => makeElementReady(); | 762 /// Called by TemplateBinding when all bindings on an element have been |
| 763 /// executed. This signals that all element inputs have been gathered and it's | |
| 764 /// safe to ready the element, create shadow-root and start data-observation. | |
| 765 bindFinished() => _makeElementReady(); | |
| 701 | 766 |
| 702 Map<String, Bindable> get bindings => nodeBindFallback(this).bindings; | 767 Map<String, Bindable> get bindings => nodeBindFallback(this).bindings; |
| 703 set bindings(Map value) { nodeBindFallback(this).bindings = value; } | 768 set bindings(Map value) { nodeBindFallback(this).bindings = value; } |
| 704 | 769 |
| 705 TemplateInstance get templateInstance => | 770 TemplateInstance get templateInstance => |
| 706 nodeBindFallback(this).templateInstance; | 771 nodeBindFallback(this).templateInstance; |
| 707 | 772 |
| 708 // TODO(sorvell): unbind/unbindAll has been removed, as public api, from | 773 /// Called at detached time to signal that an element's bindings should be |
| 709 // TemplateBinding. We still need to close/dispose of observers but perhaps | 774 /// cleaned up. This is done asynchronously so that users have the chance to |
| 710 // we should choose a more explicit name. | 775 /// call `cancelUnbindAll` to prevent unbinding. |
| 711 void asyncUnbindAll() { | 776 void asyncUnbindAll() { |
| 712 if (_unbound == true) return; | 777 if (_unbound == true) return; |
| 713 _unbindLog.fine(() => '[$_name] asyncUnbindAll'); | 778 _unbindLog.fine(() => '[$_name] asyncUnbindAll'); |
| 714 _unbindAllJob = scheduleJob(_unbindAllJob, unbindAll); | 779 _unbindAllJob = scheduleJob(_unbindAllJob, unbindAll); |
| 715 } | 780 } |
| 716 | 781 |
| 782 /// This method should rarely be used and only if `cancelUnbindAll` has been | |
| 783 /// called to prevent element unbinding. In this case, the element's bindings | |
| 784 /// will not be automatically cleaned up and it cannot be garbage collected by | |
| 785 /// by the system. If memory pressure is a concern or a large amount of | |
| 786 /// elements need to be managed in this way, `unbindAll` can be called to | |
| 787 /// deactivate the element's bindings and allow its memory to be reclaimed. | |
| 717 void unbindAll() { | 788 void unbindAll() { |
| 718 if (_unbound == true) return; | 789 if (_unbound == true) return; |
| 719 closeObservers(); | 790 closeObservers(); |
| 720 closeNamedObservers(); | 791 closeNamedObservers(); |
| 721 _unbound = true; | 792 _unbound = true; |
| 722 } | 793 } |
| 723 | 794 |
| 795 //// Call in `detached` to prevent the element from unbinding when it is | |
| 796 //// detached from the dom. The element is unbound as a cleanup step that | |
| 797 //// allows its memory to be reclaimed. If `cancelUnbindAll` is used, consider | |
| 798 /// calling `unbindAll` when the element is no longer needed. This will allow | |
| 799 /// its memory to be reclaimed. | |
| 724 void cancelUnbindAll() { | 800 void cancelUnbindAll() { |
| 725 if (_unbound == true) { | 801 if (_unbound == true) { |
| 726 _unbindLog.warning(() => | 802 _unbindLog.warning(() => |
| 727 '[$_name] already unbound, cannot cancel unbindAll'); | 803 '[$_name] already unbound, cannot cancel unbindAll'); |
| 728 return; | 804 return; |
| 729 } | 805 } |
| 730 _unbindLog.fine(() => '[$_name] cancelUnbindAll'); | 806 _unbindLog.fine(() => '[$_name] cancelUnbindAll'); |
| 731 if (_unbindAllJob != null) { | 807 if (_unbindAllJob != null) { |
| 732 _unbindAllJob.stop(); | 808 _unbindAllJob.stop(); |
| 733 _unbindAllJob = null; | 809 _unbindAllJob = null; |
| 734 } | 810 } |
| 735 } | 811 } |
| 736 | 812 |
| 737 static void _forNodeTree(Node node, void callback(Node node)) { | 813 static void _forNodeTree(Node node, void callback(Node node)) { |
| 738 if (node == null) return; | 814 if (node == null) return; |
| 739 | 815 |
| 740 callback(node); | 816 callback(node); |
| 741 for (var child = node.firstChild; child != null; child = child.nextNode) { | 817 for (var child = node.firstChild; child != null; child = child.nextNode) { |
| 742 _forNodeTree(child, callback); | 818 _forNodeTree(child, callback); |
| 743 } | 819 } |
| 744 } | 820 } |
| 745 | 821 |
| 746 /// Set up property observers. | 822 /// Creates a CompoundObserver to observe property changes. |
| 823 /// NOTE, this is only done if there are any properties in the `_observe` | |
| 824 /// object. | |
| 747 void createPropertyObserver() { | 825 void createPropertyObserver() { |
| 748 final observe = _element._observe; | 826 final observe = _element._observe; |
| 749 if (observe != null) { | 827 if (observe != null) { |
| 750 var o = _propertyObserver = new CompoundObserver(); | 828 var o = _propertyObserver = new CompoundObserver(); |
| 751 // keep track of property observer so we can shut it down | 829 // keep track of property observer so we can shut it down |
| 752 _observers.add(o); | 830 _observers.add(o); |
| 753 | 831 |
| 754 for (var path in observe.keys) { | 832 for (var path in observe.keys) { |
| 755 o.addPath(this, path); | 833 o.addPath(this, path); |
| 756 | 834 |
| 757 // TODO(jmesserly): on the Polymer side it doesn't look like they | 835 // TODO(jmesserly): on the Polymer side it doesn't look like they |
| 758 // will observe arrays unless it is a length == 1 path. | 836 // will observe arrays unless it is a length == 1 path. |
| 759 observeArrayValue(path, path.getValueFrom(this), null); | 837 observeArrayValue(path, path.getValueFrom(this), null); |
| 760 } | 838 } |
| 761 } | 839 } |
| 762 } | 840 } |
| 763 | 841 |
| 842 /// Start observing property changes. | |
| 764 void openPropertyObserver() { | 843 void openPropertyObserver() { |
| 765 if (_propertyObserver != null) { | 844 if (_propertyObserver != null) { |
| 766 _propertyObserver.open(notifyPropertyChanges); | 845 _propertyObserver.open(notifyPropertyChanges); |
| 767 } | 846 } |
| 768 | 847 |
| 769 // Dart note: we need an extra listener only to continue supporting | 848 // Dart note: we need an extra listener only to continue supporting |
| 770 // @published properties that follow the old syntax until we get rid of it. | 849 // @published properties that follow the old syntax until we get rid of it. |
| 771 // This workaround has timing issues so we prefer the new, not so nice, | 850 // This workaround has timing issues so we prefer the new, not so nice, |
| 772 // syntax. | 851 // syntax. |
| 773 if (_element._publish != null) { | 852 if (_element._publish != null) { |
| 774 changes.listen(_propertyChangeWorkaround); | 853 changes.listen(_propertyChangeWorkaround); |
| 775 } | 854 } |
| 776 } | 855 } |
| 777 | 856 |
| 778 /// Responds to property changes on this element. | 857 /// Handler for property changes; routes changes to observing methods. |
| 858 /// Note: array valued properties are observed for array splices. | |
| 779 void notifyPropertyChanges(List newValues, Map oldValues, List paths) { | 859 void notifyPropertyChanges(List newValues, Map oldValues, List paths) { |
| 780 final observe = _element._observe; | 860 final observe = _element._observe; |
| 781 final called = new HashSet(); | 861 final called = new HashSet(); |
| 782 | 862 |
| 783 oldValues.forEach((i, oldValue) { | 863 oldValues.forEach((i, oldValue) { |
| 784 final newValue = newValues[i]; | 864 final newValue = newValues[i]; |
| 785 | 865 |
| 786 // Date note: we don't need any special checking for null and undefined. | 866 // Date note: we don't need any special checking for null and undefined. |
| 787 | 867 |
| 788 // note: paths is of form [object, path, object, path] | 868 // note: paths is of form [object, path, object, path] |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 800 // TODO(sorvell): call method with the set of values it's expecting; | 880 // TODO(sorvell): call method with the set of values it's expecting; |
| 801 // e.g. 'foo bar': 'invalidate' expects the new and old values for | 881 // e.g. 'foo bar': 'invalidate' expects the new and old values for |
| 802 // foo and bar. Currently we give only one of these and then | 882 // foo and bar. Currently we give only one of these and then |
| 803 // deliver all the arguments. | 883 // deliver all the arguments. |
| 804 smoke.invoke(this, method, | 884 smoke.invoke(this, method, |
| 805 [oldValue, newValue, newValues, oldValues, paths], adjust: true); | 885 [oldValue, newValue, newValues, oldValues, paths], adjust: true); |
| 806 } | 886 } |
| 807 }); | 887 }); |
| 808 } | 888 } |
| 809 | 889 |
| 890 /// Force any pending property changes to synchronously deliver to handlers | |
| 891 /// specified in the `observe` object. | |
| 892 /// Note: normally changes are processed at microtask time. | |
| 893 /// | |
| 810 // Dart note: had to rename this to avoid colliding with | 894 // Dart note: had to rename this to avoid colliding with |
| 811 // Observable.deliverChanges. Even worse, super calls aren't possible or | 895 // Observable.deliverChanges. Even worse, super calls aren't possible or |
| 812 // it prevents Polymer from being a mixin, so we can't override it even if | 896 // it prevents Polymer from being a mixin, so we can't override it even if |
| 813 // we wanted to. | 897 // we wanted to. |
| 814 void deliverPropertyChanges() { | 898 void deliverPropertyChanges() { |
| 815 if (_propertyObserver != null) { | 899 if (_propertyObserver != null) { |
| 816 _propertyObserver.deliver(); | 900 _propertyObserver.deliver(); |
| 817 } | 901 } |
| 818 } | 902 } |
| 819 | 903 |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1058 } | 1142 } |
| 1059 | 1143 |
| 1060 _eventsLog.fine(() => '<<< [$_name]: dispatch $callbackOrMethod'); | 1144 _eventsLog.fine(() => '<<< [$_name]: dispatch $callbackOrMethod'); |
| 1061 } | 1145 } |
| 1062 | 1146 |
| 1063 /// Call [methodName] method on this object with [args]. | 1147 /// Call [methodName] method on this object with [args]. |
| 1064 invokeMethod(Symbol methodName, List args) => | 1148 invokeMethod(Symbol methodName, List args) => |
| 1065 smoke.invoke(this, methodName, args, adjust: true); | 1149 smoke.invoke(this, methodName, args, adjust: true); |
| 1066 | 1150 |
| 1067 /// Invokes a function asynchronously. | 1151 /// Invokes a function asynchronously. |
| 1068 /// This will call `Platform.flush()` and then return a `new Timer` | 1152 /// This will call `Polymer.flush()` and then return a `new Timer` |
| 1069 /// with the provided [method] and [timeout]. | 1153 /// with the provided [method] and [timeout]. |
| 1070 /// | 1154 /// |
| 1071 /// If you would prefer to run the callback using | 1155 /// If you would prefer to run the callback using |
| 1072 /// [window.requestAnimationFrame], see the [async] method. | 1156 /// [window.requestAnimationFrame], see the [async] method. |
| 1073 /// | 1157 /// |
| 1074 /// To cancel, call [Timer.cancel] on the result of this method. | 1158 /// To cancel, call [Timer.cancel] on the result of this method. |
| 1075 Timer asyncTimer(void method(), Duration timeout) { | 1159 Timer asyncTimer(void method(), Duration timeout) { |
| 1076 // Dart note: "async" is split into 2 methods so it can have a sensible type | 1160 // Dart note: "async" is split into 2 methods so it can have a sensible type |
| 1077 // signatures. Also removed the various features that don't make sense in a | 1161 // signatures. Also removed the various features that don't make sense in a |
| 1078 // Dart world, like binding to "this" and taking arguments list. | 1162 // Dart world, like binding to "this" and taking arguments list. |
| 1079 | 1163 |
| 1080 // when polyfilling Object.observe, ensure changes | 1164 // when polyfilling Object.observe, ensure changes |
| 1081 // propagate before executing the async method | 1165 // propagate before executing the async method |
| 1082 scheduleMicrotask(Observable.dirtyCheck); | 1166 scheduleMicrotask(Observable.dirtyCheck); |
| 1083 _Platform.callMethod('flush'); // for polymer-js interop | 1167 _Polymer.callMethod('flush'); // for polymer-js interop |
| 1084 return new Timer(timeout, method); | 1168 return new Timer(timeout, method); |
| 1085 } | 1169 } |
| 1086 | 1170 |
| 1087 /// Invokes a function asynchronously. | 1171 /// Invokes a function asynchronously. The context of the callback function is |
| 1088 /// This will call `Platform.flush()` and then call | 1172 /// function is bound to 'this' automatically. Returns a handle which may be |
| 1089 /// [window.requestAnimationFrame] with the provided [method] and return the | 1173 /// passed to cancelAsync to cancel the asynchronous call. |
| 1090 /// result. | |
| 1091 /// | 1174 /// |
| 1092 /// If you would prefer to run the callback after a given duration, see | 1175 /// If you would prefer to run the callback after a given duration, see |
| 1093 /// the [asyncTimer] method. | 1176 /// the [asyncTimer] method. |
| 1094 /// | 1177 /// |
| 1095 /// If you would like to cancel this, use [cancelAsync]. | 1178 /// If you would like to cancel this, use [cancelAsync]. |
| 1096 int async(RequestAnimationFrameCallback method) { | 1179 int async(RequestAnimationFrameCallback method) { |
| 1097 // when polyfilling Object.observe, ensure changes | 1180 // when polyfilling Object.observe, ensure changes |
| 1098 // propagate before executing the async method | 1181 // propagate before executing the async method |
| 1099 scheduleMicrotask(Observable.dirtyCheck); | 1182 scheduleMicrotask(Observable.dirtyCheck); |
| 1100 _Platform.callMethod('flush'); // for polymer-js interop | 1183 _Polymer.callMethod('flush'); // for polymer-js interop |
| 1101 return window.requestAnimationFrame(method); | 1184 return window.requestAnimationFrame(method); |
| 1102 } | 1185 } |
| 1103 | 1186 |
| 1104 /// Cancel an operation scenduled by [async]. This is just shorthand for: | 1187 /// Cancel an operation scheduled by [async]. |
| 1105 /// window.cancelAnimationFrame(id); | |
| 1106 void cancelAsync(int id) => window.cancelAnimationFrame(id); | 1188 void cancelAsync(int id) => window.cancelAnimationFrame(id); |
| 1107 | 1189 |
| 1108 /// Fire a [CustomEvent] targeting [onNode], or `this` if onNode is not | 1190 /// Fire a [CustomEvent] targeting [onNode], or `this` if onNode is not |
| 1109 /// supplied. Returns the new event. | 1191 /// supplied. Returns the new event. |
| 1110 CustomEvent fire(String type, {Object detail, Node onNode, bool canBubble, | 1192 CustomEvent fire(String type, {Object detail, Node onNode, bool canBubble, |
| 1111 bool cancelable}) { | 1193 bool cancelable}) { |
| 1112 var node = onNode != null ? onNode : this; | 1194 var node = onNode != null ? onNode : this; |
| 1113 var event = new CustomEvent( | 1195 var event = new CustomEvent( |
| 1114 type, | 1196 type, |
| 1115 canBubble: canBubble != null ? canBubble : true, | 1197 canBubble: canBubble != null ? canBubble : true, |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1369 final Logger _eventsLog = new Logger('polymer.events'); | 1451 final Logger _eventsLog = new Logger('polymer.events'); |
| 1370 final Logger _unbindLog = new Logger('polymer.unbind'); | 1452 final Logger _unbindLog = new Logger('polymer.unbind'); |
| 1371 final Logger _bindLog = new Logger('polymer.bind'); | 1453 final Logger _bindLog = new Logger('polymer.bind'); |
| 1372 final Logger _watchLog = new Logger('polymer.watch'); | 1454 final Logger _watchLog = new Logger('polymer.watch'); |
| 1373 final Logger _readyLog = new Logger('polymer.ready'); | 1455 final Logger _readyLog = new Logger('polymer.ready'); |
| 1374 | 1456 |
| 1375 final Expando _eventHandledTable = new Expando<Set<Node>>(); | 1457 final Expando _eventHandledTable = new Expando<Set<Node>>(); |
| 1376 | 1458 |
| 1377 final JsObject _PolymerGestures = js.context['PolymerGestures']; | 1459 final JsObject _PolymerGestures = js.context['PolymerGestures']; |
| 1378 | 1460 |
| OLD | NEW |