| Sky DOM APIs |
| ============ |
| |
| ```dart |
| SKY MODULE |
| <!-- part of sky:core --> |
| |
| <script> |
| // ELEMENT TREE API |
| |
| abstract class ChildNode { |
| @nonnull external TreeScope get ownerScope; // O(1) |
| |
| external ParentNode get parentNode; // O(1) |
| external Element get parentElement; // O(1) // if parentNode isn't an element, returns null |
| external ChildNode get previousSibling; // O(1) |
| external ChildNode get nextSibling; // O(1) |
| |
| // the following all throw if parentNode is null |
| external void insertBefore(@nonnull List</*@nonnull*/ ChildNode> nodes); // O(N) in number of arguments plus all their descendants |
| external void insertAfter(@nonnull List</*@nonnull*/ ChildNode> nodes); // O(N) in number of arguments plus all their descendants |
| external void replaceWith(@nonnull List</*@nonnull*/ ChildNode> nodes); // O(N) in number of descendants plus arguments plus all their descendants |
| external void remove(); // O(N) in number of descendants |
| |
| // called when parentNode changes |
| external void parentChangeCallback(ParentNode oldParent, ParentNode newParent, ChildNode previousSibling, ChildNode nextSibling); // O(N) in descendants |
| // default implementation calls attached/detached |
| void attachedCallback() { } |
| void detachedCallback() { } |
| |
| external List<ContentElement> getDestinationInsertionPoints(); // O(N) in number of insertion points the node is in |
| // returns the <content> elements to which this element was distributed |
| } |
| |
| abstract class Node extends EventTarget { |
| @override |
| external List</*@nonnull*/ EventTarget> getEventDispatchChain(); // O(N) in number of ancestors across shadow trees |
| // implements EventTarget.getEventDispatchChain() |
| // returns the event dispatch chain (including handling shadow trees) |
| |
| external Node cloneNode({bool deep: false}); // O(1) if deep=false, O(N) in the number of descendants if deep=true |
| |
| external ElementStyleDeclarationList get style; // O(1) |
| // for nodes that aren't reachable from the Application Document, returns null |
| // (so in particular orphaned subtrees and nodes in module documents don't have one) |
| // -- should be updated when the node's parent chain changes (same time as, e.g., |
| // the id hashtable is updated) |
| // also always returns null for ContentElement elements and ShadowRoot nodes |
| |
| external RenderNode get renderNode; // O(1) |
| // this will be null until the first time it is rendered |
| // it becomes null again when it is taken out of the rendering (see style.md) |
| |
| Type getLayoutManager() => null; // O(1) |
| |
| void resetLayoutManager() { // O(1) |
| if (renderNode != null) { |
| renderNode._layoutManager = null; |
| renderNode._needsManager = true; |
| } |
| } |
| } |
| |
| abstract class ParentNode extends Node { |
| external ChildNode get firstChild; // O(1) |
| external ChildNode get lastChild; // O(1) |
| |
| // Returns a new List every time. |
| external List</*nonnull*/ ChildNode> getChildNodes(); // O(N) in number of child nodes |
| external List<Element> getChildElements(); // O(N) in number of child nodes |
| // TODO(ianh): might not be necessary if we have the parser drop unnecessary whitespace text nodes |
| |
| external void append(@nonnull List</*nonnull*/ ChildNode> nodes); // O(N) in number of arguments plus all their descendants |
| external void prepend(@nonnull List</*nonnull*/ ChildNode> nodes); // O(N) in number of arguments plus all their descendants |
| external void replaceChildrenWith(@nonnull List</*nonnull*/ ChildNode> nodes); // O(N) in number of descendants plus arguments plus all their descendants |
| } |
| |
| class Attr { |
| const Attr (this.name, [this.value = '']); // O(1) |
| final String name; // O(1) |
| final String value; // O(1) |
| } |
| |
| // @tagname annotation for registering elements |
| // only useful when placed on classes that inherit from Element |
| class tagname extends AutomaticMetadata { |
| const tagname(this.name); |
| @nonnull final String name; |
| void init(DeclarationMirror target, Module module) { |
| assert(target is ClassMirror); |
| if (!target.isSubclassOf(reflectClass(Element))) |
| throw Error('@tagname can only be used on descendants of Element'); |
| module.registerElement(name, (target as ClassMirror).reflectedType); |
| } |
| } |
| |
| abstract class FindRoot { } |
| |
| abstract class Element extends ParentNode with ChildNode implements FindRoot { |
| external Element({Map</*@nonnull*/ String, /*@nonnull*/ String> attributes: null, |
| List</*nonnull*/ ChildNode> nodes: null, |
| Module hostModule: null}); // O(M+N), M = number of attributes, N = number of nodes plus all their descendants |
| // initialises the internal attributes table |
| // appends the given child nodes |
| // if this.needsShadow, creates a shadow tree |
| |
| @nonnull String get tagName { // O(N) in number of annotations on the class |
| // throws a StateError if the class doesn't have an @tagname annotation |
| var tagnameClass = reflectClass(tagname); |
| return (reflectClass(this.runtimeType).metadata.singleWhere((mirror) => mirror.type == tagnameClass).reflectee as tagname).value; |
| } |
| |
| @nonnull external bool hasAttribute(@nonnull String name); // O(N) in number of attributes |
| @nonnull external String getAttribute(@nonnull String name); // O(N) in number of attributes |
| external void setAttribute(@nonnull String name, [@nonnull String value = '']); // O(N) in number of attributes |
| external void removeAttribute(@nonnull String name); // O(N) in number of attributes |
| |
| // Returns a new Array and new Attr instances every time. |
| @nonnull external List<Attr> getAttributes(); // O(N) in number of attributes |
| |
| get bool needsShadow => false; // O(1) |
| external ShadowRoot get shadowRoot; // O(1) |
| // returns the shadow root |
| // TODO(ianh): Should this be mutable? It would help explain how it gets set... |
| |
| void endTagParsedCallback() { } |
| void attributeChangeCallback(String name, String oldValue, String newValue) { } |
| // name will never be null when this is called by sky |
| |
| // TODO(ianh): does a node ever need to know when it's been redistributed? |
| |
| @override |
| Type getLayoutManager() { // O(1) |
| if (renderNode) |
| return renderNode.getProperty(phDisplay); |
| return super.getLayoutManager(); |
| } |
| } |
| |
| class Text extends Node with ChildNode { |
| external Text([@nonnull String value = '']); // O(1) |
| |
| @nonnull external String get value; // O(1) |
| external void set (@nonnull String value); // O(1) |
| |
| void valueChangeCallback(@nonnull String oldValue, @nonnull String newValue) { } |
| |
| @override |
| Type getLayoutManager() => TextLayoutManager; // O(1) |
| } |
| |
| class DocumentFragment extends ParentNode implements FindRoot { |
| DocumentFragment([List</*nonnull*/ ChildNode> nodes = null]); // O(N) in number of arguments plus all their descendants |
| } |
| |
| abstract class TreeScope extends ParentNode { |
| external Document get ownerDocument; // O(1) |
| external TreeScope get parentScope; // O(1) |
| |
| external Element findId(String id); // O(1) |
| // throws if id is null |
| } |
| |
| class ShadowRoot extends TreeScope implements FindRoot { |
| ShadowRoot([this._host]); // O(1) |
| // note that there is no way in the API to use a newly created ShadowRoot currently |
| |
| Element _host; |
| Element get host => _host; // O(1) |
| } |
| |
| class Document extends TreeScope implements FindRoot { |
| external Document ([List</*@nonnull*/ ChildNode> nodes = null]); // O(N) in number of arguments plus all their descendants |
| } |
| |
| class ApplicationDocument extends Document { |
| external ApplicationDocument ([List</*@nonnull*/ ChildNode> nodes = null]); // O(N) in number of /nodes/ arguments plus all their descendants |
| |
| @override |
| Type getLayoutManager() => rootLayoutManager; // O(1) |
| } |
| |
| Type rootLayoutManager = BlockLayoutManager; // O(1) |
| |
| |
| // BUILT-IN ELEMENTS |
| |
| @tagname('import') |
| class ImportElement extends Element { |
| ImportElement = Element; |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| @tagname('template') |
| class TemplateElement extends Element { |
| TemplateElement = Element; |
| |
| // TODO(ianh): convert <template> to using a token stream instead of a DocumentFragment |
| |
| @nonnull external DocumentFragment get content; // O(1) |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| @tagname('script') |
| class ScriptElement extends Element { |
| ScriptElement = Element; |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| @tagname('style') |
| class StyleElement extends Element { |
| StyleElement = Element; |
| |
| @nonnull external List</*@nonnull*/ Rule> getRules(); // O(N) in rules |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| @tagname('content') |
| class ContentElement extends Element { |
| ContentElement = Element; |
| |
| @nonnull external List</*@nonnull*/ Node> getDistributedNodes(); // O(N) in distributed nodes |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| @tagname('img') |
| class ImgElement extends Element { |
| ImgElement = Element; |
| |
| @override |
| Type getLayoutManager() => ImgElementLayoutManager; // O(1) |
| } |
| |
| @tagname('div') |
| class DivElement extends Element { |
| DivElement = Element; |
| } |
| |
| @tagname('span') |
| class SpanElement extends Element { |
| SpanElement = Element; |
| } |
| |
| @tagname('iframe') |
| class IframeElement extends Element { |
| IframeElement = Element; |
| |
| @override |
| Type getLayoutManager() => IframeElementLayoutManager; // O(1) |
| } |
| |
| @tagname('t') |
| class TElement extends Element { |
| TElement = Element; |
| } |
| |
| @tagname('a') |
| class AElement extends Element { |
| AElement = Element; |
| } |
| |
| @tagname('title') |
| class TitleElement extends Element { |
| TitleElement = Element; |
| |
| @override |
| Type getLayoutManager() => null; // O(1) |
| } |
| |
| class _ErrorElement extends Element { |
| _ErrorElement._create(); |
| |
| @override |
| Type getLayoutManager() => _ErrorElementLayoutManager; // O(1) |
| } |
| |
| class SelectorQuery { |
| external SelectorQuery(@nonnull String selector); // O(F()) where F() is the complexity of the selector |
| |
| @nonnull external bool matches(@nonnull Element element); // O(F()) |
| external Element find(@nonnull FindRoot root); // O(N*F())+O(M) where N is the number of descendants and M the average depth of the tree |
| @nonnull external List</*@nonnull*/ Element> findAll(FindRoot root); // O(N*F())+O(N*M) where N is the number of descendants and M the average depth of the tree |
| } |
| </script> |
| ``` |