| 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 |