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