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