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 /** | 5 /** |
6 * Custom DOM elements. | 6 * Custom DOM elements. |
7 * | 7 * |
8 * This library provides access to the Polymer project's | 8 * This library provides access to the Polymer project's |
9 * [Custom Elements] | 9 * [Custom Elements] |
10 * (http://www.polymer-project.org/platform/custom-elements.html) | 10 * (http://www.polymer-project.org/platform/custom-elements.html) |
11 * API, which lets you define your own elements. With custom elements, you | 11 * API, which lets you define your own elements. With custom elements, you |
12 * associate code with custom tag names, and then use those custom tag names | 12 * associate code with custom tag names, and then use those custom tag names |
13 * as you would any standard tag. For more information, see the | 13 * as you would any standard tag. For more information, see the |
14 * [Polymer.dart homepage](https://www.dartlang.org/polymer-dart/) and its | 14 * [Polymer.dart homepage](https://www.dartlang.org/polymer-dart/) and its |
15 * [custom element example] | 15 * [custom element example] |
16 * (https://www.dartlang.org/polymer-dart/#custom-elements). | 16 * (https://www.dartlang.org/polymer-dart/#custom-elements). |
17 */ | 17 */ |
18 library custom_element; | 18 library custom_element; |
19 | 19 |
20 import 'dart:async'; | 20 import 'dart:async'; |
21 import 'dart:html'; | 21 import 'dart:html'; |
22 import 'package:meta/meta.dart'; | 22 import 'package:meta/meta.dart'; |
23 import 'src/custom_tag_name.dart'; | 23 import 'src/custom_tag_name.dart'; |
24 | 24 |
25 part 'src/attribute_map.dart'; | |
26 | |
27 // TODO(jmesserly): replace with a real custom element polyfill. | |
28 // This is just something temporary. | |
29 /** | 25 /** |
30 * *Warning*: this implementation is a work in progress. It only implements | 26 * *Deprecated* -- do not use. Extend [HtmlElement] and use |
31 * the specification partially. | 27 * [document.register] instead. If running on a browser without native |
| 28 * document.register, you can add the polyfill script to your page: |
32 * | 29 * |
33 * Registers a custom HTML element with [localName] and the associated | 30 * <script src="packages/custom_element/custom-elements.debug.js"></script> |
34 * constructor. This will ensure the element is detected and | |
35 * | 31 * |
36 * See the specification at: | 32 * You can also use "custom-elements.min.js" for the minified version. |
37 * <https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html> | |
38 */ | 33 */ |
39 void registerCustomElement(String localName, CustomElement create()) { | 34 // This is only used by Dart Web UI. |
40 if (_customElements == null) { | |
41 _customElements = {}; | |
42 // TODO(jmesserly): use MutationObserver to watch for inserts? | |
43 } | |
44 | |
45 if (!isCustomTag(localName)) { | |
46 throw new ArgumentError('$localName is not a valid custom element name, ' | |
47 'it should have at least one dash and not be a reserved name.'); | |
48 } | |
49 | |
50 if (_customElements.containsKey(localName)) { | |
51 throw new ArgumentError('custom element $localName already registered.'); | |
52 } | |
53 | |
54 // TODO(jmesserly): validate this is a valid tag name, not a selector. | |
55 _customElements[localName] = create; | |
56 | |
57 // Initialize elements already on the page. | |
58 for (var query in [localName, '[is=$localName]']) { | |
59 for (var element in document.queryAll(query)) { | |
60 _initCustomElement(element, create); | |
61 } | |
62 } | |
63 } | |
64 | |
65 /** | |
66 * Creates a new element and returns it. If the [localName] has been registered | |
67 * with [registerCustomElement], it will create the custom element. | |
68 * | |
69 * This is similar to `new Element.tag` in Dart and `document.createElement` | |
70 * in JavaScript. | |
71 * | |
72 * *Warning*: this API is temporary until [dart:html] supports custom elements. | |
73 */ | |
74 Element createElement(String localName) => | |
75 initCustomElements(new Element.tag(localName)); | |
76 | |
77 /** | |
78 * Similar to `new Element.html`, but automatically creates registed custom | |
79 * elements. | |
80 * *Warning*: this API is temporary until [dart:html] supports custom elements. | |
81 */ | |
82 Element createElementFromHtml(String html) => | |
83 initCustomElements(new Element.html(html)); | |
84 | |
85 /** | |
86 * Initialize any registered custom elements recursively in the [node] tree. | |
87 * For convenience this returns the [node] instance. | |
88 * | |
89 * *Warning*: this API is temporary until [dart:html] supports custom elements. | |
90 */ | |
91 Node initCustomElements(Node node) { | |
92 for (var c = node.firstChild; c != null; c = c.nextNode) { | |
93 initCustomElements(c); | |
94 } | |
95 if (node is Element) { | |
96 var ctor = _customElements[(node as Element).localName]; | |
97 if (ctor == null) { | |
98 var attr = (node as Element).attributes['is']; | |
99 if (attr != null) ctor = _customElements[attr]; | |
100 } | |
101 if (ctor != null) _initCustomElement(node, ctor); | |
102 } | |
103 return node; | |
104 } | |
105 | |
106 /** | |
107 * The base class for all Dart web components. In addition to the [Element] | |
108 * interface, it also provides lifecycle methods: | |
109 * - [created] | |
110 * - [inserted] | |
111 * - [attributeChanged] | |
112 * - [removed] | |
113 */ | |
114 class CustomElement implements Element { | 35 class CustomElement implements Element { |
115 /** The web component element wrapped by this class. */ | 36 /** The web component element wrapped by this class. */ |
116 Element _host; | 37 Element _host; |
117 List _shadowRoots; | 38 List _shadowRoots; |
118 _AttributeMap _attributes; | 39 _AttributeMap _attributes; |
119 | 40 |
120 /** | 41 /** |
121 * Shadow roots generated by dwc for each custom element, indexed by the | 42 * Shadow roots generated by dwc for each custom element, indexed by the |
122 * custom element tag name. | 43 * custom element tag name. |
123 */ | 44 */ |
(...skipping 477 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
601 Stream<TouchEvent> get onTouchLeave => host.onTouchLeave; | 522 Stream<TouchEvent> get onTouchLeave => host.onTouchLeave; |
602 Stream<TouchEvent> get onTouchMove => host.onTouchMove; | 523 Stream<TouchEvent> get onTouchMove => host.onTouchMove; |
603 Stream<TouchEvent> get onTouchStart => host.onTouchStart; | 524 Stream<TouchEvent> get onTouchStart => host.onTouchStart; |
604 Stream<TransitionEvent> get onTransitionEnd => host.onTransitionEnd; | 525 Stream<TransitionEvent> get onTransitionEnd => host.onTransitionEnd; |
605 | 526 |
606 // TODO(sigmund): do the normal forwarding when dartbug.com/7919 is fixed. | 527 // TODO(sigmund): do the normal forwarding when dartbug.com/7919 is fixed. |
607 Stream<WheelEvent> get onMouseWheel { | 528 Stream<WheelEvent> get onMouseWheel { |
608 throw new UnsupportedError('onMouseWheel is not supported'); | 529 throw new UnsupportedError('onMouseWheel is not supported'); |
609 } | 530 } |
610 } | 531 } |
611 | |
612 | |
613 Map<String, Function> _customElements; | |
614 | |
615 void _initCustomElement(Element node, CustomElement ctor()) { | |
616 CustomElement element = ctor(); | |
617 element.host = node; | |
618 | |
619 // TODO(jmesserly): replace lifecycle stuff with a proper polyfill. | |
620 element.created(); | |
621 | |
622 _registerLifecycleInsert(element); | |
623 } | |
624 | |
625 void _registerLifecycleInsert(CustomElement element) { | |
626 scheduleMicrotask(() { | |
627 // TODO(jmesserly): bottom up or top down insert? | |
628 var node = element.host; | |
629 | |
630 // TODO(jmesserly): need a better check to see if the node has been removed. | |
631 if (node.parentNode == null) return; | |
632 | |
633 _registerLifecycleRemove(element); | |
634 element.inserted(); | |
635 }); | |
636 } | |
637 | |
638 void _registerLifecycleRemove(CustomElement element) { | |
639 // TODO(jmesserly): need fallback or polyfill for MutationObserver. | |
640 if (!MutationObserver.supported) return; | |
641 | |
642 new MutationObserver((records, observer) { | |
643 var node = element.host; | |
644 for (var record in records) { | |
645 for (var removed in record.removedNodes) { | |
646 if (identical(node, removed)) { | |
647 observer.disconnect(); | |
648 element.removed(); | |
649 return; | |
650 } | |
651 } | |
652 } | |
653 }).observe(element.parentNode, childList: true); | |
654 } | |
OLD | NEW |