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

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

Issue 420673002: Roll polymer packages to version 0.3.4 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months 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
« no previous file with comments | « pkg/polymer/lib/src/events.dart ('k') | pkg/polymer/lib/src/js/polymer/README.md » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 /// Use this annotation to publish a field 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.
11 /// 11 ///
12 /// For example: 12 /// For example:
13 /// 13 ///
14 /// class MyPlaybackElement extends PolymerElement { 14 /// class MyPlaybackElement extends PolymerElement {
15 /// // This will be available as an HTML attribute, for example: 15 /// // This will be available as an HTML attribute, for example:
16 /// // 16 /// //
17 /// // <my-playback volume="11"> 17 /// // <my-playback volume="11">
18 /// // 18 /// //
19 /// // It will support initialization and data-binding via <template>: 19 /// // It will support initialization and data-binding via <template>:
20 /// // 20 /// //
21 /// // <template> 21 /// // <template>
22 /// // <my-playback volume="{{x}}"> 22 /// // <my-playback volume="{{x}}">
23 /// // </template> 23 /// // </template>
24 /// // 24 /// //
25 /// // If the template is instantiated or given a model, `x` will be 25 /// // If the template is instantiated or given a model, `x` will be
26 /// // used for this field and updated whenever `volume` changes. 26 /// // used for this field and updated whenever `volume` changes.
27 /// @published double volume; 27 /// @published
28 /// double get volume => readValue(#volume);
29 /// set volume(double newValue) => writeValue(#volume, newValue);
28 /// 30 ///
29 /// // This will be available as an HTML attribute, like above, but it 31 /// // This will be available as an HTML attribute, like above, but it
30 /// // will also serialize values set to the property to the attribute. 32 /// // will also serialize values set to the property to the attribute.
31 /// // In other words, attributes['volume2'] will contain a serialized 33 /// // In other words, attributes['volume2'] will contain a serialized
32 /// // version of this field. 34 /// // version of this field.
33 /// @PublishedProperty(reflect: true) double volume2; 35 /// @PublishedProperty(reflect: true)
36 /// double get volume2 => readValue(#volume2);
37 /// set volume2(double newValue) => writeValue(#volume2, newValue);
34 /// } 38 /// }
35 /// 39 ///
40 /// **Important note**: the pattern using `readValue` and `writeValue`
41 /// guarantees that reading the property will give you the latest value at any
42 /// given time, even if change notifications have not been propagated.
43 ///
44 /// We still support using @published on a field, but this will not
45 /// provide the same guarantees, so this is discouraged. For example:
46 ///
47 /// // Avoid this if possible. This will be available as an HTML
48 /// // attribute too, but you might need to delay reading volume3
49 /// // asynchronously to guarantee that you read the latest value
50 /// // set through bindings.
51 /// @published double volume3;
36 const published = const PublishedProperty(); 52 const published = const PublishedProperty();
37 53
38 /// An annotation used to publish a field as an attribute. See [published]. 54 /// An annotation used to publish a field as an attribute. See [published].
39 class PublishedProperty extends ObservableProperty { 55 class PublishedProperty extends ObservableProperty {
40 /// Whether the property value should be reflected back to the HTML attribute. 56 /// Whether the property value should be reflected back to the HTML attribute.
41 final bool reflect; 57 final bool reflect;
42 58
43 const PublishedProperty({this.reflect: false}); 59 const PublishedProperty({this.reflect: false});
44 } 60 }
45 61
(...skipping 19 matching lines...) Expand all
65 if (n is! Iterable) { 81 if (n is! Iterable) {
66 throw new UnsupportedError('ObserveProperty takes either an Iterable of ' 82 throw new UnsupportedError('ObserveProperty takes either an Iterable of '
67 'names, or a space separated String, instead of `$n`.'); 83 'names, or a space separated String, instead of `$n`.');
68 } 84 }
69 return n; 85 return n;
70 } 86 }
71 87
72 const ObserveProperty(this._names); 88 const ObserveProperty(this._names);
73 } 89 }
74 90
91 /// Use this to create computed properties that are updated automatically. The
92 /// annotation includes a polymer expression that describes how this property
93 /// value can be expressed in terms of the values of other properties. For
94 /// example:
95 ///
96 /// class MyPlaybackElement extends PolymerElement {
97 /// @observable int x;
98 ///
99 /// // Reading xTimes2 will return x * 2.
100 /// @ComputedProperty('x * 2')
101 /// int get xTimes2 => readValue(#xTimes2);
102 ///
103 /// If the polymer expression is assignable, you can also define a setter for
104 /// it. For example:
105 ///
106 /// // Reading c will return a.b, writing c will update a.b.
107 /// @ComputedProperty('a.b')
108 /// get c => readValue(#c);
109 /// set c(newValue) => writeValue(#c, newValue);
110 ///
111 /// The expression can do anything that is allowed in a polymer expresssion,
112 /// even making calls to methods in your element. However, dependencies that are
113 /// only used within those methods and that are not visible in the polymer
114 /// expression, will not be observed. For example:
115 ///
116 /// // Because `x` only appears inside method `m`, we will not notice
117 /// // that `d` has changed if `x` is modified. However, `d` will be
118 /// // updated whenever `c` changes.
119 /// @ComputedProperty('m(c)')
120 /// get d => readValue(#d);
121 ///
122 /// m(c) => c + x;
123 class ComputedProperty {
124 /// A polymer expression, evaluated in the context of the custom element where
125 /// this annotation is used.
126 final String expression;
127
128 const ComputedProperty(this.expression);
129 }
75 130
76 /// Base class for PolymerElements deriving from HtmlElement. 131 /// Base class for PolymerElements deriving from HtmlElement.
77 /// 132 ///
78 /// See [Polymer]. 133 /// See [Polymer].
79 class PolymerElement extends HtmlElement with Polymer, Observable { 134 class PolymerElement extends HtmlElement with Polymer, Observable {
80 PolymerElement.created() : super.created() { 135 PolymerElement.created() : super.created() {
81 polymerCreated(); 136 polymerCreated();
82 } 137 }
83 } 138 }
84 139
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 static Future get onReady => _onReady.future; 222 static Future get onReady => _onReady.future;
168 223
169 /// The most derived `<polymer-element>` declaration for this element. 224 /// The most derived `<polymer-element>` declaration for this element.
170 PolymerDeclaration get element => _element; 225 PolymerDeclaration get element => _element;
171 PolymerDeclaration _element; 226 PolymerDeclaration _element;
172 227
173 /// Deprecated: use [element] instead. 228 /// Deprecated: use [element] instead.
174 @deprecated PolymerDeclaration get declaration => _element; 229 @deprecated PolymerDeclaration get declaration => _element;
175 230
176 Map<String, StreamSubscription> _namedObservers; 231 Map<String, StreamSubscription> _namedObservers;
177 List<Iterable<Bindable>> _observers = []; 232 List<Bindable> _observers = [];
178 233
179 bool _unbound; // lazy-initialized 234 bool _unbound; // lazy-initialized
180 PolymerJob _unbindAllJob; 235 PolymerJob _unbindAllJob;
181 236
182 CompoundObserver _propertyObserver; 237 CompoundObserver _propertyObserver;
183 bool _readied = false; 238 bool _readied = false;
184 239
185 JsObject _jsElem; 240 JsObject _jsElem;
186 241
187 /// Returns the object that should be used as the event controller for 242 /// Returns the object that should be used as the event controller for
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 if (_element != null) return _element.name; 286 if (_element != null) return _element.name;
232 var isAttr = attributes['is']; 287 var isAttr = attributes['is'];
233 return (isAttr == null || isAttr == '') ? localName : isAttr; 288 return (isAttr == null || isAttr == '') ? localName : isAttr;
234 } 289 }
235 290
236 /// By default the data bindings will be cleaned up when this custom element 291 /// By default the data bindings will be cleaned up when this custom element
237 /// is detached from the document. Overriding this to return `true` will 292 /// is detached from the document. Overriding this to return `true` will
238 /// prevent that from happening. 293 /// prevent that from happening.
239 bool get preventDispose => false; 294 bool get preventDispose => false;
240 295
296 /// Properties exposed by this element.
297 // Dart note: unlike Javascript we can't override the original property on
298 // the object, so we use this mechanism instead to define properties. See more
299 // details in [_PropertyAccessor].
300 Map<Symbol, _PropertyAccessor> _properties = {};
301
302 /// Helper to implement a property with the given [name]. This is used for
303 /// normal and computed properties. Normal properties can provide the initial
304 /// value using the [initialValue] function. Computed properties ignore
305 /// [initialValue], their value is derived from the expression in the
306 /// [ComputedProperty] annotation that appears above the getter that uses this
307 /// helper.
308 readValue(Symbol name, [initialValue()]) {
309 var property = _properties[name];
310 if (property == null) {
311 var value;
312 // Dart note: most computed properties are created in advance in
313 // createComputedProperties, but if one computed property depends on
314 // another, the declaration order might matter. Rather than trying to
315 // register them in order, we include here also the option of lazily
316 // creating the property accessor on the first read.
317 var binding = _getBindingForComputedProperty(name);
318 if (binding == null) { // normal property
319 value = initialValue != null ? initialValue() : null;
320 } else {
321 value = binding.value;
322 }
323 property = _properties[name] = new _PropertyAccessor(name, this, value);
324 }
325 return property.value;
326 }
327
328 /// Helper to implement a setter of a property with the given [name] on a
329 /// polymer element. This can be used on normal properties and also on
330 /// computed properties, as long as the expression used for the computed
331 /// property is assignable (see [ComputedProperty]).
332 writeValue(Symbol name, newValue) {
333 var property = _properties[name];
334 if (property == null) {
335 // Note: computed properties are created in advance in
336 // createComputedProperties, so we should only need to create here
337 // non-computed properties.
338 property = _properties[name] = new _PropertyAccessor(name, this, null);
339 }
340 property.value = newValue;
341 }
342
241 /// If this class is used as a mixin, this method must be called from inside 343 /// If this class is used as a mixin, this method must be called from inside
242 /// of the `created()` constructor. 344 /// of the `created()` constructor.
243 /// 345 ///
244 /// If this class is a superclass, calling `super.created()` is sufficient. 346 /// If this class is a superclass, calling `super.created()` is sufficient.
245 void polymerCreated() { 347 void polymerCreated() {
246 var t = nodeBind(this).templateInstance; 348 var t = nodeBind(this).templateInstance;
247 if (t != null && t.model != null) { 349 if (t != null && t.model != null) {
248 window.console.warn('Attributes on $_name were data bound ' 350 window.console.warn('Attributes on $_name were data bound '
249 'prior to Polymer upgrading the element. This may result in ' 351 'prior to Polymer upgrading the element. This may result in '
250 'incorrect binding types.'); 352 'incorrect binding types.');
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 386
285 /// Initialize JS interop for this element. For now we just initialize the 387 /// Initialize JS interop for this element. For now we just initialize the
286 // JsObject, but in the future we could also initialize JS APIs here. 388 // JsObject, but in the future we could also initialize JS APIs here.
287 _initJsObject() { 389 _initJsObject() {
288 _jsElem = new JsObject.fromBrowserObject(this); 390 _jsElem = new JsObject.fromBrowserObject(this);
289 } 391 }
290 392
291 makeElementReady() { 393 makeElementReady() {
292 if (_readied) return; 394 if (_readied) return;
293 _readied = true; 395 _readied = true;
396 createComputedProperties();
294 397
295 // TODO(sorvell): We could create an entry point here 398 // TODO(sorvell): We could create an entry point here
296 // for the user to compute property values. 399 // for the user to compute property values.
297 // process declarative resources 400 // process declarative resources
298 parseDeclarations(_element); 401 parseDeclarations(_element);
299 // TODO(sorvell): CE polyfill uses unresolved attribute to simulate 402 // TODO(sorvell): CE polyfill uses unresolved attribute to simulate
300 // :unresolved; remove this attribute to be compatible with native 403 // :unresolved; remove this attribute to be compatible with native
301 // CE. 404 // CE.
302 attributes.remove('unresolved'); 405 attributes.remove('unresolved');
303 // user entry point 406 // user entry point
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 /// 650 ///
548 /// You can override this method to change the instantiation behavior of the 651 /// You can override this method to change the instantiation behavior of the
549 /// template, for example to use a different data-binding syntax. 652 /// template, for example to use a different data-binding syntax.
550 DocumentFragment instanceTemplate(Element template) { 653 DocumentFragment instanceTemplate(Element template) {
551 var syntax = this.syntax; 654 var syntax = this.syntax;
552 var t = templateBind(template); 655 var t = templateBind(template);
553 if (syntax == null && t.bindingDelegate == null) { 656 if (syntax == null && t.bindingDelegate == null) {
554 syntax = element.syntax; 657 syntax = element.syntax;
555 } 658 }
556 var dom = t.createInstance(this, syntax); 659 var dom = t.createInstance(this, syntax);
557 registerObservers(getTemplateInstanceBindings(dom)); 660 _observers.addAll(getTemplateInstanceBindings(dom));
558 return dom; 661 return dom;
559 } 662 }
560 663
561 Bindable bind(String name, bindable, {bool oneTime: false}) { 664 Bindable bind(String name, bindable, {bool oneTime: false}) {
562 var decl = propertyForAttribute(name); 665 var decl = propertyForAttribute(name);
563 if (decl == null) { 666 if (decl == null) {
564 // Cannot call super.bind because template_binding is its own package 667 // Cannot call super.bind because template_binding is its own package
565 return nodeBindFallback(this).bind(name, bindable, oneTime: oneTime); 668 return nodeBindFallback(this).bind(name, bindable, oneTime: oneTime);
566 } else { 669 } else {
567 // use n-way Polymer binding 670 // use n-way Polymer binding
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
633 _forNodeTree(child, callback); 736 _forNodeTree(child, callback);
634 } 737 }
635 } 738 }
636 739
637 /// Set up property observers. 740 /// Set up property observers.
638 void createPropertyObserver() { 741 void createPropertyObserver() {
639 final observe = _element._observe; 742 final observe = _element._observe;
640 if (observe != null) { 743 if (observe != null) {
641 var o = _propertyObserver = new CompoundObserver(); 744 var o = _propertyObserver = new CompoundObserver();
642 // keep track of property observer so we can shut it down 745 // keep track of property observer so we can shut it down
643 registerObservers([o]); 746 _observers.add(o);
644 747
645 for (var path in observe.keys) { 748 for (var path in observe.keys) {
646 o.addPath(this, path); 749 o.addPath(this, path);
647 750
648 // TODO(jmesserly): on the Polymer side it doesn't look like they 751 // TODO(jmesserly): on the Polymer side it doesn't look like they
649 // will observe arrays unless it is a length == 1 path. 752 // will observe arrays unless it is a length == 1 path.
650 observeArrayValue(path, path.getValueFrom(this), null); 753 observeArrayValue(path, path.getValueFrom(this), null);
651 } 754 }
652 } 755 }
653 } 756 }
654 757
655 void openPropertyObserver() { 758 void openPropertyObserver() {
656 if (_propertyObserver != null) { 759 if (_propertyObserver != null) {
657 _propertyObserver.open(notifyPropertyChanges); 760 _propertyObserver.open(notifyPropertyChanges);
658 } 761 }
659 // Dart note: we need an extra listener. 762
660 // see comment on [_propertyChange]. 763 // Dart note: we need an extra listener only to continue supporting
764 // @published properties that follow the old syntax until we get rid of it.
765 // This workaround has timing issues so we prefer the new, not so nice,
766 // syntax.
661 if (_element._publish != null) { 767 if (_element._publish != null) {
662 changes.listen(_propertyChange); 768 changes.listen(_propertyChangeWorkaround);
663 } 769 }
664 } 770 }
665 771
666 /// Responds to property changes on this element. 772 /// Responds to property changes on this element.
667 void notifyPropertyChanges(List newValues, Map oldValues, List paths) { 773 void notifyPropertyChanges(List newValues, Map oldValues, List paths) {
668 final observe = _element._observe; 774 final observe = _element._observe;
669 final called = new HashSet(); 775 final called = new HashSet();
670 776
671 oldValues.forEach((i, oldValue) { 777 oldValues.forEach((i, oldValue) {
672 final newValue = newValues[i]; 778 final newValue = newValues[i];
(...skipping 25 matching lines...) Expand all
698 // Dart note: had to rename this to avoid colliding with 804 // Dart note: had to rename this to avoid colliding with
699 // Observable.deliverChanges. Even worse, super calls aren't possible or 805 // Observable.deliverChanges. Even worse, super calls aren't possible or
700 // it prevents Polymer from being a mixin, so we can't override it even if 806 // it prevents Polymer from being a mixin, so we can't override it even if
701 // we wanted to. 807 // we wanted to.
702 void deliverPropertyChanges() { 808 void deliverPropertyChanges() {
703 if (_propertyObserver != null) { 809 if (_propertyObserver != null) {
704 _propertyObserver.deliver(); 810 _propertyObserver.deliver();
705 } 811 }
706 } 812 }
707 813
708 // Dart note: this is not called by observe-js because we don't have 814 // Dart note: this workaround is only for old-style @published properties,
709 // the mechanism for defining properties on our proto. 815 // which have timing issues. See _bindOldStylePublishedProperty below.
710 // TODO(jmesserly): this has similar timing issues as our @published 816 // TODO(sigmund): deprecate this.
711 // properties do generally -- it's async when it should be sync. 817 void _propertyChangeWorkaround(List<ChangeRecord> records) {
712 void _propertyChange(List<ChangeRecord> records) {
713 for (var record in records) { 818 for (var record in records) {
714 if (record is! PropertyChangeRecord) continue; 819 if (record is! PropertyChangeRecord) continue;
715 820
716 final name = smoke.symbolToName(record.name); 821 var name = record.name;
717 final reflect = _element._reflect; 822 // The setter of a new-style property will create an accessor in
718 if (reflect != null && reflect.contains(name)) { 823 // _properties[name]. We can skip the workaround for those properties.
719 reflectPropertyToAttribute(name); 824 if (_properties[name] != null) continue;
720 } 825 _propertyChange(name);
721 } 826 }
722 } 827 }
723 828
829 void _propertyChange(Symbol nameSymbol) {
830 var name = smoke.symbolToName(nameSymbol);
831 var reflect = _element._reflect;
832 if (reflect != null && reflect.contains(name)) {
833 reflectPropertyToAttribute(name);
834 }
835 }
836
724 void observeArrayValue(PropertyPath name, Object value, Object old) { 837 void observeArrayValue(PropertyPath name, Object value, Object old) {
725 final observe = _element._observe; 838 final observe = _element._observe;
726 if (observe == null) return; 839 if (observe == null) return;
727 840
728 // we only care if there are registered side-effects 841 // we only care if there are registered side-effects
729 var callbacks = observe[name]; 842 var callbacks = observe[name];
730 if (callbacks == null) return; 843 if (callbacks == null) return;
731 844
732 // if we are observing the previous value, stop 845 // if we are observing the previous value, stop
733 if (old is ObservableList) { 846 if (old is ObservableList) {
(...skipping 10 matching lines...) Expand all
744 } 857 }
745 var sub = value.listChanges.listen((changes) { 858 var sub = value.listChanges.listen((changes) {
746 for (var callback in callbacks) { 859 for (var callback in callbacks) {
747 smoke.invoke(this, callback, [old], adjust: true); 860 smoke.invoke(this, callback, [old], adjust: true);
748 } 861 }
749 }); 862 });
750 registerNamedObserver('${name}__array', sub); 863 registerNamedObserver('${name}__array', sub);
751 } 864 }
752 } 865 }
753 866
754 void registerObservers(Iterable<Bindable> observers) { 867 emitPropertyChangeRecord(Symbol name, newValue, oldValue) {
755 _observers.add(observers); 868 if (identical(oldValue, newValue)) return;
869 _propertyChange(name);
756 } 870 }
757 871
872 bindToAccessor(Symbol name, Bindable bindable, {resolveBindingValue: false}) {
873 // Dart note: our pattern is to declare the initial value in the getter. We
874 // read it via smoke to ensure that the value is initialized correctly.
875 var oldValue = smoke.read(this, name);
876 var property = _properties[name];
877 if (property == null) {
878 // We know that _properties[name] is null only for old-style @published
879 // properties. This fallback is here to make it easier to deprecate the
880 // old-style of published properties, which have bad timing guarantees
881 // (see comment in _PolymerBinding).
882 return _bindOldStylePublishedProperty(name, bindable, oldValue);
883 }
884
885 property.bindable = bindable;
886 var value = bindable.open(property.updateValue);
887
888 if (resolveBindingValue) {
889 // capture A's value if B's value is null or undefined,
890 // otherwise use B's value
891 var v = (value == null ? oldValue : value);
892 if (!identical(value, oldValue)) {
893 bindable.value = value = v;
894 }
895 }
896
897 property.updateValue(value);
898 var o = new _CloseOnlyBinding(property);
899 _observers.add(o);
900 return o;
901 }
902
903 // Dart note: this fallback uses our old-style binding mechanism to be able to
904 // link @published properties with bindings. This mechanism is backwards from
905 // what Javascript does because we can't override the original property. This
906 // workaround also brings some timing issues which are described in detail in
907 // dartbug.com/18343.
908 // TODO(sigmund): deprecate old-style @published properties.
909 _bindOldStylePublishedProperty(Symbol name, Bindable bindable, oldValue) {
910 // capture A's value if B's value is null or undefined,
911 // otherwise use B's value
912 if (bindable.value == null) bindable.value = oldValue;
913
914 var o = new _PolymerBinding(this, name, bindable);
915 _observers.add(o);
916 return o;
917 }
918
919 _getBindingForComputedProperty(Symbol name) {
920 var exprString = element._computed[name];
921 if (exprString == null) return null;
922 var expr = PolymerExpressions.getExpression(exprString);
923 return PolymerExpressions.getBinding(expr, this,
924 globals: element.syntax.globals);
925 }
926
927 createComputedProperties() {
928 var computed = this.element._computed;
929 for (var name in computed.keys) {
930 try {
931 // Dart note: this is done in Javascript by modifying the prototype in
932 // declaration/properties.js, we can't do that, so we do it here.
933 var binding = _getBindingForComputedProperty(name);
934
935 // Follow up note: ideally we would only create the accessor object
936 // here, but some computed properties might depend on others and
937 // evaluating `binding.value` could try to read the value of another
938 // computed property that we haven't created yet. For this reason we
939 // also allow to also create the accessor in [readValue].
940 if (_properties[name] == null) {
941 _properties[name] = new _PropertyAccessor(name, this, binding.value);
942 }
943 bindToAccessor(name, binding);
944 } catch (e) {
945 window.console.error('Failed to create computed property $name'
946 ' (${computed[name]}): $e');
947 }
948 }
949 }
950
951 // Dart note: to simplify the code above we made registerObserver calls
952 // directly invoke _observers.add/addAll.
758 void closeObservers() { 953 void closeObservers() {
759 _observers.forEach(closeObserverList); 954 for (var o in _observers) {
955 if (o != null) o.close();
956 }
760 _observers = []; 957 _observers = [];
761 } 958 }
762 959
763 void closeObserverList(Iterable<Bindable> observers) {
764 for (var o in observers) {
765 if (o != null) o.close();
766 }
767 }
768
769 /// Bookkeeping observers for memory management. 960 /// Bookkeeping observers for memory management.
770 void registerNamedObserver(String name, StreamSubscription sub) { 961 void registerNamedObserver(String name, StreamSubscription sub) {
771 if (_namedObservers == null) { 962 if (_namedObservers == null) {
772 _namedObservers = new Map<String, StreamSubscription>(); 963 _namedObservers = new Map<String, StreamSubscription>();
773 } 964 }
774 _namedObservers[name] = sub; 965 _namedObservers[name] = sub;
775 } 966 }
776 967
777 bool closeNamedObserver(String name) { 968 bool closeNamedObserver(String name) {
778 var sub = _namedObservers.remove(name); 969 var sub = _namedObservers.remove(name);
(...skipping 14 matching lines...) Expand all
793 /// Bind the [name] property in this element to [bindable]. *Note* in Dart it 984 /// Bind the [name] property in this element to [bindable]. *Note* in Dart it
794 /// is necessary to also define the field: 985 /// is necessary to also define the field:
795 /// 986 ///
796 /// var myProperty; 987 /// var myProperty;
797 /// 988 ///
798 /// ready() { 989 /// ready() {
799 /// super.ready(); 990 /// super.ready();
800 /// bindProperty(#myProperty, 991 /// bindProperty(#myProperty,
801 /// new PathObserver(this, 'myModel.path.to.otherProp')); 992 /// new PathObserver(this, 'myModel.path.to.otherProp'));
802 /// } 993 /// }
803 Bindable bindProperty(Symbol name, Bindable bindable, {oneTime: false}) { 994 Bindable bindProperty(Symbol name, bindableOrValue, {oneTime: false}) {
804 // Dart note: normally we only reach this code when we know it's a 995 // Dart note: normally we only reach this code when we know it's a
805 // property, but if someone uses bindProperty directly they might get a 996 // property, but if someone uses bindProperty directly they might get a
806 // NoSuchMethodError either from the getField below, or from the setField 997 // NoSuchMethodError either from the getField below, or from the setField
807 // inside PolymerBinding. That doesn't seem unreasonable, but it's a slight 998 // inside PolymerBinding. That doesn't seem unreasonable, but it's a slight
808 // difference from Polymer.js behavior. 999 // difference from Polymer.js behavior.
809 1000
810 if (_bindLog.isLoggable(Level.FINE)) { 1001 if (_bindLog.isLoggable(Level.FINE)) {
811 _bindLog.fine('bindProperty: [$bindable] to [$_name].[$name]'); 1002 _bindLog.fine('bindProperty: [$bindableOrValue] to [$_name].[$name]');
812 } 1003 }
813 1004
814 // capture A's value if B's value is null or undefined, 1005 if (oneTime) {
815 // otherwise use B's value 1006 if (bindableOrValue is Bindable) {
816 // TODO(sorvell): need to review, can do with ObserverTransform 1007 _bindLog.warning('bindProperty: expected non-bindable value '
817 var v = bindable.value; 1008 'on a one-time binding to [$_name].[$name], '
818 if (v == null) { 1009 'but found $bindableOrValue.');
819 bindable.value = smoke.read(this, name); 1010 }
1011 smoke.write(this, name, bindableOrValue);
1012 return null;
820 } 1013 }
821 1014
822 // TODO(jmesserly): we need to fix this -- it doesn't work like Polymer.js 1015 return bindToAccessor(name, bindableOrValue, resolveBindingValue: true);
823 // bindings. https://code.google.com/p/dart/issues/detail?id=18343
824 // apply Polymer two-way reference binding
825 //return Observer.bindToInstance(inA, inProperty, observable,
826 // resolveBindingValue);
827 return new _PolymerBinding(this, name, bindable);
828 } 1016 }
829 1017
830 /// Attach event listeners on the host (this) element. 1018 /// Attach event listeners on the host (this) element.
831 void addHostListeners() { 1019 void addHostListeners() {
832 var events = _element._eventDelegates; 1020 var events = _element._eventDelegates;
833 if (events.isEmpty) return; 1021 if (events.isEmpty) return;
834 1022
835 if (_eventsLog.isLoggable(Level.FINE)) { 1023 if (_eventsLog.isLoggable(Level.FINE)) {
836 _eventsLog.fine('[$_name] addHostListeners: $events'); 1024 _eventsLog.fine('[$_name] addHostListeners: $events');
837 } 1025 }
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
990 1178
991 if (scope == null) return; 1179 if (scope == null) return;
992 1180
993 if (_ShadowCss != null) { 1181 if (_ShadowCss != null) {
994 cssText = _shimCssText(cssText, scope is ShadowRoot ? scope.host : null); 1182 cssText = _shimCssText(cssText, scope is ShadowRoot ? scope.host : null);
995 } 1183 }
996 var style = element.cssTextToScopeStyle(cssText, 1184 var style = element.cssTextToScopeStyle(cssText,
997 _STYLE_CONTROLLER_SCOPE); 1185 _STYLE_CONTROLLER_SCOPE);
998 applyStyleToScope(style, scope); 1186 applyStyleToScope(style, scope);
999 // cache that this style has been applied 1187 // cache that this style has been applied
1000 Set styles = _scopeStyles[scope]; 1188 styleCacheForScope(scope).add('$_name$name');
1001 if (styles == null) _scopeStyles[scope] = styles = new Set();
1002 styles.add('$_name$name');
1003 } 1189 }
1004 1190
1005 Node findStyleScope([node]) { 1191 Node findStyleScope([node]) {
1006 // find the shadow root that contains this element 1192 // find the shadow root that contains this element
1007 var n = node; 1193 var n = node;
1008 if (n == null) n = this; 1194 if (n == null) n = this;
1009 while (n.parentNode != null) { 1195 while (n.parentNode != null) {
1010 n = n.parentNode; 1196 n = n.parentNode;
1011 } 1197 }
1012 return n; 1198 return n;
1013 } 1199 }
1014 1200
1015 bool scopeHasNamedStyle(Node scope, String name) { 1201 bool scopeHasNamedStyle(Node scope, String name) =>
1016 Set styles = _scopeStyles[scope]; 1202 styleCacheForScope(scope).contains(name);
1017 return styles != null && styles.contains(name); 1203
1204 Map _polyfillScopeStyleCache = {};
1205
1206 Set styleCacheForScope(Node scope) {
1207 var styles;
1208 if (_hasShadowDomPolyfill) {
1209 var name = scope is ShadowRoot ? scope.host.localName
1210 : (scope as Element).localName;
1211 var styles = _polyfillScopeStyleCache[name];
1212 if (styles == null) _polyfillScopeStyleCache[name] = styles = new Set();
1213 } else {
1214 styles = _scopeStyles[scope];
1215 if (styles == null) _scopeStyles[scope] = styles = new Set();
1216 }
1217 return styles;
1018 } 1218 }
1019 1219
1020 static final _scopeStyles = new Expando(); 1220 static final _scopeStyles = new Expando();
1021 1221
1022 static String _shimCssText(String cssText, [Element host]) { 1222 static String _shimCssText(String cssText, [Element host]) {
1023 var name = ''; 1223 var name = '';
1024 var is_ = false; 1224 var is_ = false;
1025 if (host != null) { 1225 if (host != null) {
1026 name = host.localName; 1226 name = host.localName;
1027 is_ = host.attributes.containsKey('is'); 1227 is_ = host.attributes.containsKey('is');
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1072 /// 1272 ///
1073 /// Returns the newly created job. 1273 /// Returns the newly created job.
1074 // Dart note: renamed to scheduleJob to be a bit more consistent with Dart. 1274 // Dart note: renamed to scheduleJob to be a bit more consistent with Dart.
1075 PolymerJob scheduleJob(PolymerJob job, void callback(), [Duration wait]) { 1275 PolymerJob scheduleJob(PolymerJob job, void callback(), [Duration wait]) {
1076 if (job == null) job = new PolymerJob._(); 1276 if (job == null) job = new PolymerJob._();
1077 // Dart note: made start smarter, so we don't need to call stop. 1277 // Dart note: made start smarter, so we don't need to call stop.
1078 return job..start(callback, wait); 1278 return job..start(callback, wait);
1079 } 1279 }
1080 } 1280 }
1081 1281
1082 // Dart note: Polymer addresses n-way bindings by metaprogramming: redefine 1282 // Dart note: this is related to _bindOldStylePublishedProperty. Polymer
1083 // the property on the PolymerElement instance to always get its value from the 1283 // addresses n-way bindings by metaprogramming: redefine the property on the
1084 // model@path. We can't replicate this in Dart so we do the next best thing: 1284 // PolymerElement instance to always get its value from the model@path. This is
1085 // listen to changes on both sides and update the values. 1285 // supported in Dart using a new style of @published property declaration using
1086 // TODO(jmesserly): our approach leads to race conditions in the bindings. 1286 // the `readValue` and `writeValue` methods above. In the past we used to work
1087 // See http://code.google.com/p/dart/issues/detail?id=13567 1287 // around this by listening to changes on both sides and updating the values.
1288 // This object provides the hooks to do this.
1289 // TODO(sigmund,jmesserly): delete after a deprecation period.
1088 class _PolymerBinding extends Bindable { 1290 class _PolymerBinding extends Bindable {
1089 final Polymer _target; 1291 final Polymer _target;
1090 final Symbol _property; 1292 final Symbol _property;
1091 final Bindable _bindable; 1293 final Bindable _bindable;
1092 StreamSubscription _sub; 1294 StreamSubscription _sub;
1093 Object _lastValue; 1295 Object _lastValue;
1094 1296
1095 _PolymerBinding(this._target, this._property, this._bindable) { 1297 _PolymerBinding(this._target, this._property, this._bindable) {
1096 _sub = _target.changes.listen(_propertyValueChanged); 1298 _sub = _target.changes.listen(_propertyValueChanged);
1097 _updateNode(open(_updateNode)); 1299 _updateNode(open(_updateNode));
1098 } 1300 }
1099 1301
1100 void _updateNode(newValue) { 1302 void _updateNode(newValue) {
1101 _lastValue = newValue; 1303 _lastValue = newValue;
1102 smoke.write(_target, _property, newValue); 1304 smoke.write(_target, _property, newValue);
1305 // Note: we don't invoke emitPropertyChangeRecord here because that's
1306 // done by listening on changes on the PolymerElement.
1103 } 1307 }
1104 1308
1105 void _propertyValueChanged(List<ChangeRecord> records) { 1309 void _propertyValueChanged(List<ChangeRecord> records) {
1106 for (var record in records) { 1310 for (var record in records) {
1107 if (record is PropertyChangeRecord && record.name == _property) { 1311 if (record is PropertyChangeRecord && record.name == _property) {
1108 final newValue = smoke.read(_target, _property); 1312 final newValue = smoke.read(_target, _property);
1109 if (!identical(_lastValue, newValue)) { 1313 if (!identical(_lastValue, newValue)) {
1110 this.value = newValue; 1314 this.value = newValue;
1111 } 1315 }
1112 return; 1316 return;
1113 } 1317 }
1114 } 1318 }
1115 } 1319 }
1116 1320
1117 open(callback(value)) => _bindable.open(callback); 1321 open(callback(value)) => _bindable.open(callback);
1118 get value => _bindable.value; 1322 get value => _bindable.value;
1119 set value(newValue) => _bindable.value = newValue; 1323 set value(newValue) => _bindable.value = newValue;
1120 1324
1121 void close() { 1325 void close() {
1122 if (_sub != null) { 1326 if (_sub != null) {
1123 _sub.cancel(); 1327 _sub.cancel();
1124 _sub = null; 1328 _sub = null;
1125 } 1329 }
1126 _bindable.close(); 1330 _bindable.close();
1127 } 1331 }
1128 } 1332 }
1129 1333
1334 // Ported from an inline object in instance/properties.js#bindToAccessor.
1335 class _CloseOnlyBinding extends Bindable {
1336 final _PropertyAccessor accessor;
1337
1338 _CloseOnlyBinding(this.accessor);
1339
1340 open(callback) {}
1341 get value => null;
1342 set value(newValue) {}
1343 deliver() {}
1344
1345 void close() {
1346 if (accessor.bindable == null) return;
1347 accessor.bindable.close();
1348 accessor.bindable = null;
1349 }
1350 }
1351
1130 bool _toBoolean(value) => null != value && false != value; 1352 bool _toBoolean(value) => null != value && false != value;
1131 1353
1132 final Logger _observeLog = new Logger('polymer.observe'); 1354 final Logger _observeLog = new Logger('polymer.observe');
1133 final Logger _eventsLog = new Logger('polymer.events'); 1355 final Logger _eventsLog = new Logger('polymer.events');
1134 final Logger _unbindLog = new Logger('polymer.unbind'); 1356 final Logger _unbindLog = new Logger('polymer.unbind');
1135 final Logger _bindLog = new Logger('polymer.bind'); 1357 final Logger _bindLog = new Logger('polymer.bind');
1136 1358
1137 final Expando _eventHandledTable = new Expando<Set<Node>>(); 1359 final Expando _eventHandledTable = new Expando<Set<Node>>();
1138 1360
1139 final JsObject _PolymerGestures = js.context['PolymerGestures']; 1361 final JsObject _PolymerGestures = js.context['PolymerGestures'];
1140 1362
OLDNEW
« no previous file with comments | « pkg/polymer/lib/src/events.dart ('k') | pkg/polymer/lib/src/js/polymer/README.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698