Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: pkg/polymer/lib/src/instance.dart

Issue 44573005: Update polymer to commit b7200854b2441a22ce89f6563963f36c50f5150d (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of polymer; 5 part of polymer;
6 6
7 /** 7 /**
8 * Use this annotation to publish a field as an attribute. For example: 8 * Use this annotation to publish a field as an attribute. For example:
9 * 9 *
10 * class MyPlaybackElement extends PolymerElement { 10 * class MyPlaybackElement extends PolymerElement {
11 * // This will be available as an HTML attribute, for example: 11 * // This will be available as an HTML attribute, for example:
12 * // <my-playback volume="11"> 12 * // <my-playback volume="11">
13 * @published double volume; 13 * @published double volume;
14 * } 14 * }
15 */ 15 */
16 // TODO(jmesserly): does @published imply @observable or vice versa? 16 // TODO(jmesserly): does @published imply @observable or vice versa?
17 const published = const PublishedProperty(); 17 const published = const PublishedProperty();
18 18
19 /** An annotation used to publish a field as an attribute. See [published]. */ 19 /** An annotation used to publish a field as an attribute. See [published]. */
20 class PublishedProperty extends ObservableProperty { 20 class PublishedProperty extends ObservableProperty {
21 const PublishedProperty(); 21 const PublishedProperty();
22 } 22 }
23 23
24 /** 24 /**
25 * The mixin class for Polymer elements. It provides convenience features on top 25 * The mixin class for Polymer elements. It provides convenience features on top
26 * of the custom elements web standard. 26 * of the custom elements web standard.
27 */ 27 */
28 abstract class Polymer implements Element, Observable, NodeBindExtension { 28 abstract class Polymer implements Element, Observable, NodeBindExtension {
29 // Fully ported from revision: 29 // Fully ported from revision:
30 // https://github.com/Polymer/polymer/blob/00e2982c78fcd396adaebff3118e94029a2 b9fb0 30 // https://github.com/Polymer/polymer/blob/b7200854b2441a22ce89f6563963f36c50f 5150d
31 // 31 //
32 // src/boot.js (static APIs on "Polymer" object) 32 // src/boot.js (static APIs on "Polymer" object)
33 // src/instance/attributes.js 33 // src/instance/attributes.js
34 // src/instance/base.js 34 // src/instance/base.js
35 // src/instance/events.js 35 // src/instance/events.js
36 // src/instance/mdv.js 36 // src/instance/mdv.js
37 // src/instance/properties.js 37 // src/instance/properties.js
38 // src/instance/style.js 38 // src/instance/style.js
39 // src/instance/utils.js 39 // src/instance/utils.js
40 40
(...skipping 15 matching lines...) Expand all
56 static void register(String name, [Type type]) { 56 static void register(String name, [Type type]) {
57 //console.log('registering [' + name + ']'); 57 //console.log('registering [' + name + ']');
58 if (type == null) type = PolymerElement; 58 if (type == null) type = PolymerElement;
59 59
60 _typesByName[name] = type; 60 _typesByName[name] = type;
61 // notify the registrar waiting for 'name', if any 61 // notify the registrar waiting for 'name', if any
62 _notifyType(name); 62 _notifyType(name);
63 } 63 }
64 64
65 /// The one syntax to rule them all. 65 /// The one syntax to rule them all.
66 static final BindingDelegate _polymerSyntax = new PolymerExpressions(); 66 static final BindingDelegate _polymerSyntax =
67 new _PolymerExpressionsWithEventDelegate();
67 68
68 static int _preparingElements = 0; 69 static int _preparingElements = 0;
69 70
70 static final Completer _ready = new Completer(); 71 static final Completer _ready = new Completer();
71 72
72 /** 73 /**
73 * Future indicating that the Polymer library has been loaded and is ready 74 * Future indicating that the Polymer library has been loaded and is ready
74 * for use. 75 * for use.
75 */ 76 */
76 static Future get onReady => _ready.future; 77 static Future get onReady => _ready.future;
77 78
78 PolymerDeclaration _declaration; 79 PolymerDeclaration _declaration;
79 80
80 /** The most derived `<polymer-element>` declaration for this element. */ 81 /** The most derived `<polymer-element>` declaration for this element. */
81 PolymerDeclaration get declaration => _declaration; 82 PolymerDeclaration get declaration => _declaration;
82 83
83 Map<String, StreamSubscription> _observers; 84 Map<String, StreamSubscription> _observers;
84 bool _unbound; // lazy-initialized 85 bool _unbound; // lazy-initialized
85 Job _unbindAllJob; 86 Job _unbindAllJob;
86 87
87 StreamSubscription _propertyObserver; 88 StreamSubscription _propertyObserver;
88 89
89 bool get _elementPrepared => _declaration != null; 90 bool get _elementPrepared => _declaration != null;
90 91
91 bool get applyAuthorStyles => false; 92 bool get applyAuthorStyles => false;
92 bool get resetStyleInheritance => false; 93 bool get resetStyleInheritance => false;
93 bool get alwaysPrepare => false; 94 bool get alwaysPrepare => false;
94 bool get preventDispose => false; 95 bool get preventDispose => false;
95 96
97 BindingDelegate syntax = _polymerSyntax;
98
96 /** 99 /**
97 * Shadow roots created by [parseElement]. See [getShadowRoot]. 100 * Shadow roots created by [parseElement]. See [getShadowRoot].
98 */ 101 */
99 final _shadowRoots = new HashMap<String, ShadowRoot>(); 102 final _shadowRoots = new HashMap<String, ShadowRoot>();
100 103
101 /** Map of items in the shadow root(s) by their [Element.id]. */ 104 /** Map of items in the shadow root(s) by their [Element.id]. */
102 // TODO(jmesserly): various issues: 105 // TODO(jmesserly): various issues:
103 // * wrap in UnmodifiableMapView? 106 // * wrap in UnmodifiableMapView?
104 // * should we have an object that implements noSuchMethod? 107 // * should we have an object that implements noSuchMethod?
105 // * should the map have a key order (e.g. LinkedHash or SplayTree)? 108 // * should the map have a key order (e.g. LinkedHash or SplayTree)?
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 root.append(dom); 275 root.append(dom);
273 // perform post-construction initialization tasks on shadow root 276 // perform post-construction initialization tasks on shadow root
274 shadowRootReady(root, template); 277 shadowRootReady(root, template);
275 // return the created shadow root 278 // return the created shadow root
276 return root; 279 return root;
277 } 280 }
278 281
279 void shadowRootReady(Node root, Element template) { 282 void shadowRootReady(Node root, Element template) {
280 // locate nodes with id and store references to them in this.$ hash 283 // locate nodes with id and store references to them in this.$ hash
281 marshalNodeReferences(root); 284 marshalNodeReferences(root);
282 // add local events of interest...
283 addInstanceListeners(root, template);
284 // TODO(jmesserly): port this 285 // TODO(jmesserly): port this
285 // set up pointer gestures 286 // set up pointer gestures
286 // PointerGestures.register(root); 287 // PointerGestures.register(root);
287 } 288 }
288 289
289 /** Locate nodes with id and store references to them in [$] hash. */ 290 /** Locate nodes with id and store references to them in [$] hash. */
290 void marshalNodeReferences(Node root) { 291 void marshalNodeReferences(Node root) {
291 if (root == null) return; 292 if (root == null) return;
292 for (var n in (root as dynamic).queryAll('[id]')) { 293 for (var n in (root as dynamic).queryAll('[id]')) {
293 $[n.id] = n; 294 $[n.id] = n;
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 * templateBind(template).createInstance(this, polymerSyntax); 414 * templateBind(template).createInstance(this, polymerSyntax);
414 * 415 *
415 * Where polymerSyntax is a singleton `PolymerExpressions` instance from the 416 * Where polymerSyntax is a singleton `PolymerExpressions` instance from the
416 * [polymer_expressions](https://pub.dartlang.org/packages/polymer_expressions ) 417 * [polymer_expressions](https://pub.dartlang.org/packages/polymer_expressions )
417 * package. 418 * package.
418 * 419 *
419 * You can override this method to change the instantiation behavior of the 420 * You can override this method to change the instantiation behavior of the
420 * template, for example to use a different data-binding syntax. 421 * template, for example to use a different data-binding syntax.
421 */ 422 */
422 DocumentFragment instanceTemplate(Element template) => 423 DocumentFragment instanceTemplate(Element template) =>
423 templateBind(template).createInstance(this, _polymerSyntax); 424 templateBind(template).createInstance(this, syntax);
424 425
425 NodeBinding createBinding(String name, model, String path) => 426 NodeBinding createBinding(String name, model, String path) =>
426 nodeBindFallback(this).createBinding(name, model, path); 427 nodeBindFallback(this).createBinding(name, model, path);
427 428
428 NodeBinding bind(String name, model, String path) { 429 NodeBinding bind(String name, model, String path) {
429 // note: binding is a prepare signal. This allows us to be sure that any 430 // note: binding is a prepare signal. This allows us to be sure that any
430 // property changes that occur as a result of binding will be observed. 431 // property changes that occur as a result of binding will be observed.
431 if (!_elementPrepared) prepareElement(); 432 if (!_elementPrepared) prepareElement();
432 433
433 var property = propertyForAttribute(name); 434 var property = propertyForAttribute(name);
434 if (property != null) { 435 if (property == null) {
436 // Cannot call super.bind because template_binding is its own package
437 return nodeBindFallback(this).bind(name, model, path);
438 } else {
439 // clean out the closets
435 unbind(name); 440 unbind(name);
436 // use n-way Polymer binding 441 // use n-way Polymer binding
437 var observer = bindProperty(property.simpleName, model, path); 442 var observer = bindProperty(property.simpleName, model, path);
438 // reflect bound property to attribute when binding 443 // reflect bound property to attribute when binding
439 // to ensure binding is not left on attribute if property 444 // to ensure binding is not left on attribute if property
440 // does not update due to not changing. 445 // does not update due to not changing.
441 // Dart note: we include this patch: 446 // Dart note: we include this patch:
442 // https://github.com/Polymer/polymer/pull/319 447 // https://github.com/Polymer/polymer/pull/319
443 reflectPropertyToAttribute(property.simpleName); 448 reflectPropertyToAttribute(property.simpleName);
444 return bindings[name] = observer; 449 return bindings[name] = observer;
445 } else {
446 // Cannot call super.bind because template_binding is its own package
447 return nodeBindFallback(this).bind(name, model, path);
448 } 450 }
449 } 451 }
450 452
451 Map<String, NodeBinding> get bindings => nodeBindFallback(this).bindings; 453 Map<String, NodeBinding> get bindings => nodeBindFallback(this).bindings;
452 454
453 void unbind(String name) => nodeBindFallback(this).unbind(name); 455 void unbind(String name) => nodeBindFallback(this).unbind(name);
454 456
455 void asyncUnbindAll() { 457 void asyncUnbindAll() {
456 if (_unbound == true) return; 458 if (_unbound == true) return;
457 _unbindLog.fine('[$localName] asyncUnbindAll'); 459 _unbindLog.fine('[$localName] asyncUnbindAll');
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 } 551 }
550 if (observe == null) return; 552 if (observe == null) return;
551 553
552 var method = observe[name]; 554 var method = observe[name];
553 if (method != null) { 555 if (method != null) {
554 // observes the value if it is an array 556 // observes the value if it is an array
555 observeArrayValue(name, pair.newValue, pair.oldValue); 557 observeArrayValue(name, pair.newValue, pair.oldValue);
556 // TODO(jmesserly): the JS code tries to avoid calling the same method 558 // TODO(jmesserly): the JS code tries to avoid calling the same method
557 // twice, but I don't see how that is possible. 559 // twice, but I don't see how that is possible.
558 // Dart note: JS also passes "arguments", so we pass all change records. 560 // Dart note: JS also passes "arguments", so we pass all change records.
559 invokeMethod(method, [pair.oldValue, pair.newValue, changes]); 561 invokeMethod(this, method, [pair.oldValue, pair.newValue, changes]);
560 } 562 }
561 }); 563 });
562 } 564 }
563 565
564 void observeArrayValue(Symbol name, Object value, Object old) { 566 void observeArrayValue(Symbol name, Object value, Object old) {
565 final observe = _declaration._observe; 567 final observe = _declaration._observe;
566 if (observe == null) return; 568 if (observe == null) return;
567 569
568 // we only care if there are registered side-effects 570 // we only care if there are registered side-effects
569 var callbackName = observe[name]; 571 var callbackName = observe[name];
570 if (callbackName == null) return; 572 if (callbackName == null) return;
571 573
572 // if we are observing the previous value, stop 574 // if we are observing the previous value, stop
573 if (old is ObservableList) { 575 if (old is ObservableList) {
574 if (_observeLog.isLoggable(Level.FINE)) { 576 if (_observeLog.isLoggable(Level.FINE)) {
575 _observeLog.fine('[$localName] observeArrayValue: unregister observer ' 577 _observeLog.fine('[$localName] observeArrayValue: unregister observer '
576 '$name'); 578 '$name');
577 } 579 }
578 580
579 unregisterObserver('${MirrorSystem.getName(name)}__array'); 581 unregisterObserver('${MirrorSystem.getName(name)}__array');
580 } 582 }
581 // if the new value is an array, being observing it 583 // if the new value is an array, being observing it
582 if (value is ObservableList) { 584 if (value is ObservableList) {
583 if (_observeLog.isLoggable(Level.FINE)) { 585 if (_observeLog.isLoggable(Level.FINE)) {
584 _observeLog.fine('[$localName] observeArrayValue: register observer ' 586 _observeLog.fine('[$localName] observeArrayValue: register observer '
585 '$name'); 587 '$name');
586 } 588 }
587 var sub = (value as ObservableList).changes.listen((changes) { 589 var sub = (value as ObservableList).changes.listen((changes) {
588 invokeMethod(callbackName, [old]); 590 invokeMethod(this, callbackName, [old]);
589 }); 591 });
590 registerObserver('${MirrorSystem.getName(name)}__array', sub); 592 registerObserver('${MirrorSystem.getName(name)}__array', sub);
591 } 593 }
592 } 594 }
593 595
594 void unbindProperty(String name) => unregisterObserver(name); 596 void unbindProperty(String name) => unregisterObserver(name);
595 597
596 void unbindAllProperties() { 598 void unbindAllProperties() {
597 if (_propertyObserver != null) { 599 if (_propertyObserver != null) {
598 _propertyObserver.cancel(); 600 _propertyObserver.cancel();
599 _propertyObserver = null; 601 _propertyObserver = null;
600 } 602 }
601 unregisterObservers(); 603 unregisterObservers();
602 } 604 }
603 605
604 /** Bookkeeping observers for memory management. */ 606 /** Bookkeeping observers for memory management. */
605 void registerObserver(String name, StreamSubscription sub) { 607 void registerObserver(String name, StreamSubscription sub) {
606 if (_observers == null) { 608 if (_observers == null) {
607 _observers = new Map<String, StreamSubscription>(); 609 _observers = new Map<String, StreamSubscription>();
608 } 610 }
609 _observers[name] = sub; 611 _observers[name] = sub;
610 } 612 }
611 613
612 bool unregisterObserver(String name) { 614 bool unregisterObserver(String name) {
613 var sub = _observers.remove(name); 615 var sub = _observers.remove(name);
614 if (sub == null) return false; 616 if (sub == null) return false;
615 subl.cancel(); 617 sub.cancel();
Jennifer Messerly 2013/10/25 22:00:41 ouch! nice catch
Siggi Cherem (dart-lang) 2013/10/26 00:02:35 dart2js warning =)
616 return true; 618 return true;
617 } 619 }
618 620
619 void unregisterObservers() { 621 void unregisterObservers() {
620 if (_observers == null) return; 622 if (_observers == null) return;
621 for (var sub in _observers.values) sub.cancel(); 623 for (var sub in _observers.values) sub.cancel();
622 _observers.clear(); 624 _observers.clear();
623 _observers = null; 625 _observers = null;
624 } 626 }
625 627
626 /** 628 /**
627 * Bind a [property] in this object to a [path] in model. *Note* in Dart it 629 * Bind a [property] in this object to a [path] in model. *Note* in Dart it
628 * is necessary to also define the field: 630 * is necessary to also define the field:
629 * 631 *
630 * var myProperty; 632 * var myProperty;
631 * 633 *
632 * created() { 634 * ready() {
633 * super.created(); 635 * super.ready();
634 * bindProperty(#myProperty, this, 'myModel.path.to.otherProp'); 636 * bindProperty(#myProperty, this, 'myModel.path.to.otherProp');
635 * } 637 * }
636 */ 638 */
637 // TODO(jmesserly): replace with something more localized, like: 639 // TODO(jmesserly): replace with something more localized, like:
638 // @ComputedField('myModel.path.to.otherProp'); 640 // @ComputedField('myModel.path.to.otherProp');
639 NodeBinding bindProperty(Symbol name, Object model, String path) => 641 NodeBinding bindProperty(Symbol name, Object model, String path) =>
640 // apply Polymer two-way reference binding 642 // apply Polymer two-way reference binding
641 _bindProperties(this, name, model, path); 643 _bindProperties(this, name, model, path);
642 644
643 /** 645 /**
(...skipping 27 matching lines...) Expand all
671 void addHostListeners() { 673 void addHostListeners() {
672 var events = _declaration._eventDelegates; 674 var events = _declaration._eventDelegates;
673 if (events.isEmpty) return; 675 if (events.isEmpty) return;
674 676
675 if (_eventsLog.isLoggable(Level.FINE)) { 677 if (_eventsLog.isLoggable(Level.FINE)) {
676 _eventsLog.fine('[$localName] addHostListeners: $events'); 678 _eventsLog.fine('[$localName] addHostListeners: $events');
677 } 679 }
678 addNodeListeners(this, events.keys, hostEventListener); 680 addNodeListeners(this, events.keys, hostEventListener);
679 } 681 }
680 682
681 /** Attach event listeners inside a shadow [root]. */
682 void addInstanceListeners(Node root, Element template) {
683 var templateDelegates = _declaration._templateDelegates;
684 if (templateDelegates == null) return;
685 var events = templateDelegates[template];
686 if (events == null) return;
687
688 if (_eventsLog.isLoggable(Level.FINE)) {
689 _eventsLog.fine('[$localName] addInstanceListeners: $events');
690 }
691 addNodeListeners(root, events, instanceEventListener);
692 }
693
694 void addNodeListeners(Node node, Iterable<String> events, 683 void addNodeListeners(Node node, Iterable<String> events,
695 void listener(Event e)) { 684 void listener(Event e)) {
696 685
697 for (var name in events) { 686 for (var name in events) {
698 addNodeListener(node, name, listener); 687 addNodeListener(node, name, listener);
699 } 688 }
700 } 689 }
701 690
702 void addNodeListener(Node node, String event, void listener(Event e)) { 691 void addNodeListener(Node node, String event, void listener(Event e)) {
703 node.on[event].listen(listener); 692 node.on[event].listen(listener);
704 } 693 }
705 694
706 void hostEventListener(Event event) { 695 void hostEventListener(Event event) {
707 // TODO(jmesserly): do we need this check? It was using cancelBubble, see: 696 // TODO(jmesserly): do we need this check? It was using cancelBubble, see:
708 // https://github.com/Polymer/polymer/issues/292 697 // https://github.com/Polymer/polymer/issues/292
709 if (!event.bubbles) return; 698 if (!event.bubbles) return;
710 699
711 bool log = _eventsLog.isLoggable(Level.FINE); 700 bool log = _eventsLog.isLoggable(Level.FINE);
712 if (log) { 701 if (log) {
713 _eventsLog.fine('>>> [$localName]: hostEventListener(${event.type})'); 702 _eventsLog.fine('>>> [$localName]: hostEventListener(${event.type})');
714 } 703 }
715 704
716 var h = findEventDelegate(event); 705 var h = findEventDelegate(event);
717 if (h != null) { 706 if (h != null) {
718 if (log) _eventsLog.fine('[$localName] found host handler name [$h]'); 707 if (log) _eventsLog.fine('[$localName] found host handler name [$h]');
719 var detail = event is CustomEvent ? 708 var detail = event is CustomEvent ?
720 (event as CustomEvent).detail : null; 709 (event as CustomEvent).detail : null;
721 // TODO(jmesserly): cache the symbols? 710 // TODO(jmesserly): cache the symbols?
722 dispatchMethod(new Symbol(h), [event, detail, this]); 711 dispatchMethod(this, h, [event, detail, this]);
723 } 712 }
724 713
725 if (log) { 714 if (log) {
726 _eventsLog.fine('<<< [$localName]: hostEventListener(${event.type})'); 715 _eventsLog.fine('<<< [$localName]: hostEventListener(${event.type})');
727 } 716 }
728 } 717 }
729 718
730 String findEventDelegate(Event event) => 719 String findEventDelegate(Event event) =>
731 _declaration._eventDelegates[_eventNameFromType(event.type)]; 720 _declaration._eventDelegates[_eventNameFromType(event.type)];
732 721
733 /** Call [methodName] method on [this] with [args], if the method exists. */ 722 /**
734 // TODO(jmesserly): I removed the [node] argument as it was unused. Reconcile. 723 * Calls [methodOrCallback] with [args] if it is a closure, otherwise, treat
735 void dispatchMethod(Symbol methodName, List args) { 724 * it as a method name in [object], and invoke it.
725 */
726 void dispatchMethod(object, callbackOrMethod, List args) {
736 bool log = _eventsLog.isLoggable(Level.FINE); 727 bool log = _eventsLog.isLoggable(Level.FINE);
737 if (log) _eventsLog.fine('>>> [$localName]: dispatch $methodName'); 728 if (log) _eventsLog.fine('>>> [$localName]: dispatch $callbackOrMethod');
738 729
739 invokeMethod(methodName, args); 730 if (callbackOrMethod is Function) {
731 Function.apply(callbackOrMethod, args);
732 } else if (callbackOrMethod is String) {
733 invokeMethod(object, new Symbol(callbackOrMethod), args);
734 } else {
735 _eventsLog.warning('invalid callback');
736 }
740 737
741 if (log) _eventsLog.info('<<< [$localName]: dispatch $methodName'); 738 if (log) _eventsLog.info('<<< [$localName]: dispatch $callbackOrMethod');
742 } 739 }
743 740
744 invokeMethod(Symbol methodName, List args) { 741 /** Call [methodName] method on [receiver] with [args]. */
742 static invokeMethod(receiver, Symbol methodName, List args) {
Jennifer Messerly 2013/10/25 22:00:41 fyi -- this is an instance method in Polymer: htt
Siggi Cherem (dart-lang) 2013/10/26 00:02:35 Done.
745 // TODO(sigmund): consider making callbacks list all arguments 743 // TODO(sigmund): consider making callbacks list all arguments
746 // explicitly. Unless VM mirrors are optimized first, this will be expensive 744 // explicitly. Unless VM mirrors are optimized first, this will be expensive
747 // once custom elements extend directly from Element (see issue 11108). 745 // once custom elements extend directly from Element (see issue 11108).
748 var self = reflect(this); 746 var receiverMirror = reflect(receiver);
749 var method = self.type.methods[methodName]; 747 var method = receiverMirror.type.methods[methodName];
750 if (method != null) { 748 if (method != null) {
751 // This will either truncate the argument list or extend it with extra 749 // This will either truncate the argument list or extend it with extra
752 // null arguments, so it will match the signature. 750 // null arguments, so it will match the signature.
753 // TODO(sigmund): consider accepting optional arguments when we can tell 751 // TODO(sigmund): consider accepting optional arguments when we can tell
754 // them appart from named arguments (see http://dartbug.com/11334) 752 // them appart from named arguments (see http://dartbug.com/11334)
755 args.length = method.parameters.where((p) => !p.isOptional).length; 753 args.length = method.parameters.where((p) => !p.isOptional).length;
756 } 754 }
757 return self.invoke(methodName, args).reflectee; 755 return receiverMirror.invoke(methodName, args).reflectee;
758 }
759
760 void instanceEventListener(Event event) {
761 _listenLocal(this, event);
762 }
763
764 // TODO(sjmiles): much of the below privatized only because of the vague
765 // notion this code is too fiddly and we need to revisit the core feature
766 void _listenLocal(Polymer host, Event event) {
Jennifer Messerly 2013/10/25 22:00:41 yay! so much code gone :)
767 // TODO(jmesserly): do we need this check? It was using cancelBubble, see:
768 // https://github.com/Polymer/polymer/issues/292
769 if (!event.bubbles) return;
770
771 bool log = _eventsLog.isLoggable(Level.FINE);
772 if (log) _eventsLog.fine('>>> [$localName]: listenLocal [${event.type}]');
773
774 final eventOn = '$_EVENT_PREFIX${_eventNameFromType(event.type)}';
775 if (event.path == null) {
776 _listenLocalNoEventPath(host, event, eventOn);
777 } else {
778 _listenLocalEventPath(host, event, eventOn);
779 }
780
781 if (log) _eventsLog.fine('<<< [$localName]: listenLocal [${event.type}]');
782 }
783
784 static void _listenLocalEventPath(Polymer host, Event event, String eventOn) {
785 var c = null;
786 for (var target in event.path) {
787 // if we hit host, stop
788 if (identical(target, host)) return;
789
790 // find a controller for the target, unless we already found `host`
791 // as a controller
792 c = identical(c, host) ? c : _findController(target);
793
794 // if we have a controller, dispatch the event, and stop if the handler
795 // returns true
796 if (c != null && _handleEvent(c, target, event, eventOn)) {
797 return;
798 }
799 }
800 }
801
802 // TODO(sorvell): remove when ShadowDOM polyfill supports event path.
803 // Note that _findController will not return the expected controller when the
804 // event target is a distributed node. This is because we cannot traverse
805 // from a composed node to a node in shadowRoot.
806 // This will be addressed via an event path api
807 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21066
808 static void _listenLocalNoEventPath(Polymer host, Event event,
809 String eventOn) {
810
811 if (_eventsLog.isLoggable(Level.FINE)) {
812 _eventsLog.fine('event.path() not supported for ${event.type}');
813 }
814
815 var target = event.target;
816 var c = null;
817 // if we hit dirt or host, stop
818 while (target != null && target != host) {
819 // find a controller for target `t`, unless we already found `host`
820 // as a controller
821 c = identical(c, host) ? c : _findController(target);
822
823 // if we have a controller, dispatch the event, return 'true' if
824 // handler returns true
825 if (c != null && _handleEvent(c, target, event, eventOn)) {
826 return;
827 }
828 target = target.parent;
829 }
830 }
831
832 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot
833 // was created on a PolymerElement.
834 static Polymer _findController(Node node) {
835 while (node.parentNode != null) {
836 node = node.parentNode;
837 }
838 return _shadowHost[node];
839 }
840
841 static bool _handleEvent(Polymer ctrlr, Node node, Event event,
842 String eventOn) {
843
844 // Note: local events are listened only in the shadow root. This dynamic
845 // lookup is used to distinguish determine whether the target actually has a
846 // listener, and if so, to determine lazily what's the target method.
847 var name = node is Element ? (node as Element).attributes[eventOn] : null;
848 if (name != null && _handleIfNotHandled(node, event)) {
849 if (_eventsLog.isLoggable(Level.FINE)) {
850 _eventsLog.fine('[${ctrlr.localName}] found handler name [$name]');
851 }
852 var detail = event is CustomEvent ?
853 (event as CustomEvent).detail : null;
854
855 if (node != null) {
856 // TODO(jmesserly): cache symbols?
857 ctrlr.dispatchMethod(new Symbol(name), [event, detail, node]);
858 }
859 }
860
861 // TODO(jmesserly): do we need this? It was using cancelBubble, see:
862 // https://github.com/Polymer/polymer/issues/292
863 return !event.bubbles;
864 }
865
866 // TODO(jmesserly): I don't understand this bit. It seems to be a duplicate
867 // delivery prevention mechanism?
868 static bool _handleIfNotHandled(Node node, Event event) {
869 var list = _eventHandledTable[event];
870 if (list == null) _eventHandledTable[event] = list = new Set<Node>();
871 if (!list.contains(node)) {
872 list.add(node);
873 return true;
874 }
875 return false;
876 } 756 }
877 757
878 /** 758 /**
879 * Invokes a function asynchronously. 759 * Invokes a function asynchronously.
880 * This will call `Platform.flush()` and then return a `new Timer` 760 * This will call `Platform.flush()` and then return a `new Timer`
881 * with the provided [method] and [timeout]. 761 * with the provided [method] and [timeout].
882 * 762 *
883 * If you would prefer to run the callback using 763 * If you would prefer to run the callback using
884 * [window.requestAnimationFrame], see the [async] method. 764 * [window.requestAnimationFrame], see the [async] method.
885 */ 765 */
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after
1138 class PolymerElement extends HtmlElement with Polymer, Observable { 1018 class PolymerElement extends HtmlElement with Polymer, Observable {
1139 PolymerElement.created() : super.created() { 1019 PolymerElement.created() : super.created() {
1140 polymerCreated(); 1020 polymerCreated();
1141 } 1021 }
1142 } 1022 }
1143 1023
1144 class _PropertyValue { 1024 class _PropertyValue {
1145 Object oldValue, newValue; 1025 Object oldValue, newValue;
1146 _PropertyValue(this.oldValue); 1026 _PropertyValue(this.oldValue);
1147 } 1027 }
1028
1029 // TODO(sorvell): we're patching the syntax while evaluating
1030 // event bindings. we'll move this to a better spot when that's done
1031 class _PolymerExpressionsWithEventDelegate extends PolymerExpressions {
Jennifer Messerly 2013/10/25 22:00:41 I wonder if we should make this functionality avai
Siggi Cherem (dart-lang) 2013/10/26 00:02:35 Good idea, done.
1032 /**
1033 * Bind events via attributes of the form `on-eventName`. This method hooks
1034 * into the model syntax and does adds event listeners as needed. By default,
1035 * binding paths are always method names on the root model, the custom element
1036 * in which the node exists. Adding a '@' in the path directs the event
1037 * binding to use the model path as the event listener. In both cases, the
1038 * actual listener is attached to a generic method which evaluates the bound
1039 * path at event execution time.
1040 */
1041 // from src/instance/event.js#prepareBinding
1042 // Dart note: template_binding doesn't have the notion of a delegateBinding
1043 // function, so we implement this by overriding getBinding instead.
1044 getBinding(model, String path, name, node) {
1045 // if lhs an event prefix,
1046 if (name is! String || !_hasEventPrefix(name)) {
1047 return super.getBinding(model, path, name, node);
1048 }
1049
1050 // provide an event-binding callback.
1051 // return (model, name, node) {
1052 if (_eventsLog.isLoggable(Level.FINE)) {
1053 _eventsLog.fine('event: [${node.localName}].$name => '
1054 '[${model.localName}].$path())');
1055 }
1056 var eventName = _removeEventPrefix(name);
1057 // TODO(sigmund): polymer.js dropped event translations. reconcile?
1058 var translated = _eventTranslations[eventName];
1059 eventName = translated != null ? translated : eventName;
1060 return node.on[eventName].listen((event) {
1061 var ctrlr = _findController(node);
1062 if (ctrlr is! Polymer) return;
Jennifer Messerly 2013/10/25 22:00:41 fyi -- _findController should not return something
1063 var obj = ctrlr;
1064 var method = path;
1065 if (path[0] == '@') {
1066 obj = model;
1067 // Dart note: using getBinding gets us the result of evaluating the
1068 // original path (without the @) as a normal expression.
1069 method = super.getBinding(model, path.substring(1), name, node).value;
1070 }
1071 var detail = event is CustomEvent ?
1072 (event as CustomEvent).detail : null;
1073 ctrlr.dispatchMethod(obj, method, [event, detail, node]);
1074 });
1075 }
1076
1077 // TODO(jmesserly): this won't find the correct host unless the ShadowRoot
1078 // was created on a PolymerElement.
1079 static Polymer _findController(Node node) {
1080 while (node.parentNode != null) {
1081 node = node.parentNode;
1082 }
1083 return _shadowHost[node];
1084 }
1085 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698