| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 /** | |
| 6 * This library exposes the types in [watcher], [safe_html], [templating] and | |
| 7 * the [WebComponent] base class. See this article for more information about | |
| 8 * this library: <http://www.dartlang.org/articles/dart-web-components/>. | |
| 9 */ | |
| 10 library web_components; | |
| 11 | |
| 12 export 'watcher.dart'; | |
| 13 export 'safe_html.dart'; | |
| 14 export 'templating.dart'; | |
| 15 | |
| 16 import 'dart:html'; | |
| 17 | |
| 18 // Imported for the doc comment | |
| 19 import 'watcher.dart' as watcher; | |
| 20 import 'safe_html.dart' as safe_html; | |
| 21 import 'templating.dart' as templating; | |
| 22 | |
| 23 /** | |
| 24 * The base class for all Dart web components. In addition to the [Element] | |
| 25 * interface, it also provides lifecycle methods: | |
| 26 * - [created] | |
| 27 * - [inserted] | |
| 28 * - [attributeChanged] | |
| 29 * - [removed] | |
| 30 */ | |
| 31 abstract class WebComponent implements Element { | |
| 32 /** The web component element wrapped by this class. */ | |
| 33 final Element _element; | |
| 34 List _shadowRoots; | |
| 35 | |
| 36 /** | |
| 37 * Default constructor for web components. This contructor is only provided | |
| 38 * for tooling, and is *not* currently supported. | |
| 39 * Use [WebComponent.forElement] instead. | |
| 40 */ | |
| 41 WebComponent() : _element = null { | |
| 42 throw new UnsupportedError( | |
| 43 'Directly constructing web components is not currently supported. ' | |
| 44 'You need to use the WebComponent.forElement constructor to associate ' | |
| 45 'a component with its DOM element. If you run "bin/dwc.dart" on your ' | |
| 46 'component, the compiler will create the approriate construction ' | |
| 47 'logic.'); | |
| 48 } | |
| 49 | |
| 50 /** | |
| 51 * Temporary constructor until components extend [Element]. Attaches this | |
| 52 * component to the provided [element]. The element must not already have a | |
| 53 * component associated with it. | |
| 54 */ | |
| 55 WebComponent.forElement(Element element) : _element = element { | |
| 56 if (element == null || _element.xtag != null) { | |
| 57 throw new ArgumentError( | |
| 58 'element must be provided and not have its xtag property set'); | |
| 59 } | |
| 60 _element.xtag = this; | |
| 61 } | |
| 62 | |
| 63 /** | |
| 64 * **Note**: This is an implementation helper and should not need to be called | |
| 65 * from your code. | |
| 66 * | |
| 67 * Creates the [ShadowRoot] backing this component. | |
| 68 */ | |
| 69 createShadowRoot() { | |
| 70 if (_realShadowRoot) { | |
| 71 return new ShadowRoot(_element); | |
| 72 } | |
| 73 if (_shadowRoots == null) _shadowRoots = []; | |
| 74 _shadowRoots.add(new Element.html('<div class="shadowroot"></div>')); | |
| 75 return _shadowRoots.last; | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * Invoked when this component gets created. | |
| 80 * Note that [root] will be a [ShadowRoot] if the browser supports Shadow DOM. | |
| 81 */ | |
| 82 void created() {} | |
| 83 | |
| 84 /** Invoked when this component gets inserted in the DOM tree. */ | |
| 85 void inserted() {} | |
| 86 | |
| 87 /** Invoked when this component is removed from the DOM tree. */ | |
| 88 void removed() {} | |
| 89 | |
| 90 // TODO(jmesserly): how do we implement this efficiently? | |
| 91 // See https://github.com/dart-lang/dart-web-components/issues/37 | |
| 92 /** Invoked when any attribute of the component is modified. */ | |
| 93 void attributeChanged( | |
| 94 String name, String oldValue, String newValue) {} | |
| 95 | |
| 96 /** | |
| 97 * **Note**: This is an implementation helper and should not need to be called | |
| 98 * from your code. | |
| 99 * | |
| 100 * If [ShadowRoot.supported] or [useShadowDom] is false, this distributes | |
| 101 * children to the insertion points of the emulated ShadowRoot. | |
| 102 * This is an implementation helper and should not need to be called from your | |
| 103 * code. | |
| 104 * | |
| 105 * This is an implementation of [composition][1] and [rendering][2] from the | |
| 106 * Shadow DOM spec. Currently the algorithm will replace children of this | |
| 107 * component with the DOM as it should be rendered. | |
| 108 * | |
| 109 * Note that because we're always expanding to the render tree, and nodes are | |
| 110 * expanded in a bottom up fashion, [reprojection][3] is handled naturally. | |
| 111 * | |
| 112 * [1]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.htm
l#composition | |
| 113 * [2]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.htm
l#rendering-shadow-trees | |
| 114 * [3]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.htm
l#reprojection | |
| 115 */ | |
| 116 void composeChildren() { | |
| 117 if (_realShadowRoot) return; | |
| 118 | |
| 119 if (_shadowRoots.length == 0) { | |
| 120 // TODO(jmesserly): this is a limitation of our codegen approach. | |
| 121 // We could keep the _shadowRoots around and clone(true) them, but then | |
| 122 // bindings wouldn't be properly associated. | |
| 123 throw new StateError('Distribution algorithm requires at least one shadow' | |
| 124 ' root and can only be run once.'); | |
| 125 } | |
| 126 | |
| 127 var treeStack = _shadowRoots; | |
| 128 | |
| 129 // Let TREE be the youngest tree in the HOST's tree stack | |
| 130 var tree = treeStack.removeLast(); | |
| 131 var youngestRoot = tree; | |
| 132 // Let POOL be the list of nodes | |
| 133 var pool = new List.from(nodes); | |
| 134 | |
| 135 // Note: reprojection logic is skipped here because composeChildren is | |
| 136 // run on each component in bottom up fashion. | |
| 137 | |
| 138 var shadowInsertionPoints = []; | |
| 139 var shadowInsertionTrees = []; | |
| 140 | |
| 141 while (true) { | |
| 142 // Run the distribution algorithm, supplying POOL and TREE as input | |
| 143 pool = _distributeNodes(tree, pool); | |
| 144 | |
| 145 // Let POINT be the first encountered active shadow insertion point in | |
| 146 // TREE, in tree order | |
| 147 var point = tree.query('shadow'); | |
| 148 if (point != null) { | |
| 149 if (treeStack.length > 0) { | |
| 150 // Find the next older tree, relative to TREE in the HOST's tree stack | |
| 151 // Set TREE to be this older tree | |
| 152 tree = treeStack.removeLast(); | |
| 153 // Assign TREE to the POINT | |
| 154 | |
| 155 // Note: we defer the actual tree replace operation until the end, so | |
| 156 // we can run _distributeNodes on this tree. This simplifies the query | |
| 157 // for content nodes in tree order. | |
| 158 shadowInsertionPoints.add(point); | |
| 159 shadowInsertionTrees.add(tree); | |
| 160 | |
| 161 // Continue to repeat | |
| 162 } else { | |
| 163 // If we've hit a built-in element, just use a content selector. | |
| 164 // This matches the behavior of built-in HTML elements. | |
| 165 // Since <content> can be implemented simply, we just inline it. | |
| 166 _distribute(point, pool); | |
| 167 | |
| 168 // If there is no older tree, stop. | |
| 169 break; | |
| 170 } | |
| 171 } else { | |
| 172 // If POINT exists: ... Otherwise, stop | |
| 173 break; | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 // Handle shadow tree assignments that we deferred earlier. | |
| 178 for (int i = 0; i < shadowInsertionPoints.length; i++) { | |
| 179 var point = shadowInsertionPoints[i]; | |
| 180 var tree = shadowInsertionTrees[i]; | |
| 181 // Note: defensive copy is a workaround for http://dartbug.com/6684 | |
| 182 _distribute(point, new List.from(tree.nodes)); | |
| 183 } | |
| 184 | |
| 185 // Replace our child nodes with the ones in the youngest root. | |
| 186 nodes.clear(); | |
| 187 // Note: defensive copy is a workaround for http://dartbug.com/6684 | |
| 188 nodes.addAll(new List.from(youngestRoot.nodes)); | |
| 189 } | |
| 190 | |
| 191 | |
| 192 /** | |
| 193 * This is an implementation of the [distribution algorithm][1] from the | |
| 194 * Shadow DOM spec. | |
| 195 * | |
| 196 * [1]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.htm
l#dfn-distribution-algorithm | |
| 197 */ | |
| 198 List<Node> _distributeNodes(Element tree, List<Node> pool) { | |
| 199 // Repeat for each active insertion point in TREE, in tree order: | |
| 200 for (var insertionPoint in tree.queryAll('content')) { | |
| 201 if (!_isActive(insertionPoint)) continue; | |
| 202 // Let POINT be the current insertion point. | |
| 203 | |
| 204 // TODO(jmesserly): validate selector, as specified here: | |
| 205 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
#matching-insertion-points | |
| 206 var select = insertionPoint.attributes['select']; | |
| 207 if (select == null || select == '') select = '*'; | |
| 208 | |
| 209 // Repeat for each node in POOL: | |
| 210 // 1. Let NODE be the current node | |
| 211 // 2. If the NODE matches POINT's matching criteria: | |
| 212 // 1. Distribute the NODE to POINT | |
| 213 // 2. Remove NODE from the POOL | |
| 214 | |
| 215 var matching = []; | |
| 216 var notMatching = []; | |
| 217 for (var node in pool) { | |
| 218 (_matches(node, select) ? matching : notMatching).add(node); | |
| 219 } | |
| 220 | |
| 221 if (matching.length == 0) { | |
| 222 // When an insertion point or a shadow insertion point has nothing | |
| 223 // assigned or distributed to them, the fallback content must be used | |
| 224 // instead when rendering. The fallback content is all descendants of | |
| 225 // the element that represents the insertion point. | |
| 226 matching = insertionPoint.nodes; | |
| 227 } | |
| 228 | |
| 229 _distribute(insertionPoint, matching); | |
| 230 | |
| 231 pool = notMatching; | |
| 232 } | |
| 233 | |
| 234 return pool; | |
| 235 } | |
| 236 | |
| 237 static bool _matches(Node node, String selector) { | |
| 238 if (node is Text) return selector == '*'; | |
| 239 // TODO(jmesserly): cannot use matchesSelector because of dartbug.com/4401 | |
| 240 //return (node as Element).matchesSelector(selector); | |
| 241 return (node.parent as Element).queryAll(selector).some((n) => n == node); | |
| 242 } | |
| 243 | |
| 244 static bool _isInsertionPoint(Element node) => | |
| 245 node.tagName == 'CONTENT' || node.tagName == 'SHADOW'; | |
| 246 | |
| 247 /** | |
| 248 * An insertion point is "active" if it is not the child of another insertion | |
| 249 * point. A child of an insertion point is "fallback" content and should not | |
| 250 * be considered during the distribution algorithm. | |
| 251 */ | |
| 252 static bool _isActive(Element node) { | |
| 253 assert(_isInsertionPoint(node)); | |
| 254 for (node = node.parent; node != null; node = node.parent) { | |
| 255 if (_isInsertionPoint(node)) return false; | |
| 256 } | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 /** Distribute the [nodes] in place of an existing [insertionPoint]. */ | |
| 261 static void _distribute(Element insertionPoint, List<Node> nodes) { | |
| 262 assert(_isInsertionPoint(insertionPoint)); | |
| 263 for (var node in nodes) { | |
| 264 insertionPoint.parent.insertBefore(node, insertionPoint); | |
| 265 } | |
| 266 insertionPoint.remove(); | |
| 267 } | |
| 268 | |
| 269 // TODO(jmesserly): this forwarding is temporary until Dart supports | |
| 270 // subclassing Elements. | |
| 271 // TODO(jmesserly): we were missing the setter for title, are other things | |
| 272 // missing setters? | |
| 273 | |
| 274 List<Node> get nodes => _element.nodes; | |
| 275 | |
| 276 set nodes(Collection<Node> value) { _element.nodes = value; } | |
| 277 | |
| 278 /** | |
| 279 * Replaces this node with another node. | |
| 280 */ | |
| 281 Node replaceWith(Node otherNode) { _element.replaceWith(otherNode); } | |
| 282 | |
| 283 /** | |
| 284 * Removes this node from the DOM. | |
| 285 */ | |
| 286 void remove() => _element.remove(); | |
| 287 | |
| 288 Node get nextNode => _element.nextNode; | |
| 289 | |
| 290 Document get document => _element.document; | |
| 291 | |
| 292 Node get previousNode => _element.previousNode; | |
| 293 | |
| 294 String get text => _element.text; | |
| 295 | |
| 296 set text(String v) { _element.text = v; } | |
| 297 | |
| 298 bool contains(Node other) => _element.contains(other); | |
| 299 | |
| 300 bool hasChildNodes() => _element.hasChildNodes(); | |
| 301 | |
| 302 Node insertBefore(Node newChild, Node refChild) => | |
| 303 _element.insertBefore(newChild, refChild); | |
| 304 | |
| 305 Map<String, String> get attributes => _element.attributes; | |
| 306 set attributes(Map<String, String> value) { | |
| 307 _element.attributes = value; | |
| 308 } | |
| 309 | |
| 310 List<Element> get elements => _element.elements; | |
| 311 | |
| 312 set elements(Collection<Element> value) { | |
| 313 _element.elements = value; | |
| 314 } | |
| 315 | |
| 316 List<Element> get children => _element.children; | |
| 317 | |
| 318 set children(Collection<Element> value) { | |
| 319 _element.children = value; | |
| 320 } | |
| 321 | |
| 322 Set<String> get classes => _element.classes; | |
| 323 | |
| 324 set classes(Collection<String> value) { | |
| 325 _element.classes = value; | |
| 326 } | |
| 327 | |
| 328 Map<String, String> get dataAttributes => _element.dataAttributes; | |
| 329 set dataAttributes(Map<String, String> value) { | |
| 330 _element.dataAttributes = value; | |
| 331 } | |
| 332 | |
| 333 Map<String, String> getNamespacedAttributes(String namespace) => | |
| 334 _element.getNamespacedAttributes(namespace); | |
| 335 | |
| 336 Future<CSSStyleDeclaration> get computedStyle => _element.computedStyle; | |
| 337 | |
| 338 Future<CSSStyleDeclaration> getComputedStyle(String pseudoElement) | |
| 339 => _element.getComputedStyle(pseudoElement); | |
| 340 | |
| 341 Element clone(bool deep) => _element.clone(deep); | |
| 342 | |
| 343 Element get parent => _element.parent; | |
| 344 | |
| 345 ElementEvents get on => _element.on; | |
| 346 | |
| 347 String get contentEditable => _element.contentEditable; | |
| 348 | |
| 349 String get dir => _element.dir; | |
| 350 | |
| 351 bool get draggable => _element.draggable; | |
| 352 | |
| 353 bool get hidden => _element.hidden; | |
| 354 | |
| 355 String get id => _element.id; | |
| 356 | |
| 357 String get innerHTML => _element.innerHtml; | |
| 358 | |
| 359 void set innerHTML(String v) { | |
| 360 _element.innerHtml = v; | |
| 361 } | |
| 362 | |
| 363 String get innerHtml => _element.innerHtml; | |
| 364 void set innerHtml(String v) { | |
| 365 _element.innerHtml = v; | |
| 366 } | |
| 367 | |
| 368 bool get isContentEditable => _element.isContentEditable; | |
| 369 | |
| 370 String get lang => _element.lang; | |
| 371 | |
| 372 String get outerHtml => _element.outerHtml; | |
| 373 | |
| 374 bool get spellcheck => _element.spellcheck; | |
| 375 | |
| 376 int get tabIndex => _element.tabIndex; | |
| 377 | |
| 378 String get title => _element.title; | |
| 379 | |
| 380 set title(String value) { _element.title = value; } | |
| 381 | |
| 382 bool get translate => _element.translate; | |
| 383 | |
| 384 String get webkitdropzone => _element.webkitdropzone; | |
| 385 | |
| 386 void click() { _element.click(); } | |
| 387 | |
| 388 Element insertAdjacentElement(String where, Element element) => | |
| 389 _element.insertAdjacentElement(where, element); | |
| 390 | |
| 391 void insertAdjacentHtml(String where, String html) { | |
| 392 _element.insertAdjacentHtml(where, html); | |
| 393 } | |
| 394 | |
| 395 void insertAdjacentText(String where, String text) { | |
| 396 _element.insertAdjacentText(where, text); | |
| 397 } | |
| 398 | |
| 399 Map<String, String> get dataset => _element.dataset; | |
| 400 | |
| 401 Element get nextElementSibling => _element.nextElementSibling; | |
| 402 | |
| 403 Element get offsetParent => _element.offsetParent; | |
| 404 | |
| 405 Element get previousElementSibling => _element.previousElementSibling; | |
| 406 | |
| 407 CSSStyleDeclaration get style => _element.style; | |
| 408 | |
| 409 String get tagName => _element.tagName; | |
| 410 | |
| 411 void blur() { _element.blur(); } | |
| 412 | |
| 413 void focus() { _element.focus(); } | |
| 414 | |
| 415 void scrollByLines(int lines) { | |
| 416 _element.scrollByLines(lines); | |
| 417 } | |
| 418 | |
| 419 void scrollByPages(int pages) { | |
| 420 _element.scrollByPages(pages); | |
| 421 } | |
| 422 | |
| 423 void scrollIntoView([bool centerIfNeeded]) { | |
| 424 if (centerIfNeeded == null) { | |
| 425 _element.scrollIntoView(); | |
| 426 } else { | |
| 427 _element.scrollIntoView(centerIfNeeded); | |
| 428 } | |
| 429 } | |
| 430 | |
| 431 bool matchesSelector(String selectors) => _element.matchesSelector(selectors); | |
| 432 | |
| 433 void webkitRequestFullScreen(int flags) { | |
| 434 _element.webkitRequestFullScreen(flags); | |
| 435 } | |
| 436 | |
| 437 void webkitRequestFullscreen() { _element.webkitRequestFullscreen(); } | |
| 438 | |
| 439 void webkitRequestPointerLock() { _element.webkitRequestPointerLock(); } | |
| 440 | |
| 441 Element query(String selectors) => _element.query(selectors); | |
| 442 | |
| 443 List<Element> queryAll(String selectors) => _element.queryAll(selectors); | |
| 444 | |
| 445 HTMLCollection get $dom_children => _element.$dom_children; | |
| 446 | |
| 447 int get $dom_childElementCount => _element.$dom_childElementCount; | |
| 448 | |
| 449 String get $dom_className => _element.$dom_className; | |
| 450 set $dom_className(String value) { _element.$dom_className = value; } | |
| 451 | |
| 452 int get clientHeight => _element.clientHeight; | |
| 453 | |
| 454 int get clientLeft => _element.clientLeft; | |
| 455 | |
| 456 int get clientTop => _element.clientTop; | |
| 457 | |
| 458 int get clientWidth => _element.clientWidth; | |
| 459 | |
| 460 int get childElementCount => _element.childElementCount; | |
| 461 | |
| 462 Element get firstElementChild => _element.firstElementChild; | |
| 463 | |
| 464 Element get lastElementChild => _element.lastElementChild; | |
| 465 | |
| 466 Element get $dom_firstElementChild => _element.$dom_firstElementChild; | |
| 467 | |
| 468 Element get $dom_lastElementChild => _element.$dom_lastElementChild; | |
| 469 | |
| 470 int get offsetHeight => _element.offsetHeight; | |
| 471 | |
| 472 int get offsetLeft => _element.offsetLeft; | |
| 473 | |
| 474 int get offsetTop => _element.offsetTop; | |
| 475 | |
| 476 int get offsetWidth => _element.offsetWidth; | |
| 477 | |
| 478 int get scrollHeight => _element.scrollHeight; | |
| 479 | |
| 480 int get scrollLeft => _element.scrollLeft; | |
| 481 | |
| 482 int get scrollTop => _element.scrollTop; | |
| 483 | |
| 484 set scrollLeft(int value) { _element.scrollLeft = value; } | |
| 485 | |
| 486 set scrollTop(int value) { _element.scrollTop = value; } | |
| 487 | |
| 488 int get scrollWidth => _element.scrollWidth; | |
| 489 | |
| 490 String $dom_getAttribute(String name) => | |
| 491 _element.$dom_getAttribute(name); | |
| 492 | |
| 493 String $dom_getAttributeNS(String namespaceUri, String localName) => | |
| 494 _element.$dom_getAttributeNS(namespaceUri, localName); | |
| 495 | |
| 496 String $dom_setAttributeNS( | |
| 497 String namespaceUri, String localName, String value) { | |
| 498 _element.$dom_setAttributeNS(namespaceUri, localName, value); | |
| 499 } | |
| 500 | |
| 501 bool $dom_hasAttributeNS(String namespaceUri, String localName) => | |
| 502 _element.$dom_hasAttributeNS(namespaceUri, localName); | |
| 503 | |
| 504 void $dom_removeAttributeNS(String namespaceUri, String localName) => | |
| 505 _element.$dom_removeAttributeNS(namespaceUri, localName); | |
| 506 | |
| 507 ClientRect getBoundingClientRect() => _element.getBoundingClientRect(); | |
| 508 | |
| 509 List<ClientRect> getClientRects() => _element.getClientRects(); | |
| 510 | |
| 511 List<Node> $dom_getElementsByClassName(String name) => | |
| 512 _element.$dom_getElementsByClassName(name); | |
| 513 | |
| 514 List<Node> $dom_getElementsByTagName(String name) => | |
| 515 _element.$dom_getElementsByTagName(name); | |
| 516 | |
| 517 bool $dom_hasAttribute(String name) => | |
| 518 _element.$dom_hasAttribute(name); | |
| 519 | |
| 520 Element $dom_querySelector(String selectors) => | |
| 521 _element.$dom_querySelector(selectors); | |
| 522 | |
| 523 List<Node> $dom_querySelectorAll(String selectors) => | |
| 524 _element.$dom_querySelectorAll(selectors); | |
| 525 | |
| 526 void $dom_removeAttribute(String name) => | |
| 527 _element.$dom_removeAttribute(name); | |
| 528 | |
| 529 void $dom_setAttribute(String name, String value) => | |
| 530 _element.$dom_setAttribute(name, value); | |
| 531 | |
| 532 NamedNodeMap get $dom_attributes => _element.$dom_attributes; | |
| 533 | |
| 534 List<Node> get $dom_childNodes => _element.$dom_childNodes; | |
| 535 | |
| 536 Node get $dom_firstChild => _element.$dom_firstChild; | |
| 537 | |
| 538 Node get $dom_lastChild => _element.$dom_lastChild; | |
| 539 | |
| 540 String get $dom_localName => _element.$dom_localName; | |
| 541 | |
| 542 String get $dom_namespaceUri => _element.$dom_namespaceUri; | |
| 543 | |
| 544 int get nodeType => _element.nodeType; | |
| 545 | |
| 546 void $dom_addEventListener(String type, EventListener listener, | |
| 547 [bool useCapture]) { | |
| 548 _element.$dom_addEventListener(type, listener, useCapture); | |
| 549 } | |
| 550 | |
| 551 Node $dom_appendChild(Node newChild) => _element.$dom_appendChild(newChild); | |
| 552 | |
| 553 bool $dom_dispatchEvent(Event event) => _element.$dom_dispatchEvent(event); | |
| 554 | |
| 555 Node $dom_removeChild(Node oldChild) => _element.$dom_removeChild(oldChild); | |
| 556 | |
| 557 void $dom_removeEventListener(String type, EventListener listener, | |
| 558 [bool useCapture]) { | |
| 559 _element.$dom_removeEventListener(type, listener, useCapture); | |
| 560 } | |
| 561 | |
| 562 Node $dom_replaceChild(Node newChild, Node oldChild) => | |
| 563 _element.$dom_replaceChild(newChild, oldChild); | |
| 564 | |
| 565 get xtag => _element.xtag; | |
| 566 | |
| 567 set xtag(value) { _element.xtag = value; } | |
| 568 | |
| 569 void append(Element e) => _element.append(e); | |
| 570 | |
| 571 void appendText(String text) => _element.appendText(text); | |
| 572 | |
| 573 void appendHtml(String html) => _element.appendHtml(html); | |
| 574 } | |
| 575 | |
| 576 /** | |
| 577 * Set this to true to use native Shadow DOM if it is supported. | |
| 578 * Note that this will change behavior of [WebComponent] APIs for tree | |
| 579 * traversal. | |
| 580 */ | |
| 581 bool useShadowDom = false; | |
| 582 | |
| 583 bool get _realShadowRoot => useShadowDom && ShadowRoot.supported; | |
| OLD | NEW |