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 library polymer.polymer_element; | 5 library polymer.polymer_element; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:html'; | 8 import 'dart:html'; |
9 import 'dart:mirrors'; | 9 import 'dart:mirrors'; |
10 | 10 |
11 import 'package:custom_element/custom_element.dart'; | 11 import 'package:custom_element/custom_element.dart'; |
12 import 'package:mdv/mdv.dart' as mdv; | 12 import 'package:fancy_syntax/syntax.dart' show FancySyntax; |
| 13 import 'package:mdv/mdv.dart' show NodeBinding; |
13 import 'package:observe/observe.dart'; | 14 import 'package:observe/observe.dart'; |
| 15 import 'package:observe/src/microtask.dart'; |
14 | 16 |
15 import 'observe.dart'; | |
16 import 'src/utils_observe.dart' show toCamelCase, toHyphenedName; | 17 import 'src/utils_observe.dart' show toCamelCase, toHyphenedName; |
17 | 18 |
18 /** | 19 /** |
19 * Registers a [PolymerElement]. This is similar to [registerCustomElement] | 20 * Registers a [PolymerElement]. This is similar to [registerCustomElement] |
20 * but it is designed to work with the `<element>` element and adds additional | 21 * but it is designed to work with the `<element>` element and adds additional |
21 * features. | 22 * features. |
22 */ | 23 */ |
23 void registerPolymerElement(String localName, PolymerElement create()) { | 24 void registerPolymerElement(String localName, PolymerElement create()) { |
24 registerCustomElement(localName, () => create().._initialize(localName)); | 25 registerCustomElement(localName, () => create().._initialize(localName)); |
25 } | 26 } |
26 | 27 |
27 /** | 28 /** |
28 * *Warning*: many features of this class are not fully implemented. | 29 * *Warning*: many features of this class are not fully implemented. |
29 * | 30 * |
30 * The base class for Polymer elements. It provides convience features on top | 31 * The base class for Polymer elements. It provides convience features on top |
31 * of the custom elements web standard. | 32 * of the custom elements web standard. |
32 * | 33 * |
33 * Currently it supports publishing attributes via: | 34 * Currently it supports publishing attributes via: |
34 * | 35 * |
35 * <element name="..." attributes="foo, bar, baz"> | 36 * <element name="..." attributes="foo, bar, baz"> |
36 * | 37 * |
37 * Any attribute published this way can be used in a data binding expression, | 38 * Any attribute published this way can be used in a data binding expression, |
38 * and it should contain a corresponding DOM field. | 39 * and it should contain a corresponding DOM field. |
39 * | 40 * |
40 * *Warning*: due to dart2js mirror limititations, the mapping from HTML | 41 * *Warning*: due to dart2js mirror limititations, the mapping from HTML |
41 * attribute to element property is a conversion from `dash-separated-words` | 42 * attribute to element property is a conversion from `dash-separated-words` |
42 * to camelCase, rather than searching for a property with the same name. | 43 * to camelCase, rather than searching for a property with the same name. |
43 */ | 44 */ |
44 // TODO(jmesserly): fix the dash-separated-words issue. Polymer uses lowercase. | 45 // TODO(jmesserly): fix the dash-separated-words issue. Polymer uses lowercase. |
45 abstract class PolymerElement extends CustomElement with _EventsMixin { | 46 class PolymerElement extends CustomElement with _EventsMixin { |
46 // This is a partial port of: | 47 // This is a partial port of: |
47 // https://github.com/Polymer/polymer/blob/stable/src/attrs.js | 48 // https://github.com/Polymer/polymer/blob/stable/src/attrs.js |
48 // https://github.com/Polymer/polymer/blob/stable/src/bindProperties.js | 49 // https://github.com/Polymer/polymer/blob/stable/src/bindProperties.js |
49 // https://github.com/Polymer/polymer/blob/7936ff8/src/declaration/events.js | 50 // https://github.com/Polymer/polymer/blob/7936ff8/src/declaration/events.js |
50 // https://github.com/Polymer/polymer/blob/7936ff8/src/instance/events.js | 51 // https://github.com/Polymer/polymer/blob/7936ff8/src/instance/events.js |
51 // TODO(jmesserly): we still need to port more of the functionality | 52 // TODO(jmesserly): we still need to port more of the functionality |
52 | 53 |
| 54 /// The one syntax to rule them all. |
| 55 static BindingDelegate _fancySyntax = new FancySyntax(); |
| 56 |
53 // TODO(sigmund): delete. The next line is only added to avoid warnings from | 57 // TODO(sigmund): delete. The next line is only added to avoid warnings from |
54 // the analyzer (see http://dartbug.com/11672) | 58 // the analyzer (see http://dartbug.com/11672) |
55 Element get host => super.host; | 59 Element get host => super.host; |
56 | 60 |
57 bool get applyAuthorStyles => false; | 61 bool get applyAuthorStyles => false; |
58 bool get resetStyleInheritance => false; | 62 bool get resetStyleInheritance => false; |
59 | 63 |
60 /** | 64 /** |
61 * The declaration of this polymer-element, used to extract template contents | 65 * The declaration of this polymer-element, used to extract template contents |
62 * and other information. | 66 * and other information. |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 void created() { | 118 void created() { |
115 // TODO(jmesserly): this breaks until we get some kind of type conversion. | 119 // TODO(jmesserly): this breaks until we get some kind of type conversion. |
116 // _publishedAttrs.forEach((name, propObserver) { | 120 // _publishedAttrs.forEach((name, propObserver) { |
117 // var value = attributes[name]; | 121 // var value = attributes[name]; |
118 // if (value != null) propObserver.value = value; | 122 // if (value != null) propObserver.value = value; |
119 // }); | 123 // }); |
120 _initShadowRoot(); | 124 _initShadowRoot(); |
121 _addHostListeners(); | 125 _addHostListeners(); |
122 } | 126 } |
123 | 127 |
| 128 /** |
| 129 * Creates the document fragment to use for each instance of the custom |
| 130 * element, given the `<template>` node. By default this is equivalent to: |
| 131 * |
| 132 * template.createInstance(this, fancySyntax); |
| 133 * |
| 134 * Where fancySyntax is a singleton `FancySyntax` instance from the |
| 135 * [fancy_syntax](https://pub.dartlang.org/packages/fancy_syntax) package. |
| 136 * |
| 137 * You can override this method to change the instantiation behavior of the |
| 138 * template, for example to use a different data-binding syntax. |
| 139 */ |
| 140 DocumentFragment instanceTemplate(Element template) => |
| 141 template.createInstance(this, _fancySyntax); |
| 142 |
124 void _initShadowRoot() { | 143 void _initShadowRoot() { |
125 for (var localName in _localNames) { | 144 for (var localName in _localNames) { |
126 var declaration = getDeclaration(localName); | 145 var declaration = getDeclaration(localName); |
127 var root = createShadowRoot(localName); | 146 var root = createShadowRoot(localName); |
128 _addInstanceListeners(root, localName); | 147 _addInstanceListeners(root, localName); |
129 | 148 |
130 root.applyAuthorStyles = applyAuthorStyles; | 149 root.applyAuthorStyles = applyAuthorStyles; |
131 root.resetStyleInheritance = resetStyleInheritance; | 150 root.resetStyleInheritance = resetStyleInheritance; |
132 | 151 |
133 var templateNode = declaration.children.firstWhere( | 152 var templateNode = declaration.children.firstWhere( |
134 (n) => n.localName == 'template', orElse: () => null); | 153 (n) => n.localName == 'template', orElse: () => null); |
135 if (templateNode == null) return; | 154 if (templateNode == null) return; |
136 | 155 |
137 root.nodes.add(cloneTemplate(templateNode.content)); | 156 // Create the contents of the element's ShadowRoot, and add them. |
138 // TODO(sigmund): use fancy-syntax as the default. | 157 root.nodes.add(instanceTemplate(templateNode)); |
139 // TODO(sigmund,jmesserly): mdv.bindModel should be async internally | |
140 Timer.run(() => mdv.bindModel(root, this)); | |
141 } | 158 } |
142 } | 159 } |
143 | 160 |
144 void bind(String name, model, String path) { | 161 NodeBinding createBinding(String name, model, String path) { |
145 var propObserver = _publishedAttrs[name]; | 162 var propObserver = _publishedAttrs[name]; |
146 if (propObserver != null) { | 163 if (propObserver != null) { |
147 unbind(name); | 164 return new _PolymerBinding(this, name, model, path, propObserver); |
148 | |
149 _bindings[name] = new PathObserver(model, path).bindSync((value) { | |
150 propObserver.value = value; | |
151 }); | |
152 return; | |
153 } | 165 } |
154 return super.bind(name, model, path); | 166 return super.createBinding(name, model, path); |
155 } | |
156 | |
157 void unbind(String name) { | |
158 if (_bindings != null) { | |
159 var binding = _bindings.remove(name); | |
160 if (binding != null) { | |
161 binding.cancel(); | |
162 return; | |
163 } | |
164 } | |
165 return super.unbind(name); | |
166 } | |
167 | |
168 void unbindAll() { | |
169 for (var binding in _bindings.values) binding.cancel(); | |
170 _bindings.clear(); | |
171 return super.unbindAll(); | |
172 } | 167 } |
173 } | 168 } |
174 | 169 |
| 170 class _PolymerBinding extends NodeBinding { |
| 171 final PathObserver _publishedAttr; |
| 172 |
| 173 _PolymerBinding(node, property, model, path, PathObserver this._publishedAttr) |
| 174 : super(node, property, model, path); |
| 175 |
| 176 void boundValueChanged(newValue) { |
| 177 _publishedAttr.value = newValue; |
| 178 } |
| 179 } |
| 180 |
175 /** | 181 /** |
176 * Polymer features to handle the syntactic sugar on-* to declare to | 182 * Polymer features to handle the syntactic sugar on-* to declare to |
177 * automatically map event handlers to instance methods of the [PolymerElement]. | 183 * automatically map event handlers to instance methods of the [PolymerElement]. |
178 * This mixin is a port of: | 184 * This mixin is a port of: |
179 * https://github.com/Polymer/polymer/blob/7936ff8/src/declaration/events.js | 185 * https://github.com/Polymer/polymer/blob/7936ff8/src/declaration/events.js |
180 * https://github.com/Polymer/polymer/blob/7936ff8/src/instance/events.js | 186 * https://github.com/Polymer/polymer/blob/7936ff8/src/instance/events.js |
181 */ | 187 */ |
182 abstract class _EventsMixin { | 188 abstract class _EventsMixin { |
183 // TODO(sigmund): implement the Dart equivalent of 'inheritDelegates' | 189 // TODO(sigmund): implement the Dart equivalent of 'inheritDelegates' |
184 // Notes about differences in the implementation below: | 190 // Notes about differences in the implementation below: |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 // dartbug.com/11108). | 353 // dartbug.com/11108). |
348 var methodDecl = reflectClass(receiver.runtimeType).methods[method]; | 354 var methodDecl = reflectClass(receiver.runtimeType).methods[method]; |
349 if (methodDecl != null) { | 355 if (methodDecl != null) { |
350 // This will either truncate the argument list or extend it with extra | 356 // This will either truncate the argument list or extend it with extra |
351 // null arguments, so it will match the signature. | 357 // null arguments, so it will match the signature. |
352 // TODO(sigmund): consider accepting optional arguments when we can tell | 358 // TODO(sigmund): consider accepting optional arguments when we can tell |
353 // them appart from named arguments (see http://dartbug.com/11334) | 359 // them appart from named arguments (see http://dartbug.com/11334) |
354 args.length = methodDecl.parameters.where((p) => !p.isOptional).length; | 360 args.length = methodDecl.parameters.where((p) => !p.isOptional).length; |
355 } | 361 } |
356 reflect(receiver).invoke(method, args); | 362 reflect(receiver).invoke(method, args); |
| 363 performMicrotaskCheckpoint(); |
357 } | 364 } |
358 | 365 |
359 bool _instanceEventListener(String eventName, Event event) { | 366 bool _instanceEventListener(String eventName, Event event) { |
360 if (event.bubbles) { | 367 if (event.bubbles) { |
361 if (event.path == null || !ShadowRoot.supported) { | 368 if (event.path == null || !ShadowRoot.supported) { |
362 return _listenLocalNoEventPath(eventName, event); | 369 return _listenLocalNoEventPath(eventName, event); |
363 } else { | 370 } else { |
364 return _listenLocal(eventName, event); | 371 return _listenLocal(eventName, event); |
365 } | 372 } |
366 } | 373 } |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 'onKeyMessage': MediaElement.keyMessageEvent, | 515 'onKeyMessage': MediaElement.keyMessageEvent, |
509 'onNeedKey': MediaElement.needKeyEvent, | 516 'onNeedKey': MediaElement.needKeyEvent, |
510 'onWebGlContextLost': CanvasElement.webGlContextLostEvent, | 517 'onWebGlContextLost': CanvasElement.webGlContextLostEvent, |
511 'onWebGlContextRestored': CanvasElement.webGlContextRestoredEvent, | 518 'onWebGlContextRestored': CanvasElement.webGlContextRestoredEvent, |
512 'onPointerLockChange': Document.pointerLockChangeEvent, | 519 'onPointerLockChange': Document.pointerLockChangeEvent, |
513 'onPointerLockError': Document.pointerLockErrorEvent, | 520 'onPointerLockError': Document.pointerLockErrorEvent, |
514 'onReadyStateChange': Document.readyStateChangeEvent, | 521 'onReadyStateChange': Document.readyStateChangeEvent, |
515 'onSelectionChange': Document.selectionChangeEvent, | 522 'onSelectionChange': Document.selectionChangeEvent, |
516 'onSecurityPolicyViolation': Document.securityPolicyViolationEvent, | 523 'onSecurityPolicyViolation': Document.securityPolicyViolationEvent, |
517 }; | 524 }; |
OLD | NEW |