OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * Custom Elements let authors define their own elements. Authors associate code |
| 7 * with custom tag names, and then use those custom tag names as they would any |
| 8 * standard tag. See <www.polymer-project.org/platform/custom-elements.html> |
| 9 * for more information. |
| 10 */ |
| 11 library custom_element; |
| 12 |
| 13 import 'dart:async'; |
| 14 import 'dart:html'; |
| 15 import 'package:mdv/mdv.dart' as mdv; |
| 16 import 'package:meta/meta.dart'; |
| 17 import 'src/custom_tag_name.dart'; |
| 18 |
| 19 // TODO(jmesserly): replace with a real custom element polyfill. |
| 20 // This is just something temporary. |
| 21 /** |
| 22 * *Warning*: this implementation is a work in progress. It only implements |
| 23 * the specification partially. |
| 24 * |
| 25 * Registers a custom HTML element with [localName] and the associated |
| 26 * constructor. This will ensure the element is detected and |
| 27 * |
| 28 * See the specification at: |
| 29 * <https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html> |
| 30 */ |
| 31 void registerCustomElement(String localName, CustomElement create()) { |
| 32 if (_customElements == null) { |
| 33 _customElements = {}; |
| 34 CustomElement.templateCreated.add(initCustomElements); |
| 35 // TODO(jmesserly): use MutationObserver to watch for inserts? |
| 36 } |
| 37 |
| 38 if (!isCustomTag(localName)) { |
| 39 throw new ArgumentError('$localName is not a valid custom element name, ' |
| 40 'it should have at least one dash and not be a reserved name.'); |
| 41 } |
| 42 |
| 43 if (_customElements.containsKey(localName)) { |
| 44 throw new ArgumentError('custom element $localName already registered.'); |
| 45 } |
| 46 |
| 47 // TODO(jmesserly): validate this is a valid tag name, not a selector. |
| 48 _customElements[localName] = create; |
| 49 |
| 50 // Initialize elements already on the page. |
| 51 for (var query in [localName, '[is=$localName]']) { |
| 52 for (var element in document.queryAll(query)) { |
| 53 _initCustomElement(element, create); |
| 54 } |
| 55 } |
| 56 } |
| 57 |
| 58 /** |
| 59 * Creates a new element and returns it. If the [localName] has been registered |
| 60 * with [registerCustomElement], it will create the custom element. |
| 61 * |
| 62 * This is similar to `new Element.tag` in Dart and `document.createElement` |
| 63 * in JavaScript. |
| 64 * |
| 65 * *Warning*: this API is temporary until [dart:html] supports custom elements. |
| 66 */ |
| 67 Element createElement(String localName) => |
| 68 initCustomElements(new Element.tag(localName)); |
| 69 |
| 70 /** |
| 71 * Similar to `new Element.html`, but automatically creates registed custom |
| 72 * elements. |
| 73 * *Warning*: this API is temporary until [dart:html] supports custom elements. |
| 74 */ |
| 75 Element createElementFromHtml(String html) => |
| 76 initCustomElements(new Element.html(html)); |
| 77 |
| 78 /** |
| 79 * Initialize any registered custom elements recursively in the [node] tree. |
| 80 * For convenience this returns the [node] instance. |
| 81 * |
| 82 * *Warning*: this API is temporary until [dart:html] supports custom elements. |
| 83 */ |
| 84 Node initCustomElements(Node node) { |
| 85 for (var c = node.firstChild; c != null; c = c.nextNode) { |
| 86 initCustomElements(c); |
| 87 } |
| 88 if (node is Element) { |
| 89 var ctor = _customElements[node.localName]; |
| 90 if (ctor == null) { |
| 91 var attr = node.attributes['is']; |
| 92 if (attr != null) ctor = _customElements[attr]; |
| 93 } |
| 94 if (ctor != null) _initCustomElement(node, ctor); |
| 95 } |
| 96 return node; |
| 97 } |
| 98 |
| 99 /** |
| 100 * The base class for all Dart web components. In addition to the [Element] |
| 101 * interface, it also provides lifecycle methods: |
| 102 * - [created] |
| 103 * - [inserted] |
| 104 * - [attributeChanged] |
| 105 * - [removed] |
| 106 */ |
| 107 class CustomElement implements Element { |
| 108 /** The web component element wrapped by this class. */ |
| 109 Element _host; |
| 110 List _shadowRoots; |
| 111 |
| 112 /** |
| 113 * Shadow roots generated by dwc for each custom element, indexed by the |
| 114 * custom element tag name. |
| 115 */ |
| 116 Map<String, dynamic> _generatedRoots = {}; |
| 117 |
| 118 /** |
| 119 * Temporary property until components extend [Element]. An element can |
| 120 * only be associated with one host, and it is an error to use a web component |
| 121 * without an associated host element. |
| 122 */ |
| 123 Element get host { |
| 124 if (_host == null) throw new StateError('host element has not been set.'); |
| 125 return _host; |
| 126 } |
| 127 |
| 128 set host(Element value) { |
| 129 if (value == null) { |
| 130 throw new ArgumentError('host must not be null.'); |
| 131 } |
| 132 // TODO(jmesserly): xtag used to return "null" if unset, now it checks for |
| 133 // "this". Temporarily allow both. |
| 134 var xtag = value.xtag; |
| 135 if (xtag != null && xtag != value) { |
| 136 throw new ArgumentError('host must not have its xtag property set.'); |
| 137 } |
| 138 if (_host != null) { |
| 139 throw new StateError('host can only be set once.'); |
| 140 } |
| 141 |
| 142 value.xtag = this; |
| 143 _host = value; |
| 144 } |
| 145 |
| 146 /** |
| 147 * **Note**: This is an implementation helper and should not need to be called |
| 148 * from your code. |
| 149 * |
| 150 * Creates the [ShadowRoot] backing this component. |
| 151 */ |
| 152 createShadowRoot([String componentName]) { |
| 153 var root = host.createShadowRoot(); |
| 154 if (componentName != null) { |
| 155 _generatedRoots[componentName] = root; |
| 156 } |
| 157 return root; |
| 158 } |
| 159 |
| 160 getShadowRoot(String componentName) => _generatedRoots[componentName]; |
| 161 |
| 162 |
| 163 /** |
| 164 * *Warning*: This is an implementation helper for Custom Elements and |
| 165 * should not be used in your code. |
| 166 * |
| 167 * Clones the template, instantiates custom elements and hooks events, then |
| 168 * returns it. |
| 169 */ |
| 170 DocumentFragment cloneTemplate(DocumentFragment shadowTemplate) { |
| 171 var result = shadowTemplate.clone(true); |
| 172 // TODO(jmesserly): should bindModel ensure this happens? |
| 173 TemplateElement.bootstrap(result); |
| 174 if (_templateCreated != null) { |
| 175 for (var callback in _templateCreated) callback(result); |
| 176 } |
| 177 return result; |
| 178 } |
| 179 |
| 180 // TODO(jmesserly): ideally this would be a stream, but they don't allow |
| 181 // reentrancy. |
| 182 static Set<DocumentFragmentCreated> _templateCreated; |
| 183 |
| 184 /** |
| 185 * *Warning*: This is an implementation helper for Custom Elements and |
| 186 * should not be used in your code. |
| 187 * |
| 188 * This event is fired whenever a template is instantiated via |
| 189 * [cloneTemplate] or via [Element.createInstance] |
| 190 */ |
| 191 // TODO(jmesserly): This is a hack, and is neccesary for the polyfill |
| 192 // because custom elements are not upgraded during clone() |
| 193 static Set<DocumentFragmentCreated> get templateCreated { |
| 194 if (_templateCreated == null) { |
| 195 _templateCreated = new Set<DocumentFragmentCreated>(); |
| 196 mdv.instanceCreated.listen((value) { |
| 197 for (var callback in _templateCreated) callback(value); |
| 198 }); |
| 199 } |
| 200 return _templateCreated; |
| 201 } |
| 202 /** |
| 203 * Invoked when this component gets created. |
| 204 * Note that [root] will be a [ShadowRoot] if the browser supports Shadow DOM. |
| 205 */ |
| 206 void created() {} |
| 207 |
| 208 /** Invoked when this component gets inserted in the DOM tree. */ |
| 209 void inserted() {} |
| 210 |
| 211 /** Invoked when this component is removed from the DOM tree. */ |
| 212 void removed() {} |
| 213 |
| 214 // TODO(jmesserly): how do we implement this efficiently? |
| 215 // See https://github.com/dart-lang/web-ui/issues/37 |
| 216 /** Invoked when any attribute of the component is modified. */ |
| 217 void attributeChanged(String name, String oldValue, String newValue) {} |
| 218 |
| 219 get model => host.model; |
| 220 |
| 221 void set model(newModel) { |
| 222 host.model = newModel; |
| 223 } |
| 224 |
| 225 get templateInstance => host.templateInstance; |
| 226 get isTemplate => host.isTemplate; |
| 227 get ref => host.ref; |
| 228 get content => host.content; |
| 229 DocumentFragment createInstance(model, BindingDelegate delegate) => |
| 230 host.createInstance(model, delegate); |
| 231 createBinding(String name, model, String path) => |
| 232 host.createBinding(name, model, path); |
| 233 void bind(String name, model, String path) => host.bind(name, model, path); |
| 234 void unbind(String name) => host.unbind(name); |
| 235 void unbindAll() => host.unbindAll(); |
| 236 get bindings => host.bindings; |
| 237 BindingDelegate get bindingDelegate => host.bindingDelegate; |
| 238 set bindingDelegate(BindingDelegate value) { host.bindingDelegate = value; } |
| 239 |
| 240 // TODO(jmesserly): this forwarding is temporary until Dart supports |
| 241 // subclassing Elements. |
| 242 // TODO(jmesserly): we were missing the setter for title, are other things |
| 243 // missing setters? |
| 244 |
| 245 List<Node> get nodes => host.nodes; |
| 246 |
| 247 set nodes(Iterable<Node> value) { host.nodes = value; } |
| 248 |
| 249 /** |
| 250 * Replaces this node with another node. |
| 251 */ |
| 252 Node replaceWith(Node otherNode) { host.replaceWith(otherNode); } |
| 253 |
| 254 /** |
| 255 * Removes this node from the DOM. |
| 256 */ |
| 257 void remove() => host.remove(); |
| 258 |
| 259 Node get nextNode => host.nextNode; |
| 260 |
| 261 String get nodeName => host.nodeName; |
| 262 |
| 263 Document get document => host.document; |
| 264 |
| 265 Node get previousNode => host.previousNode; |
| 266 |
| 267 String get text => host.text; |
| 268 |
| 269 set text(String v) { host.text = v; } |
| 270 |
| 271 bool contains(Node other) => host.contains(other); |
| 272 |
| 273 bool hasChildNodes() => host.hasChildNodes(); |
| 274 |
| 275 Node insertBefore(Node newChild, Node refChild) => |
| 276 host.insertBefore(newChild, refChild); |
| 277 |
| 278 Node insertAllBefore(Iterable<Node> newChild, Node refChild) => |
| 279 host.insertAllBefore(newChild, refChild); |
| 280 |
| 281 Map<String, String> get attributes => host.attributes; |
| 282 set attributes(Map<String, String> value) { |
| 283 host.attributes = value; |
| 284 } |
| 285 |
| 286 List<Element> get elements => host.children; |
| 287 |
| 288 set elements(List<Element> value) { |
| 289 host.children = value; |
| 290 } |
| 291 |
| 292 List<Element> get children => host.children; |
| 293 |
| 294 set children(List<Element> value) { |
| 295 host.children = value; |
| 296 } |
| 297 |
| 298 Set<String> get classes => host.classes; |
| 299 |
| 300 set classes(Iterable<String> value) { |
| 301 host.classes = value; |
| 302 } |
| 303 |
| 304 CssRect get contentEdge => host.contentEdge; |
| 305 CssRect get paddingEdge => host.paddingEdge; |
| 306 CssRect get borderEdge => host.borderEdge; |
| 307 CssRect get marginEdge => host.marginEdge; |
| 308 Point get documentOffset => host.documentOffset; |
| 309 Point offsetTo(Element parent) => host.offsetTo(parent); |
| 310 |
| 311 Map<String, String> getNamespacedAttributes(String namespace) => |
| 312 host.getNamespacedAttributes(namespace); |
| 313 |
| 314 CssStyleDeclaration getComputedStyle([String pseudoElement]) |
| 315 => host.getComputedStyle(pseudoElement); |
| 316 |
| 317 Element clone(bool deep) => host.clone(deep); |
| 318 |
| 319 Element get parent => host.parent; |
| 320 |
| 321 Node get parentNode => host.parentNode; |
| 322 |
| 323 String get nodeValue => host.nodeValue; |
| 324 |
| 325 @deprecated |
| 326 // TODO(sigmund): restore the old return type and call host.on when |
| 327 // dartbug.com/8131 is fixed. |
| 328 dynamic get on { throw new UnsupportedError('on is deprecated'); } |
| 329 |
| 330 String get contentEditable => host.contentEditable; |
| 331 set contentEditable(String v) { host.contentEditable = v; } |
| 332 |
| 333 String get dir => host.dir; |
| 334 set dir(String v) { host.dir = v; } |
| 335 |
| 336 bool get draggable => host.draggable; |
| 337 set draggable(bool v) { host.draggable = v; } |
| 338 |
| 339 bool get hidden => host.hidden; |
| 340 set hidden(bool v) { host.hidden = v; } |
| 341 |
| 342 String get id => host.id; |
| 343 set id(String v) { host.id = v; } |
| 344 |
| 345 String get innerHTML => host.innerHtml; |
| 346 |
| 347 void set innerHTML(String v) { |
| 348 host.innerHtml = v; |
| 349 } |
| 350 |
| 351 String get innerHtml => host.innerHtml; |
| 352 void set innerHtml(String v) { |
| 353 host.innerHtml = v; |
| 354 } |
| 355 |
| 356 bool get isContentEditable => host.isContentEditable; |
| 357 |
| 358 String get lang => host.lang; |
| 359 set lang(String v) { host.lang = v; } |
| 360 |
| 361 String get outerHtml => host.outerHtml; |
| 362 |
| 363 bool get spellcheck => host.spellcheck; |
| 364 set spellcheck(bool v) { host.spellcheck = v; } |
| 365 |
| 366 int get tabIndex => host.tabIndex; |
| 367 set tabIndex(int i) { host.tabIndex = i; } |
| 368 |
| 369 String get title => host.title; |
| 370 |
| 371 set title(String value) { host.title = value; } |
| 372 |
| 373 bool get translate => host.translate; |
| 374 set translate(bool v) { host.translate = v; } |
| 375 |
| 376 String get dropzone => host.dropzone; |
| 377 set dropzone(String v) { host.dropzone = v; } |
| 378 |
| 379 void click() { host.click(); } |
| 380 |
| 381 InputMethodContext getInputContext() => host.getInputContext(); |
| 382 |
| 383 Element insertAdjacentElement(String where, Element element) => |
| 384 host.insertAdjacentElement(where, element); |
| 385 |
| 386 void insertAdjacentHtml(String where, String html) { |
| 387 host.insertAdjacentHtml(where, html); |
| 388 } |
| 389 |
| 390 void insertAdjacentText(String where, String text) { |
| 391 host.insertAdjacentText(where, text); |
| 392 } |
| 393 |
| 394 Map<String, String> get dataset => host.dataset; |
| 395 |
| 396 set dataset(Map<String, String> value) { |
| 397 host.dataset = value; |
| 398 } |
| 399 |
| 400 Element get nextElementSibling => host.nextElementSibling; |
| 401 |
| 402 Element get offsetParent => host.offsetParent; |
| 403 |
| 404 Element get previousElementSibling => host.previousElementSibling; |
| 405 |
| 406 CssStyleDeclaration get style => host.style; |
| 407 |
| 408 String get tagName => host.tagName; |
| 409 |
| 410 String get pseudo => host.pseudo; |
| 411 |
| 412 void set pseudo(String value) { |
| 413 host.pseudo = value; |
| 414 } |
| 415 |
| 416 // Note: we are not polyfilling the shadow root here. This will be fixed when |
| 417 // we migrate to the JS Shadow DOM polyfills. You can still use getShadowRoot |
| 418 // to retrieve a node that behaves as the shadow root when Shadow DOM is not |
| 419 // enabled. |
| 420 ShadowRoot get shadowRoot => host.shadowRoot; |
| 421 |
| 422 void blur() { host.blur(); } |
| 423 |
| 424 void focus() { host.focus(); } |
| 425 |
| 426 void scrollByLines(int lines) { |
| 427 host.scrollByLines(lines); |
| 428 } |
| 429 |
| 430 void scrollByPages(int pages) { |
| 431 host.scrollByPages(pages); |
| 432 } |
| 433 |
| 434 void scrollIntoView([ScrollAlignment alignment]) { |
| 435 host.scrollIntoView(alignment); |
| 436 } |
| 437 |
| 438 bool matches(String selectors) => host.matches(selectors); |
| 439 |
| 440 @deprecated |
| 441 void requestFullScreen(int flags) { requestFullscreen(); } |
| 442 |
| 443 void requestFullscreen() { host.requestFullscreen(); } |
| 444 |
| 445 void requestPointerLock() { host.requestPointerLock(); } |
| 446 |
| 447 Element query(String selectors) => host.query(selectors); |
| 448 |
| 449 ElementList queryAll(String selectors) => host.queryAll(selectors); |
| 450 |
| 451 HtmlCollection get $dom_children => host.$dom_children; |
| 452 |
| 453 int get $dom_childElementCount => host.$dom_childElementCount; |
| 454 |
| 455 String get className => host.className; |
| 456 set className(String value) { host.className = value; } |
| 457 |
| 458 @deprecated |
| 459 int get clientHeight => client.height; |
| 460 |
| 461 @deprecated |
| 462 int get clientLeft => client.left; |
| 463 |
| 464 @deprecated |
| 465 int get clientTop => client.top; |
| 466 |
| 467 @deprecated |
| 468 int get clientWidth => client.width; |
| 469 |
| 470 Rect get client => host.client; |
| 471 |
| 472 Element get $dom_firstElementChild => host.$dom_firstElementChild; |
| 473 |
| 474 Element get $dom_lastElementChild => host.$dom_lastElementChild; |
| 475 |
| 476 @deprecated |
| 477 int get offsetHeight => offset.height; |
| 478 |
| 479 @deprecated |
| 480 int get offsetLeft => offset.left; |
| 481 |
| 482 @deprecated |
| 483 int get offsetTop => offset.top; |
| 484 |
| 485 @deprecated |
| 486 int get offsetWidth => offset.width; |
| 487 |
| 488 Rect get offset => host.offset; |
| 489 |
| 490 int get scrollHeight => host.scrollHeight; |
| 491 |
| 492 int get scrollLeft => host.scrollLeft; |
| 493 |
| 494 int get scrollTop => host.scrollTop; |
| 495 |
| 496 set scrollLeft(int value) { host.scrollLeft = value; } |
| 497 |
| 498 set scrollTop(int value) { host.scrollTop = value; } |
| 499 |
| 500 int get scrollWidth => host.scrollWidth; |
| 501 |
| 502 String $dom_getAttribute(String name) => |
| 503 host.$dom_getAttribute(name); |
| 504 |
| 505 String $dom_getAttributeNS(String namespaceUri, String localName) => |
| 506 host.$dom_getAttributeNS(namespaceUri, localName); |
| 507 |
| 508 String $dom_setAttributeNS( |
| 509 String namespaceUri, String localName, String value) { |
| 510 host.$dom_setAttributeNS(namespaceUri, localName, value); |
| 511 } |
| 512 |
| 513 bool $dom_hasAttributeNS(String namespaceUri, String localName) => |
| 514 host.$dom_hasAttributeNS(namespaceUri, localName); |
| 515 |
| 516 void $dom_removeAttributeNS(String namespaceUri, String localName) => |
| 517 host.$dom_removeAttributeNS(namespaceUri, localName); |
| 518 |
| 519 Rect getBoundingClientRect() => host.getBoundingClientRect(); |
| 520 |
| 521 List<Rect> getClientRects() => host.getClientRects(); |
| 522 |
| 523 List<Node> getElementsByClassName(String name) => |
| 524 host.getElementsByClassName(name); |
| 525 |
| 526 List<Node> $dom_getElementsByTagName(String name) => |
| 527 host.$dom_getElementsByTagName(name); |
| 528 |
| 529 bool $dom_hasAttribute(String name) => |
| 530 host.$dom_hasAttribute(name); |
| 531 |
| 532 List<Node> $dom_querySelectorAll(String selectors) => |
| 533 host.$dom_querySelectorAll(selectors); |
| 534 |
| 535 void $dom_removeAttribute(String name) => |
| 536 host.$dom_removeAttribute(name); |
| 537 |
| 538 void $dom_setAttribute(String name, String value) => |
| 539 host.$dom_setAttribute(name, value); |
| 540 |
| 541 get $dom_attributes => host.$dom_attributes; |
| 542 |
| 543 List<Node> get $dom_childNodes => host.$dom_childNodes; |
| 544 |
| 545 Node get firstChild => host.firstChild; |
| 546 |
| 547 Node get lastChild => host.lastChild; |
| 548 |
| 549 String get localName => host.localName; |
| 550 String get $dom_localName => host.$dom_localName; |
| 551 |
| 552 String get namespaceUri => host.namespaceUri; |
| 553 String get $dom_namespaceUri => host.$dom_namespaceUri; |
| 554 |
| 555 int get nodeType => host.nodeType; |
| 556 |
| 557 void $dom_addEventListener(String type, EventListener listener, |
| 558 [bool useCapture]) { |
| 559 host.$dom_addEventListener(type, listener, useCapture); |
| 560 } |
| 561 |
| 562 bool dispatchEvent(Event event) => host.dispatchEvent(event); |
| 563 |
| 564 Node $dom_removeChild(Node oldChild) => host.$dom_removeChild(oldChild); |
| 565 |
| 566 void $dom_removeEventListener(String type, EventListener listener, |
| 567 [bool useCapture]) { |
| 568 host.$dom_removeEventListener(type, listener, useCapture); |
| 569 } |
| 570 |
| 571 Node $dom_replaceChild(Node newChild, Node oldChild) => |
| 572 host.$dom_replaceChild(newChild, oldChild); |
| 573 |
| 574 get xtag => host.xtag; |
| 575 |
| 576 set xtag(value) { host.xtag = value; } |
| 577 |
| 578 Node append(Node e) => host.append(e); |
| 579 |
| 580 void appendText(String text) => host.appendText(text); |
| 581 |
| 582 void appendHtml(String html) => host.appendHtml(html); |
| 583 |
| 584 void $dom_scrollIntoView([bool alignWithTop]) { |
| 585 if (alignWithTop == null) { |
| 586 host.$dom_scrollIntoView(); |
| 587 } else { |
| 588 host.$dom_scrollIntoView(alignWithTop); |
| 589 } |
| 590 } |
| 591 |
| 592 void $dom_scrollIntoViewIfNeeded([bool centerIfNeeded]) { |
| 593 if (centerIfNeeded == null) { |
| 594 host.$dom_scrollIntoViewIfNeeded(); |
| 595 } else { |
| 596 host.$dom_scrollIntoViewIfNeeded(centerIfNeeded); |
| 597 } |
| 598 } |
| 599 |
| 600 String get regionOverset => host.regionOverset; |
| 601 |
| 602 List<Range> getRegionFlowRanges() => host.getRegionFlowRanges(); |
| 603 |
| 604 // TODO(jmesserly): rename "created" to "onCreated". |
| 605 void onCreated() => created(); |
| 606 |
| 607 Stream<Event> get onAbort => host.onAbort; |
| 608 Stream<Event> get onBeforeCopy => host.onBeforeCopy; |
| 609 Stream<Event> get onBeforeCut => host.onBeforeCut; |
| 610 Stream<Event> get onBeforePaste => host.onBeforePaste; |
| 611 Stream<Event> get onBlur => host.onBlur; |
| 612 Stream<Event> get onChange => host.onChange; |
| 613 Stream<MouseEvent> get onClick => host.onClick; |
| 614 Stream<MouseEvent> get onContextMenu => host.onContextMenu; |
| 615 Stream<Event> get onCopy => host.onCopy; |
| 616 Stream<Event> get onCut => host.onCut; |
| 617 Stream<Event> get onDoubleClick => host.onDoubleClick; |
| 618 Stream<MouseEvent> get onDrag => host.onDrag; |
| 619 Stream<MouseEvent> get onDragEnd => host.onDragEnd; |
| 620 Stream<MouseEvent> get onDragEnter => host.onDragEnter; |
| 621 Stream<MouseEvent> get onDragLeave => host.onDragLeave; |
| 622 Stream<MouseEvent> get onDragOver => host.onDragOver; |
| 623 Stream<MouseEvent> get onDragStart => host.onDragStart; |
| 624 Stream<MouseEvent> get onDrop => host.onDrop; |
| 625 Stream<Event> get onError => host.onError; |
| 626 Stream<Event> get onFocus => host.onFocus; |
| 627 Stream<Event> get onInput => host.onInput; |
| 628 Stream<Event> get onInvalid => host.onInvalid; |
| 629 Stream<KeyboardEvent> get onKeyDown => host.onKeyDown; |
| 630 Stream<KeyboardEvent> get onKeyPress => host.onKeyPress; |
| 631 Stream<KeyboardEvent> get onKeyUp => host.onKeyUp; |
| 632 Stream<Event> get onLoad => host.onLoad; |
| 633 Stream<MouseEvent> get onMouseDown => host.onMouseDown; |
| 634 Stream<MouseEvent> get onMouseMove => host.onMouseMove; |
| 635 Stream<Event> get onFullscreenChange => host.onFullscreenChange; |
| 636 Stream<Event> get onFullscreenError => host.onFullscreenError; |
| 637 Stream<Event> get onPaste => host.onPaste; |
| 638 Stream<Event> get onReset => host.onReset; |
| 639 Stream<Event> get onScroll => host.onScroll; |
| 640 Stream<Event> get onSearch => host.onSearch; |
| 641 Stream<Event> get onSelect => host.onSelect; |
| 642 Stream<Event> get onSelectStart => host.onSelectStart; |
| 643 Stream<Event> get onSubmit => host.onSubmit; |
| 644 Stream<MouseEvent> get onMouseOut => host.onMouseOut; |
| 645 Stream<MouseEvent> get onMouseOver => host.onMouseOver; |
| 646 Stream<MouseEvent> get onMouseUp => host.onMouseUp; |
| 647 Stream<TouchEvent> get onTouchCancel => host.onTouchCancel; |
| 648 Stream<TouchEvent> get onTouchEnd => host.onTouchEnd; |
| 649 Stream<TouchEvent> get onTouchEnter => host.onTouchEnter; |
| 650 Stream<TouchEvent> get onTouchLeave => host.onTouchLeave; |
| 651 Stream<TouchEvent> get onTouchMove => host.onTouchMove; |
| 652 Stream<TouchEvent> get onTouchStart => host.onTouchStart; |
| 653 Stream<TransitionEvent> get onTransitionEnd => host.onTransitionEnd; |
| 654 |
| 655 // TODO(sigmund): do the normal forwarding when dartbug.com/7919 is fixed. |
| 656 Stream<WheelEvent> get onMouseWheel { |
| 657 throw new UnsupportedError('onMouseWheel is not supported'); |
| 658 } |
| 659 } |
| 660 |
| 661 |
| 662 typedef DocumentFragmentCreated(DocumentFragment fragment); |
| 663 |
| 664 Map<String, Function> _customElements; |
| 665 |
| 666 void _initCustomElement(Element node, CustomElement ctor()) { |
| 667 CustomElement element = ctor(); |
| 668 element.host = node; |
| 669 |
| 670 // TODO(jmesserly): replace lifecycle stuff with a proper polyfill. |
| 671 element.created(); |
| 672 |
| 673 _registerLifecycleInsert(element); |
| 674 } |
| 675 |
| 676 void _registerLifecycleInsert(CustomElement element) { |
| 677 runAsync(() { |
| 678 // TODO(jmesserly): bottom up or top down insert? |
| 679 var node = element.host; |
| 680 |
| 681 // TODO(jmesserly): need a better check to see if the node has been removed. |
| 682 if (node.parentNode == null) return; |
| 683 |
| 684 _registerLifecycleRemove(element); |
| 685 element.inserted(); |
| 686 }); |
| 687 } |
| 688 |
| 689 void _registerLifecycleRemove(CustomElement element) { |
| 690 // TODO(jmesserly): need fallback or polyfill for MutationObserver. |
| 691 if (!MutationObserver.supported) return; |
| 692 |
| 693 new MutationObserver((records, observer) { |
| 694 var node = element.host; |
| 695 for (var record in records) { |
| 696 for (var removed in record.removedNodes) { |
| 697 if (identical(node, removed)) { |
| 698 observer.disconnect(); |
| 699 element.removed(); |
| 700 return; |
| 701 } |
| 702 } |
| 703 } |
| 704 }).observe(element.parentNode, childList: true); |
| 705 } |
OLD | NEW |