OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 class FilteredElementList implements ElementList { | |
6 final Node _node; | |
7 final NodeList _childNodes; | |
8 | |
9 FilteredElementList(Node node): _childNodes = node.nodes, _node = node; | |
10 | |
11 // We can't memoize this, since it's possible that children will be messed | |
12 // with externally to this class. | |
13 // | |
14 // TODO(nweiz): Do we really need to copy the list to make the types work out? | |
15 List<Element> get _filtered() => | |
16 new List.from(_childNodes.filter((n) => n is Element)); | |
17 | |
18 // Don't use _filtered.first so we can short-circuit once we find an element. | |
19 Element get first() { | |
20 for (final node in _childNodes) { | |
21 if (node is Element) { | |
22 return node; | |
23 } | |
24 } | |
25 return null; | |
26 } | |
27 | |
28 void forEach(void f(Element element)) { | |
29 _filtered.forEach(f); | |
30 } | |
31 | |
32 void operator []=(int index, Element value) { | |
33 this[index].replaceWith(value); | |
34 } | |
35 | |
36 void set length(int newLength) { | |
37 final len = this.length; | |
38 if (newLength >= len) { | |
39 return; | |
40 } else if (newLength < 0) { | |
41 throw const IllegalArgumentException("Invalid list length"); | |
42 } | |
43 | |
44 removeRange(newLength - 1, len - newLength); | |
45 } | |
46 | |
47 void add(Element value) { | |
48 _childNodes.add(value); | |
49 } | |
50 | |
51 void addAll(Collection<Element> collection) { | |
52 collection.forEach(add); | |
53 } | |
54 | |
55 void addLast(Element value) { | |
56 add(value); | |
57 } | |
58 | |
59 void sort(int compare(Element a, Element b)) { | |
60 throw const UnsupportedOperationException('TODO(jacobr): should we impl?'); | |
61 } | |
62 | |
63 void copyFrom(List<Object> src, int srcStart, int dstStart, int count) { | |
64 throw const NotImplementedException(); | |
65 } | |
66 | |
67 void setRange(int start, int length, List from, [int startFrom = 0]) { | |
68 throw const NotImplementedException(); | |
69 } | |
70 | |
71 void removeRange(int start, int length) { | |
72 _filtered.getRange(start, length).forEach((el) => el.remove()); | |
73 } | |
74 | |
75 void insertRange(int start, int length, [initialValue = null]) { | |
76 throw const NotImplementedException(); | |
77 } | |
78 | |
79 void clear() { | |
80 // Currently, ElementList#clear clears even non-element nodes, so we follow | |
81 // that behavior. | |
82 _childNodes.clear(); | |
83 } | |
84 | |
85 Element removeLast() { | |
86 final last = this.last(); | |
87 if (last != null) { | |
88 last.remove(); | |
89 } | |
90 return last; | |
91 } | |
92 | |
93 Collection map(f(Element element)) => _filtered.map(f); | |
94 Collection<Element> filter(bool f(Element element)) => _filtered.filter(f); | |
95 bool every(bool f(Element element)) => _filtered.every(f); | |
96 bool some(bool f(Element element)) => _filtered.some(f); | |
97 bool isEmpty() => _filtered.isEmpty(); | |
98 int get length() => _filtered.length; | |
99 Element operator [](int index) => _filtered[index]; | |
100 Iterator<Element> iterator() => _filtered.iterator(); | |
101 List<Element> getRange(int start, int length) => | |
102 _filtered.getRange(start, length); | |
103 int indexOf(Element element, [int start = 0]) => | |
104 _filtered.indexOf(element, start); | |
105 | |
106 int lastIndexOf(Element element, [int start = null]) { | |
107 if (start === null) start = length - 1; | |
108 return _filtered.lastIndexOf(element, start); | |
109 } | |
110 | |
111 Element last() => _filtered.last(); | |
112 } | |
113 | |
114 Future<CSSStyleDeclaration> _emptyStyleFuture() { | |
115 return _createMeasurementFuture(() => new Element.tag('div').style, | |
116 new Completer<CSSStyleDeclaration>()); | |
117 } | |
118 | |
119 class EmptyElementRect implements ElementRect { | |
120 final ClientRect client = const _SimpleClientRect(0, 0, 0, 0); | |
121 final ClientRect offset = const _SimpleClientRect(0, 0, 0, 0); | |
122 final ClientRect scroll = const _SimpleClientRect(0, 0, 0, 0); | |
123 final ClientRect bounding = const _SimpleClientRect(0, 0, 0, 0); | |
124 final List<ClientRect> clientRects = const <ClientRect>[]; | |
125 | |
126 const EmptyElementRect(); | |
127 } | |
128 | |
129 class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | |
130 ElementList _elements; | |
131 | |
132 ElementList get elements() { | |
133 if (_elements == null) { | |
134 _elements = new FilteredElementList(this); | |
135 } | |
136 return _elements; | |
137 } | |
138 | |
139 // TODO: The type of value should be Collection<Element>. See http://b/5392897 | |
140 void set elements(value) { | |
141 // Copy list first since we don't want liveness during iteration. | |
142 List copy = new List.from(value); | |
143 final elements = this.elements; | |
144 elements.clear(); | |
145 elements.addAll(copy); | |
146 } | |
147 | |
148 ElementList queryAll(String selectors) => | |
149 new _FrozenElementList._wrap(_querySelectorAll(selectors)); | |
150 | |
151 String get innerHTML() { | |
152 final e = new Element.tag("div"); | |
153 e.nodes.add(this.clone(true)); | |
154 return e.innerHTML; | |
155 } | |
156 | |
157 String get outerHTML() => innerHTML; | |
158 | |
159 // TODO(nweiz): Do we want to support some variant of innerHTML for XML and/or | |
160 // SVG strings? | |
161 void set innerHTML(String value) { | |
162 this.nodes.clear(); | |
163 | |
164 final e = new Element.tag("div"); | |
165 e.innerHTML = value; | |
166 | |
167 // Copy list first since we don't want liveness during iteration. | |
168 List nodes = new List.from(e.nodes); | |
169 this.nodes.addAll(nodes); | |
170 } | |
171 | |
172 Node _insertAdjacentNode(String where, Node node) { | |
173 switch (where.toLowerCase()) { | |
174 case "beforebegin": return null; | |
175 case "afterend": return null; | |
176 case "afterbegin": | |
177 this.insertBefore(node, this.nodes.first); | |
178 return node; | |
179 case "beforeend": | |
180 this.nodes.add(node); | |
181 return node; | |
182 default: | |
183 throw new IllegalArgumentException("Invalid position ${where}"); | |
184 } | |
185 } | |
186 | |
187 Element insertAdjacentElement(String where, Element element) | |
188 => this._insertAdjacentNode(where, element); | |
189 | |
190 void insertAdjacentText(String where, String text) { | |
191 this._insertAdjacentNode(where, new Text(text)); | |
192 } | |
193 | |
194 void insertAdjacentHTML(String where, String text) { | |
195 this._insertAdjacentNode(where, new DocumentFragment.html(text)); | |
196 } | |
197 | |
198 Future<ElementRect> get rect() { | |
199 return _createMeasurementFuture(() => const EmptyElementRect(), | |
200 new Completer<ElementRect>()); | |
201 } | |
202 | |
203 // If we can come up with a semi-reasonable default value for an Element | |
204 // getter, we'll use it. In general, these return the same values as an | |
205 // element that has no parent. | |
206 String get contentEditable() => "false"; | |
207 bool get isContentEditable() => false; | |
208 bool get draggable() => false; | |
209 bool get hidden() => false; | |
210 bool get spellcheck() => false; | |
211 bool get translate() => false; | |
212 int get tabIndex() => -1; | |
213 String get id() => ""; | |
214 String get title() => ""; | |
215 String get tagName() => ""; | |
216 String get webkitdropzone() => ""; | |
217 String get webkitRegionOverflow() => ""; | |
218 Element get firstElementChild() => elements.first(); | |
219 Element get lastElementChild() => elements.last(); | |
220 Element get nextElementSibling() => null; | |
221 Element get previousElementSibling() => null; | |
222 Element get offsetParent() => null; | |
223 Element get parent() => null; | |
224 Map<String, String> get attributes() => const {}; | |
225 // Issue 174: this should be a const set. | |
226 Set<String> get classes() => new Set<String>(); | |
227 Map<String, String> get dataAttributes() => const {}; | |
228 CSSStyleDeclaration get style() => new Element.tag('div').style; | |
229 Future<CSSStyleDeclaration> get computedStyle() => | |
230 _emptyStyleFuture(); | |
231 Future<CSSStyleDeclaration> getComputedStyle(String pseudoElement) => | |
232 _emptyStyleFuture(); | |
233 bool matchesSelector(String selectors) => false; | |
234 | |
235 // Imperative Element methods are made into no-ops, as they are on parentless | |
236 // elements. | |
237 void blur() {} | |
238 void focus() {} | |
239 void click() {} | |
240 void scrollByLines(int lines) {} | |
241 void scrollByPages(int pages) {} | |
242 void scrollIntoView([bool centerIfNeeded]) {} | |
243 void webkitRequestFullScreen(int flags) {} | |
244 void webkitRequestFullscreen() {} | |
245 | |
246 // Setters throw errors rather than being no-ops because we aren't going to | |
247 // retain the values that were set, and erroring out seems clearer. | |
248 void set attributes(Map<String, String> value) { | |
249 throw new UnsupportedOperationException( | |
250 "Attributes can't be set for document fragments."); | |
251 } | |
252 | |
253 void set classes(Collection<String> value) { | |
254 throw new UnsupportedOperationException( | |
255 "Classes can't be set for document fragments."); | |
256 } | |
257 | |
258 void set dataAttributes(Map<String, String> value) { | |
259 throw new UnsupportedOperationException( | |
260 "Data attributes can't be set for document fragments."); | |
261 } | |
262 | |
263 void set contentEditable(String value) { | |
264 throw new UnsupportedOperationException( | |
265 "Content editable can't be set for document fragments."); | |
266 } | |
267 | |
268 String get dir() { | |
269 throw new UnsupportedOperationException( | |
270 "Document fragments don't support text direction."); | |
271 } | |
272 | |
273 void set dir(String value) { | |
274 throw new UnsupportedOperationException( | |
275 "Document fragments don't support text direction."); | |
276 } | |
277 | |
278 void set draggable(bool value) { | |
279 throw new UnsupportedOperationException( | |
280 "Draggable can't be set for document fragments."); | |
281 } | |
282 | |
283 void set hidden(bool value) { | |
284 throw new UnsupportedOperationException( | |
285 "Hidden can't be set for document fragments."); | |
286 } | |
287 | |
288 void set id(String value) { | |
289 throw new UnsupportedOperationException( | |
290 "ID can't be set for document fragments."); | |
291 } | |
292 | |
293 String get lang() { | |
294 throw new UnsupportedOperationException( | |
295 "Document fragments don't support language."); | |
296 } | |
297 | |
298 void set lang(String value) { | |
299 throw new UnsupportedOperationException( | |
300 "Document fragments don't support language."); | |
301 } | |
302 | |
303 void set scrollLeft(int value) { | |
304 throw new UnsupportedOperationException( | |
305 "Document fragments don't support scrolling."); | |
306 } | |
307 | |
308 void set scrollTop(int value) { | |
309 throw new UnsupportedOperationException( | |
310 "Document fragments don't support scrolling."); | |
311 } | |
312 | |
313 void set spellcheck(bool value) { | |
314 throw new UnsupportedOperationException( | |
315 "Spellcheck can't be set for document fragments."); | |
316 } | |
317 | |
318 void set translate(bool value) { | |
319 throw new UnsupportedOperationException( | |
320 "Spellcheck can't be set for document fragments."); | |
321 } | |
322 | |
323 void set tabIndex(int value) { | |
324 throw new UnsupportedOperationException( | |
325 "Tab index can't be set for document fragments."); | |
326 } | |
327 | |
328 void set title(String value) { | |
329 throw new UnsupportedOperationException( | |
330 "Title can't be set for document fragments."); | |
331 } | |
332 | |
333 void set webkitdropzone(String value) { | |
334 throw new UnsupportedOperationException( | |
335 "WebKit drop zone can't be set for document fragments."); | |
336 } | |
337 | |
338 void set webkitRegionOverflow(String value) { | |
339 throw new UnsupportedOperationException( | |
340 "WebKit region overflow can't be set for document fragments."); | |
341 } | |
342 | |
343 $!MEMBERS | |
344 } | |
OLD | NEW |