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

Unified Diff: samples/webcomponents/shadow_polyfill.dart

Issue 10964030: A variety of cleanups to bring our samples up-to-date. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « samples/webcomponents/list_map.dart ('k') | utils/css/tree.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: samples/webcomponents/shadow_polyfill.dart
===================================================================
--- samples/webcomponents/shadow_polyfill.dart (revision 12654)
+++ samples/webcomponents/shadow_polyfill.dart (working copy)
@@ -1,347 +0,0 @@
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * Polyfill script for custom elements. To use this script, your app must
- * create a CustomElementsManager with the appropriate lookup function before
- * doing any DOM queries or modifications.
- * Currently, all custom elements must be registered with the polyfill. To
- * register custom elements, provide the appropriate lookup function to your
- * CustomElementsManager.
- *
- * This script only works at present in dart2js, but it should work in dartium
- * soon (pending MutationObservers). The script does an HTTP request, so
- * to test using locally defined custom elements you must run chrome with the
- * flag -allow-file-access-from-files.
- */
-
-#library('webcomponents');
-
-#import('dart:html');
-
-#source('list_map.dart');
-
-// typedefs
-typedef WebComponent WebComponentFactory (ShadowRoot shadowRoot, Element elt);
-typedef WebComponentFactory RegistryLookupFunction(String tagName);
-
-// Globals
-const int REQUEST_DONE = 4;
-CustomElementsManager _manager;
-CustomElementsManager get manager => _manager;
-
-void initializeComponents(RegistryLookupFunction lookup) {
- _manager = new CustomElementsManager._internal(lookup);
- manager._loadComponents();
-}
-
-/** A Dart wrapper for a web component. */
-abstract class WebComponent {
- /** The web component element wrapped by this class. */
- abstract Element get element;
-
- /** Invoked when this component gets created. */
- abstract void created();
-
- /** Invoked when this component gets inserted in the DOM tree. */
- abstract void inserted();
-
- /** Invoked when any attribute of the component is modified. */
- abstract void attributeChanged(
- String name, String oldValue, String newValue);
-
- /** Invoked when this component is removed from the DOM tree. */
- abstract void removed();
-}
-
-/** Loads and manages the custom elements on a page. */
-class CustomElementsManager {
-
- /**
- * Maps tag names to our internal dart representation of the custom element.
- */
- Map<String, _CustomDeclaration> _customDeclarations;
-
- // TODO(samhop): evaluate possibility of using vsm's trick of storing
- // arbitrary Dart objects directly on DOM objects rather than this map.
- /** Maps DOM elements to the user-defiend corresponding dart objects. */
- ListMap<Element, WebComponent> _customElements;
-
- RegistryLookupFunction _lookup;
-
- MutationObserver _insertionObserver;
-
- CustomElementsManager._internal(this._lookup) {
- // TODO(samhop): check for ShadowDOM support
- _customDeclarations = <String, _CustomDeclaration>{};
- // We use a ListMap because DOM objects aren't hashable right now.
- // TODO(samhop): DOM objects (and everything else) should be hashable
- _customElements = new ListMap<Element, WebComponent>();
- initializeInsertedRemovedCallbacks(document);
- }
-
- /**
- * Locate all external component files, load each of them, and expand
- * declarations.
- */
- void _loadComponents() {
- queryAll('link[rel=components]').forEach((link) => _load(link.href));
- _expandDeclarations();
- }
-
- /**
- * Load the document at the given url and parse it to extract
- * custom element declarations.
- */
- void _load(String url) {
- var request = new HttpRequest();
- // We use a blocking request here because no Dart is allowed to run
- // until DOM content is loaded.
- // TODO(samhop): give a decent timeout message if custom elements fail to
- // load
- request.open('GET', url, async: false);
- request.on.readyStateChange.add((Event e) {
- if (request.readyState == REQUEST_DONE) {
- if (request.status >= 200 && request.status < 300
- || request.status == 304 || request.status == 0) {
- var declarations = _parse(request.response);
- declarations.forEach((declaration) {
- _customDeclarations[declaration.name] = declaration;
- });
- } else {
- window.console.error(
- 'Unable to load component: Status ${request.status}'
- ' - ${request.statusText}');
- }
- }
- });
- request.send();
- }
-
- /** Parse the given string of HTML to extract the custom declarations. */
- List<_CustomDeclaration> _parse(String toParse) {
- var declarations = new DocumentFragment.html(toParse);
- var newDeclarations = [];
- declarations.queryAll('element').forEach((element) {
- newDeclarations.add(new _CustomDeclaration(element));
- });
- return newDeclarations;
- }
-
- /**
- * Look for all custom elements under [root] and expand them appropriately.
- * This calls the [created] event on a webcomponent, but it will not insert
- * the component in the DOM tree (and hence it won't call [inserted].
- */
- List expandDeclarations(Element root) =>
- _expandDeclarations(root, insert: false);
-
- /** Look for all custom elements uses and expand them appropriately. */
- List _expandDeclarations([Element root, bool insert = true]) {
- var newCustomElements = [];
- var target;
- var rootUnderTemplate = false;
- if (root == null) {
- target = document;
- } else {
- target = root;
- rootUnderTemplate = root.matchesSelector('template *');
- }
- for (var declaration in _customDeclarations.getValues()) {
- var selector = '${declaration.extendz}[is=${declaration.name}]';
- var all = target.queryAll(selector);
- // templates are innert and should not be expanded.
- var activeElements = all.filter(
- (e) => !e.matchesSelector('template *'));
- if (root != null && root.matchesSelector(selector)
- && !rootUnderTemplate) {
- activeElements.add(root);
- }
- for (var e in activeElements) {
- var component = _customElements[e];
- if (component == null) {
- component = declaration.morph(e);
- newCustomElements.add(component);
- }
- if (insert) {
- component.inserted();
- }
- }
- }
- return newCustomElements;
- }
-
-
- /** Fire the [removed] event on all web components under [root]. */
- void _removeComponents(Element root) {
- for (var decl in _customDeclarations.getValues()) {
- for (var e in root.queryAll('${decl.extendz}[is=${decl.name}]')) {
- if (_customElements[e] != null) _customElements[e].removed();
- }
- if (root.matchesSelector('${decl.extendz}[is=${decl.name}]')) {
- if (_customElements[root] != null) _customElements[root].removed();
- }
- }
- }
-
- /**
- * Expands the given html string into a custom element.
- * Assumes only one element use in htmlString (or only returns the
- * first one) and assumes corresponding custom element is already
- * registered.
- */
- WebComponent expandHtml(String htmlString) {
- return expandElement(new Element.html(htmlString));
- }
-
- /** Expand [element], assuming it is a webcomponent. */
- WebComponent expandElement(Element element) {
- var declaration = _customDeclarations[element.attributes['is']];
- if (declaration == null) throw 'No such custom element declaration';
- return declaration.morph(element);
- }
-
- WebComponent operator [](Element element) => _customElements[element];
-
- // Initializes management of inserted and removed
- // callbacks for WebComponents below root in the DOM. We need one of these
- // for every shadow subtree, since mutation observers can't see across
- // shadow boundaries.
- void initializeInsertedRemovedCallbacks(Element root) {
- _insertionObserver = new MutationObserver((mutations, observer) {
- for (var mutation in mutations) {
- // TODO(samhop): remove this test if it turns out that it always passes
- if (mutation.type == 'childList') {
- for (var node in mutation.addedNodes) {
- if (node is Element) _expandDeclarations(node);
- }
- for (var node in mutation.removedNodes) {
- if (node is Element) _removeComponents(node);
- }
- }
- }
- });
- _insertionObserver.observe(root, childList: true, subtree: true);
- }
-}
-
-bool _hasShadowRoot;
-
-/**
- * True if the browser supports the [ShadowRoot] element and it is enabled.
- * See the [Shadow DOM spec](http://www.w3.org/TR/shadow-dom/) for more
- * information about the ShadowRoot.
- */
-bool get hasShadowRoot {
- if (_hasShadowRoot == null) {
- try {
- // TODO(jmesserly): it'd be nice if we could check this without causing
- // an exception to be thrown.
- new ShadowRoot(new DivElement());
- _hasShadowRoot = true;
- } catch (e) {
- _hasShadowRoot = false;
- // Hide <template> elements.
- // TODO(jmesserly): This is a workaround because we don't distribute
- // children correctly. It's not actually the right fix.
- var style = new Element.html(
- @'<style type="text/css">template { display: none; }</style>');
- document.head.nodes.add(style);
- }
- }
- return _hasShadowRoot;
-}
-
-class _CustomDeclaration {
- String name;
- String extendz;
- Element template;
- bool applyAuthorStyles;
-
- _CustomDeclaration(Element element) {
- name = element.attributes['name'];
- applyAuthorStyles = element.attributes.containsKey('apply-author-styles');
- if (name == null) {
- // TODO(samhop): friendlier errors
- window.console.error('name attribute is required');
- return;
- }
- extendz = element.attributes['extends'];
- if (extendz == null || extendz.length == 0) {
- window.console.error('extends attribute is required');
- return;
- }
- template = element.query('template');
- }
-
- int hashCode() {
- return name.hashCode();
- }
-
- operator ==(other) {
- if (other is! _CustomDeclaration) {
- return false;
- } else {
- return other.name == name &&
- other.extendz == extendz &&
- other.template == template;
- }
- }
-
- // TODO(samhop): better docs
- /**
- * Modify the DOM for e, return a new Dart object corresponding to it.
- * Returns null if this custom declaration has no template element.
- */
- WebComponent morph(Element e) {
- if (template == null) {
- return null;
- }
-
- var shadowRoot;
- if (hasShadowRoot) {
- shadowRoot = new ShadowRoot(e);
- shadowRoot.resetStyleInheritance = false;
- if (applyAuthorStyles) {
- shadowRoot.applyAuthorStyles = true;
- }
- } else {
- // Remove the old ShadowRoot, if any
- // TODO(jmesserly): can we avoid morphing the same node twice?
- shadowRoot = e.query('.shadowroot');
- if (shadowRoot != null && shadowRoot.parent == e) shadowRoot.remove();
-
- // TODO(jmesserly): distribute children to insertion points.
- shadowRoot = new Element.html('<div class="shadowroot"></div>');
- e.nodes.add(shadowRoot);
- }
-
- template.nodes.forEach((node) => shadowRoot.nodes.add(node.clone(true)));
- var newCustomElement = manager._lookup(this.name)(shadowRoot, e);
- manager._customElements[e] = newCustomElement;
- manager._expandDeclarations(shadowRoot, insert: false);
- newCustomElement.created();
- manager._expandDeclarations(shadowRoot, insert: true);
-
- // TODO(samhop): investigate refactoring/redesigning the API so that
- // components which don't need their attributes observed don't have an
- // observer created, for perf reasons.
- var attributeObserver = new MutationObserver((mutations, observer) {
- for (var mutation in mutations) {
- if (mutation.type == 'attributes') {
- var attrName = mutation.attributeName;
- Element element = mutation.target;
- newCustomElement.attributeChanged(attrName,
- mutation.oldValue, element.attributes[attrName]);
- }
- }
- });
- attributeObserver.observe(e, attributes: true, attributeOldValue: true);
-
- // Listen for all insertions and deletions on the DOM so that we can
- // catch custom elements being inserted and call the appropriate callbacks.
- manager.initializeInsertedRemovedCallbacks(shadowRoot);
- return newCustomElement;
- }
-}
« no previous file with comments | « samples/webcomponents/list_map.dart ('k') | utils/css/tree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698