| OLD | NEW |
| (Empty) |
| 1 Sky DOM APIs | |
| 2 ============ | |
| 3 | |
| 4 ```dart | |
| 5 // ELEMENT TREE API | |
| 6 | |
| 7 abstract class Node extends EventTarget { | |
| 8 @override | |
| 9 external List<EventTarget> getEventDispatchChain(); // O(N) in number of ances
tors across shadow trees | |
| 10 // implements EventTarget.getEventDispatchChain() | |
| 11 // returns the event dispatch chain (including handling shadow trees) | |
| 12 | |
| 13 external Root get owner; // O(1) | |
| 14 | |
| 15 external ParentNode get parentNode; // O(1) | |
| 16 Element get parentElement { | |
| 17 if (parentNode is Element) | |
| 18 return parentNode as Element; | |
| 19 return null; | |
| 20 } | |
| 21 | |
| 22 external Node get previousSibling; // O(1) | |
| 23 Element get previousElementSibling { | |
| 24 var result = previousSibling; | |
| 25 while (result != null && result is! Element) | |
| 26 result = result.previousSibling; | |
| 27 return result as Element; | |
| 28 } | |
| 29 | |
| 30 external Node get nextSibling; // O(1) | |
| 31 Element get nextElementSibling { | |
| 32 var result = nextSibling; | |
| 33 while (result != null && result is! Element) | |
| 34 result = result.nextSibling; | |
| 35 return result as Element; | |
| 36 } | |
| 37 | |
| 38 // TODO(ianh): rename insertBefore() and insertAfter() since the Web | |
| 39 // has an insertBefore() that means something else. What's a good | |
| 40 // name, though? | |
| 41 | |
| 42 external void _insertBefore(Node node); // O(N) in number of descendants | |
| 43 // node must be Text or Element, parentNode must be non-null | |
| 44 void insertBefore(List nodes) { | |
| 45 List.forEach((node) { | |
| 46 if (node is String) | |
| 47 node = new Text(node); | |
| 48 _insertBefore(node); | |
| 49 }); | |
| 50 } | |
| 51 | |
| 52 external void _insertAfter(Node node); // O(N) in number of arguments plus all
their descendants | |
| 53 // node must be Text or Element, parentNode must be non-null | |
| 54 void insertAfter(List nodes) { | |
| 55 var lastNode = this; | |
| 56 List.forEach((node) { | |
| 57 if (node is String) | |
| 58 node = new Text(node); | |
| 59 lastNode._insertAfter(node); | |
| 60 lastNode = node; | |
| 61 }); | |
| 62 } | |
| 63 | |
| 64 void replaceWith(List nodes) { | |
| 65 if (nextSibling != null) { | |
| 66 var anchor = nextSibling; | |
| 67 remove(); // parentNode can't be null here, so this won't throw | |
| 68 anchor.insertBefore(nodes); | |
| 69 } else { | |
| 70 var anchor = parentNode; | |
| 71 remove(); // throws if parentNode is null | |
| 72 anchor.append(nodes); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 external void remove(); // O(N) in number of descendants | |
| 77 // parentNode must be non-null | |
| 78 | |
| 79 // called when parentNode changes | |
| 80 // this is why insertBefore(), append(), et al, are O(N) -- the whole affected
subtree is walked | |
| 81 // mutating the element tree from within this is strongly discouraged, since i
t will result in the | |
| 82 // callbacks being invoked while the element tree is in a different state than
implied by the callbacks | |
| 83 external void parentChangedCallback(ParentNode oldParent, ParentNode newParent
); // O(N) in descendants | |
| 84 // default implementation calls attached/detached | |
| 85 void attachedCallback() { } | |
| 86 void detachedCallback() { } | |
| 87 | |
| 88 external List<ContentElement> getDestinationInsertionPoints(); // O(N) in numb
er of insertion points the node is in | |
| 89 // returns the <content> elements to which this element was distributed | |
| 90 | |
| 91 external Node cloneNode({bool deep: false}); // O(1) if deep=false, O(N) in th
e number of descendants if deep=true | |
| 92 | |
| 93 external ElementStyleDeclarationList get style; // O(1) | |
| 94 // for nodes that aren't in the ApplicationRoot's composed tree, | |
| 95 // returns null (so in particular orphaned subtrees and nodes in | |
| 96 // module Roots don't have one, nor do shadow tree Roots) | |
| 97 // also always returns null for ContentElement elements | |
| 98 // -- should be (lazily) updated when the node's parent chain | |
| 99 // changes (same time as, e.g., the id hashtable is marked | |
| 100 // dirty) | |
| 101 | |
| 102 external RenderNode get renderNode; // O(1) | |
| 103 // this will be null until the first time it is rendered | |
| 104 // it becomes null again when it is taken out of the rendering (see style.md) | |
| 105 | |
| 106 Type getLayoutManager() => null; // O(1) | |
| 107 | |
| 108 void resetLayoutManager() { // O(1) | |
| 109 if (renderNode != null) { | |
| 110 renderNode._layoutManager = null; | |
| 111 renderNode._needsManager = true; | |
| 112 } | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 abstract class ParentNode extends Node { | |
| 117 external Node get firstChild; // O(1) | |
| 118 Element get firstElementChild { | |
| 119 var result = firstChild; | |
| 120 while (result != null && result is! Element) | |
| 121 result = result.nextSibling; | |
| 122 return result as Element; | |
| 123 } | |
| 124 | |
| 125 external Node get lastChild; // O(1) | |
| 126 Element get lastElementChild { | |
| 127 var result = lastChild; | |
| 128 while (result != null && result is! Element) | |
| 129 result = result.previousSibling; | |
| 130 return result as Element; | |
| 131 } | |
| 132 | |
| 133 // Returns a new List every time. | |
| 134 external List<Node> getChildren(); // O(N) in number of child nodes | |
| 135 List<Element> getChildElements() { | |
| 136 // that the following works without a cast is absurd | |
| 137 return getChildren().where((node) => node is Element).toList(); | |
| 138 } | |
| 139 | |
| 140 external void _appendChild(Node node); // O(N) in number of descendants | |
| 141 // node must be Text or Element | |
| 142 void appendChild(node) { | |
| 143 if (node is String) | |
| 144 node = new Text(node); | |
| 145 _appendChild(node); | |
| 146 } | |
| 147 void append(List nodes) { | |
| 148 nodes.forEach(appendChild); | |
| 149 } | |
| 150 | |
| 151 external void _prependChild(Node node); // O(N) in number of descendants | |
| 152 // node must be Text or Element | |
| 153 void prependChild(node) { | |
| 154 if (node is String) | |
| 155 node = new Text(node); | |
| 156 _prependChild(node); | |
| 157 } | |
| 158 void prepend(List nodes) { | |
| 159 // note: not implemented in terms of _prependChild() | |
| 160 if (firstChild != null) | |
| 161 firstChild.insertBefore(nodes); | |
| 162 else | |
| 163 append(nodes); | |
| 164 } | |
| 165 | |
| 166 external void removeChildren(); // O(N) in number of descendants | |
| 167 void setChild(node) { | |
| 168 removeChildren(); | |
| 169 appendChild(node); | |
| 170 } | |
| 171 void setChildren(List nodes) { | |
| 172 removeChildren(); | |
| 173 append(nodes); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 class Attr { | |
| 178 const Attr (this.name, [this.value = '']); // O(1) | |
| 179 final String name; // O(1) | |
| 180 final String value; // O(1) | |
| 181 } | |
| 182 | |
| 183 // @hasShadow annotation for registering elements | |
| 184 class _HasShadow { | |
| 185 const _HasShadow(); | |
| 186 } | |
| 187 const hasShadow = const _HasShadow(); | |
| 188 | |
| 189 abstract class Element extends ParentNode { | |
| 190 Element({Map<String, String> attributes: null, | |
| 191 List children: null, | |
| 192 Module hostModule: null}) { // O(M+N), M = number of attributes, N =
number of children nodes plus all their descendants | |
| 193 var shadowClass = reflectClass(hasShadow.runtimeType); | |
| 194 var shadowAnnotations = reflect(this).type.metadata.where((mirror) => mirror
.type == shadowClass); | |
| 195 if (shadowAnnotations.length > 2) | |
| 196 throw new StateError('@hasShadow specified multiple times on ' + currentMi
rrorSystem().getName(reflectClass(this.runtimeType).simpleName)); | |
| 197 bool needsShadow = shadowAnnotations.length == 1; | |
| 198 if (children != null) | |
| 199 children = children.map((node) => node is String ? new Text(node) : node).
toList(); | |
| 200 this._initElement(attributes, children, hostModule, needsShadow); | |
| 201 } | |
| 202 external void _initElement(Map<String, String> attributes, List children, Modu
le hostModule, bool needsShadow); | |
| 203 // initialises the internal attributes table, which is a ordered list | |
| 204 // appends the given children nodes | |
| 205 // children must be Text or Element | |
| 206 // if needsShadow is true, creates a shadow tree | |
| 207 | |
| 208 external bool hasAttribute(String name); // O(N) in number of attributes | |
| 209 external String getAttribute(String name); // O(N) in number of attributes | |
| 210 external void setAttribute(String name, [String value = '']); // O(N) in numbe
r of attributes | |
| 211 external void removeAttribute(String name); // O(N) in number of attributes | |
| 212 // calling setAttribute() with a null value removes the attribute | |
| 213 // (calling it without a value sets it to the empty string) | |
| 214 | |
| 215 // Returns a new Array and new Attr instances every time. | |
| 216 external List<Attr> getAttributes(); // O(N) in number of attributes | |
| 217 | |
| 218 external Root get shadowRoot; // O(1) | |
| 219 // returns the shadow root | |
| 220 | |
| 221 void endTagParsedCallback() { } | |
| 222 void attributeChangedCallback(String name, String oldValue, String newValue) {
} | |
| 223 // name will never be null when this is called by sky | |
| 224 | |
| 225 // TODO(ianh): does a node ever need to know when it's been redistributed? | |
| 226 | |
| 227 @override | |
| 228 Type getLayoutManager() { // O(1) | |
| 229 if (renderNode) | |
| 230 return renderNode.getProperty(phDisplay); | |
| 231 return super.getLayoutManager(); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 class Text extends Node { | |
| 236 external Text([String value = '']); // O(1) | |
| 237 | |
| 238 external String get value; // O(1) | |
| 239 external void set (String value); // O(1) | |
| 240 | |
| 241 void valueChangedCallback(String oldValue, String newValue) { } | |
| 242 | |
| 243 @override | |
| 244 Type getLayoutManager() => TextLayoutManager; // O(1) | |
| 245 } | |
| 246 | |
| 247 class Fragment extends ParentNode { | |
| 248 Fragment({List children}); // O(N) in number of arguments plus all their desce
ndants | |
| 249 // children must be String, Text, or Element | |
| 250 } | |
| 251 | |
| 252 class Root extends ParentNode { | |
| 253 Root({List children: null, this.host}) { // O(N) in number of children nodes p
lus all their descendants | |
| 254 if (children != null) | |
| 255 children = children.map((node) => node is String ? new Text(node) : node).
toList(); | |
| 256 this._initRoot(children); | |
| 257 } | |
| 258 external void _initRoot(List children); | |
| 259 // appends the given children nodes | |
| 260 // children must be Text or Element | |
| 261 | |
| 262 final Element host; | |
| 263 | |
| 264 external Element findId(String id); // O(1) | |
| 265 // throws if id is null | |
| 266 } | |
| 267 | |
| 268 class ApplicationRoot extends Root { | |
| 269 ApplicationRoot ({List children}) : super(children: children); // O(N) in numb
er of children nodes arguments plus all their descendants | |
| 270 | |
| 271 @override | |
| 272 Type getLayoutManager() => rootLayoutManager; // O(1) | |
| 273 } | |
| 274 | |
| 275 Type rootLayoutManager = BlockLayoutManager; // O(1) | |
| 276 | |
| 277 class SelectorQuery { | |
| 278 external SelectorQuery(String selector); // O(F()) where F() is the complexity
of the selector | |
| 279 | |
| 280 external bool matches(Element element); // O(F()) | |
| 281 external Element find(Node root); // O(N*F())+O(M) where N is the number of de
scendants and M the average depth of the tree | |
| 282 external List<Element> findAll(Node root); // O(N*F())+O(N*M) where N is the n
umber of descendants and M the average depth of the tree | |
| 283 // find() and findAll() throw if the root is not one of the following: | |
| 284 // - Element | |
| 285 // - Fragment | |
| 286 // - Root | |
| 287 } | |
| 288 ``` | |
| OLD | NEW |