Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(526)

Side by Side Diff: lib/custom_element.dart

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

Powered by Google App Engine
This is Rietveld 408576698