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

Side by Side Diff: lib/web_components.dart

Issue 11465028: rename web_components -> web_ui (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 8 years 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
OLDNEW
(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;
OLDNEW
« no previous file with comments | « lib/templating.dart ('k') | lib/web_ui.dart » ('j') | pubspec.yaml » ('J')

Powered by Google App Engine
This is Rietveld 408576698