OLD | NEW |
| (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 // TODO(jacobr): use _Lists.dart to remove some of the duplicated | |
6 // functionality. | |
7 class _ChildrenElementList implements ElementList { | |
8 // Raw Element. | |
9 final _ElementImpl _element; | |
10 final _HTMLCollectionImpl _childElements; | |
11 | |
12 _ChildrenElementList._wrap(_ElementImpl element) | |
13 : _childElements = element._children, | |
14 _element = element; | |
15 | |
16 List<Element> _toList() { | |
17 final output = new List(_childElements.length); | |
18 for (int i = 0, len = _childElements.length; i < len; i++) { | |
19 output[i] = _childElements[i]; | |
20 } | |
21 return output; | |
22 } | |
23 | |
24 _ElementImpl get first() { | |
25 return _element._firstElementChild; | |
26 } | |
27 | |
28 void forEach(void f(Element element)) { | |
29 for (_ElementImpl element in _childElements) { | |
30 f(element); | |
31 } | |
32 } | |
33 | |
34 ElementList filter(bool f(Element element)) { | |
35 final output = <Element>[]; | |
36 forEach((Element element) { | |
37 if (f(element)) { | |
38 output.add(element); | |
39 } | |
40 }); | |
41 return new _FrozenElementList._wrap(output); | |
42 } | |
43 | |
44 bool every(bool f(Element element)) { | |
45 for(Element element in this) { | |
46 if (!f(element)) { | |
47 return false; | |
48 } | |
49 }; | |
50 return true; | |
51 } | |
52 | |
53 bool some(bool f(Element element)) { | |
54 for(Element element in this) { | |
55 if (f(element)) { | |
56 return true; | |
57 } | |
58 }; | |
59 return false; | |
60 } | |
61 | |
62 Collection map(f(Element element)) { | |
63 final out = []; | |
64 for (Element el in this) { | |
65 out.add(f(el)); | |
66 } | |
67 return out; | |
68 } | |
69 | |
70 bool isEmpty() { | |
71 return _element._firstElementChild == null; | |
72 } | |
73 | |
74 int get length() { | |
75 return _childElements.length; | |
76 } | |
77 | |
78 _ElementImpl operator [](int index) { | |
79 return _childElements[index]; | |
80 } | |
81 | |
82 void operator []=(int index, _ElementImpl value) { | |
83 _element._replaceChild(value, _childElements[index]); | |
84 } | |
85 | |
86 void set length(int newLength) { | |
87 // TODO(jacobr): remove children when length is reduced. | |
88 throw const UnsupportedOperationException(''); | |
89 } | |
90 | |
91 Element add(_ElementImpl value) { | |
92 _element._appendChild(value); | |
93 return value; | |
94 } | |
95 | |
96 Element addLast(_ElementImpl value) => add(value); | |
97 | |
98 Iterator<Element> iterator() => _toList().iterator(); | |
99 | |
100 void addAll(Collection<Element> collection) { | |
101 for (_ElementImpl element in collection) { | |
102 _element._appendChild(element); | |
103 } | |
104 } | |
105 | |
106 void sort(int compare(Element a, Element b)) { | |
107 throw const UnsupportedOperationException('TODO(jacobr): should we impl?'); | |
108 } | |
109 | |
110 void copyFrom(List<Object> src, int srcStart, int dstStart, int count) { | |
111 throw 'Not impl yet. todo(jacobr)'; | |
112 } | |
113 | |
114 void setRange(int start, int length, List from, [int startFrom = 0]) { | |
115 throw const NotImplementedException(); | |
116 } | |
117 | |
118 void removeRange(int start, int length) { | |
119 throw const NotImplementedException(); | |
120 } | |
121 | |
122 void insertRange(int start, int length, [initialValue = null]) { | |
123 throw const NotImplementedException(); | |
124 } | |
125 | |
126 List getRange(int start, int length) => | |
127 new _FrozenElementList._wrap(_Lists.getRange(this, start, length, | |
128 <Element>[])); | |
129 | |
130 int indexOf(Element element, [int start = 0]) { | |
131 return _Lists.indexOf(this, element, start, this.length); | |
132 } | |
133 | |
134 int lastIndexOf(Element element, [int start = null]) { | |
135 if (start === null) start = length - 1; | |
136 return _Lists.lastIndexOf(this, element, start); | |
137 } | |
138 | |
139 void clear() { | |
140 // It is unclear if we want to keep non element nodes? | |
141 _element.text = ''; | |
142 } | |
143 | |
144 Element removeLast() { | |
145 final last = this.last(); | |
146 if (last != null) { | |
147 _element._removeChild(last); | |
148 } | |
149 return last; | |
150 } | |
151 | |
152 Element last() { | |
153 return _element.lastElementChild; | |
154 } | |
155 } | |
156 | |
157 // TODO(jacobr): this is an inefficient implementation but it is hard to see | |
158 // a better option given that we cannot quite force NodeList to be an | |
159 // ElementList as there are valid cases where a NodeList JavaScript object | |
160 // contains Node objects that are not Elements. | |
161 class _FrozenElementList implements ElementList { | |
162 final List<Node> _nodeList; | |
163 | |
164 _FrozenElementList._wrap(this._nodeList); | |
165 | |
166 Element get first() { | |
167 return _nodeList[0]; | |
168 } | |
169 | |
170 void forEach(void f(Element element)) { | |
171 for (Element el in this) { | |
172 f(el); | |
173 } | |
174 } | |
175 | |
176 Collection map(f(Element element)) { | |
177 final out = []; | |
178 for (Element el in this) { | |
179 out.add(f(el)); | |
180 } | |
181 return out; | |
182 } | |
183 | |
184 ElementList filter(bool f(Element element)) { | |
185 final out = new _ElementList([]); | |
186 for (Element el in this) { | |
187 if (f(el)) out.add(el); | |
188 } | |
189 return out; | |
190 } | |
191 | |
192 bool every(bool f(Element element)) { | |
193 for(Element element in this) { | |
194 if (!f(element)) { | |
195 return false; | |
196 } | |
197 }; | |
198 return true; | |
199 } | |
200 | |
201 bool some(bool f(Element element)) { | |
202 for(Element element in this) { | |
203 if (f(element)) { | |
204 return true; | |
205 } | |
206 }; | |
207 return false; | |
208 } | |
209 | |
210 bool isEmpty() => _nodeList.isEmpty(); | |
211 | |
212 int get length() => _nodeList.length; | |
213 | |
214 Element operator [](int index) => _nodeList[index]; | |
215 | |
216 void operator []=(int index, Element value) { | |
217 throw const UnsupportedOperationException(''); | |
218 } | |
219 | |
220 void set length(int newLength) { | |
221 _nodeList.length = newLength; | |
222 } | |
223 | |
224 void add(Element value) { | |
225 throw const UnsupportedOperationException(''); | |
226 } | |
227 | |
228 void addLast(Element value) { | |
229 throw const UnsupportedOperationException(''); | |
230 } | |
231 | |
232 Iterator<Element> iterator() => new _FrozenElementListIterator(this); | |
233 | |
234 void addAll(Collection<Element> collection) { | |
235 throw const UnsupportedOperationException(''); | |
236 } | |
237 | |
238 void sort(int compare(Element a, Element b)) { | |
239 throw const UnsupportedOperationException(''); | |
240 } | |
241 | |
242 void setRange(int start, int length, List from, [int startFrom = 0]) { | |
243 throw const UnsupportedOperationException(''); | |
244 } | |
245 | |
246 void removeRange(int start, int length) { | |
247 throw const UnsupportedOperationException(''); | |
248 } | |
249 | |
250 void insertRange(int start, int length, [initialValue = null]) { | |
251 throw const UnsupportedOperationException(''); | |
252 } | |
253 | |
254 ElementList getRange(int start, int length) => | |
255 new _FrozenElementList._wrap(_nodeList.getRange(start, length)); | |
256 | |
257 int indexOf(Element element, [int start = 0]) => | |
258 _nodeList.indexOf(element, start); | |
259 | |
260 int lastIndexOf(Element element, [int start = null]) => | |
261 _nodeList.lastIndexOf(element, start); | |
262 | |
263 void clear() { | |
264 throw const UnsupportedOperationException(''); | |
265 } | |
266 | |
267 Element removeLast() { | |
268 throw const UnsupportedOperationException(''); | |
269 } | |
270 | |
271 Element last() => _nodeList.last(); | |
272 } | |
273 | |
274 class _FrozenElementListIterator implements Iterator<Element> { | |
275 final _FrozenElementList _list; | |
276 int _index = 0; | |
277 | |
278 _FrozenElementListIterator(this._list); | |
279 | |
280 /** | |
281 * Gets the next element in the iteration. Throws a | |
282 * [NoMoreElementsException] if no element is left. | |
283 */ | |
284 Element next() { | |
285 if (!hasNext()) { | |
286 throw const NoMoreElementsException(); | |
287 } | |
288 | |
289 return _list[_index++]; | |
290 } | |
291 | |
292 /** | |
293 * Returns whether the [Iterator] has elements left. | |
294 */ | |
295 bool hasNext() => _index < _list.length; | |
296 } | |
297 | |
298 class _ElementList extends _ListWrapper<Element> implements ElementList { | |
299 _ElementList(List<Element> list) : super(list); | |
300 | |
301 ElementList filter(bool f(Element element)) => | |
302 new _ElementList(super.filter(f)); | |
303 | |
304 ElementList getRange(int start, int length) => | |
305 new _ElementList(super.getRange(start, length)); | |
306 } | |
307 | |
308 class ElementAttributeMap implements Map<String, String> { | |
309 | |
310 final _ElementImpl _element; | |
311 | |
312 ElementAttributeMap._wrap(this._element); | |
313 | |
314 bool containsValue(String value) { | |
315 final attributes = _element._attributes; | |
316 for (int i = 0, len = attributes.length; i < len; i++) { | |
317 if(value == attributes[i].value) { | |
318 return true; | |
319 } | |
320 } | |
321 return false; | |
322 } | |
323 | |
324 bool containsKey(String key) { | |
325 return _element._hasAttribute(key); | |
326 } | |
327 | |
328 String operator [](String key) { | |
329 return _element._getAttribute(key); | |
330 } | |
331 | |
332 void operator []=(String key, String value) { | |
333 _element._setAttribute(key, value); | |
334 } | |
335 | |
336 String putIfAbsent(String key, String ifAbsent()) { | |
337 if (!containsKey(key)) { | |
338 this[key] = ifAbsent(); | |
339 } | |
340 } | |
341 | |
342 String remove(String key) { | |
343 _element._removeAttribute(key); | |
344 } | |
345 | |
346 void clear() { | |
347 final attributes = _element._attributes; | |
348 for (int i = attributes.length - 1; i >= 0; i--) { | |
349 remove(attributes[i].name); | |
350 } | |
351 } | |
352 | |
353 void forEach(void f(String key, String value)) { | |
354 final attributes = _element._attributes; | |
355 for (int i = 0, len = attributes.length; i < len; i++) { | |
356 final item = attributes[i]; | |
357 f(item.name, item.value); | |
358 } | |
359 } | |
360 | |
361 Collection<String> getKeys() { | |
362 // TODO(jacobr): generate a lazy collection instead. | |
363 final attributes = _element._attributes; | |
364 final keys = new List<String>(attributes.length); | |
365 for (int i = 0, len = attributes.length; i < len; i++) { | |
366 keys[i] = attributes[i].name; | |
367 } | |
368 return keys; | |
369 } | |
370 | |
371 Collection<String> getValues() { | |
372 // TODO(jacobr): generate a lazy collection instead. | |
373 final attributes = _element._attributes; | |
374 final values = new List<String>(attributes.length); | |
375 for (int i = 0, len = attributes.length; i < len; i++) { | |
376 values[i] = attributes[i].value; | |
377 } | |
378 return values; | |
379 } | |
380 | |
381 /** | |
382 * The number of {key, value} pairs in the map. | |
383 */ | |
384 int get length() { | |
385 return _element._attributes.length; | |
386 } | |
387 | |
388 /** | |
389 * Returns true if there is no {key, value} pair in the map. | |
390 */ | |
391 bool isEmpty() { | |
392 return length == 0; | |
393 } | |
394 } | |
395 | |
396 class _SimpleClientRect implements ClientRect { | |
397 final num left; | |
398 final num top; | |
399 final num width; | |
400 final num height; | |
401 num get right() => left + width; | |
402 num get bottom() => top + height; | |
403 | |
404 const _SimpleClientRect(this.left, this.top, this.width, this.height); | |
405 | |
406 bool operator ==(ClientRect other) { | |
407 return other !== null && left == other.left && top == other.top | |
408 && width == other.width && height == other.height; | |
409 } | |
410 | |
411 String toString() => "($left, $top, $width, $height)"; | |
412 } | |
413 | |
414 // TODO(jacobr): we cannot currently be lazy about calculating the client | |
415 // rects as we must perform all measurement queries at a safe point to avoid | |
416 // triggering unneeded layouts. | |
417 /** | |
418 * All your element measurement needs in one place | |
419 * @domName none | |
420 */ | |
421 class _ElementRectImpl implements ElementRect { | |
422 final ClientRect client; | |
423 final ClientRect offset; | |
424 final ClientRect scroll; | |
425 | |
426 // TODO(jacobr): should we move these outside of ElementRect to avoid the | |
427 // overhead of computing them every time even though they are rarely used. | |
428 final _ClientRectImpl _boundingClientRect; | |
429 final _ClientRectListImpl _clientRects; | |
430 | |
431 _ElementRectImpl(_ElementImpl element) : | |
432 client = new _SimpleClientRect(element._clientLeft, | |
433 element._clientTop, | |
434 element._clientWidth, | |
435 element._clientHeight), | |
436 offset = new _SimpleClientRect(element._offsetLeft, | |
437 element._offsetTop, | |
438 element._offsetWidth, | |
439 element._offsetHeight), | |
440 scroll = new _SimpleClientRect(element._scrollLeft, | |
441 element._scrollTop, | |
442 element._scrollWidth, | |
443 element._scrollHeight), | |
444 _boundingClientRect = element._getBoundingClientRect(), | |
445 _clientRects = element._getClientRects(); | |
446 | |
447 _ClientRectImpl get bounding() => _boundingClientRect; | |
448 | |
449 // TODO(jacobr): cleanup. | |
450 List<ClientRect> get clientRects() { | |
451 final out = new List(_clientRects.length); | |
452 for (num i = 0; i < _clientRects.length; i++) { | |
453 out[i] = _clientRects.item(i); | |
454 } | |
455 return out; | |
456 } | |
457 } | |
458 | |
459 class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | |
460 | |
461 // TODO(jacobr): caching these may hurt performance. | |
462 ElementAttributeMap _elementAttributeMap; | |
463 _CssClassSet _cssClassSet; | |
464 _DataAttributeMap _dataAttributes; | |
465 | |
466 /** | |
467 * @domName Element.hasAttribute, Element.getAttribute, Element.setAttribute, | |
468 * Element.removeAttribute | |
469 */ | |
470 Map<String, String> get attributes() { | |
471 if (_elementAttributeMap === null) { | |
472 _elementAttributeMap = new ElementAttributeMap._wrap(this); | |
473 } | |
474 return _elementAttributeMap; | |
475 } | |
476 | |
477 void set attributes(Map<String, String> value) { | |
478 Map<String, String> attributes = this.attributes; | |
479 attributes.clear(); | |
480 for (String key in value.getKeys()) { | |
481 attributes[key] = value[key]; | |
482 } | |
483 } | |
484 | |
485 void set elements(Collection<Element> value) { | |
486 final elements = this.elements; | |
487 elements.clear(); | |
488 elements.addAll(value); | |
489 } | |
490 | |
491 ElementList get elements() => new _ChildrenElementList._wrap(this); | |
492 | |
493 ElementList queryAll(String selectors) => | |
494 new _FrozenElementList._wrap(_querySelectorAll(selectors)); | |
495 | |
496 Set<String> get classes() { | |
497 if (_cssClassSet === null) { | |
498 _cssClassSet = new _CssClassSet(this); | |
499 } | |
500 return _cssClassSet; | |
501 } | |
502 | |
503 void set classes(Collection<String> value) { | |
504 _CssClassSet classSet = classes; | |
505 classSet.clear(); | |
506 classSet.addAll(value); | |
507 } | |
508 | |
509 Map<String, String> get dataAttributes() { | |
510 if (_dataAttributes === null) { | |
511 _dataAttributes = new _DataAttributeMap(attributes); | |
512 } | |
513 return _dataAttributes; | |
514 } | |
515 | |
516 void set dataAttributes(Map<String, String> value) { | |
517 Map<String, String> dataAttributes = this.dataAttributes; | |
518 dataAttributes.clear(); | |
519 for (String key in value.getKeys()) { | |
520 dataAttributes[key] = value[key]; | |
521 } | |
522 } | |
523 | |
524 Future<ElementRect> get rect() { | |
525 return _createMeasurementFuture( | |
526 () => new _ElementRectImpl(this), | |
527 new Completer<ElementRect>()); | |
528 } | |
529 | |
530 Future<CSSStyleDeclaration> get computedStyle() { | |
531 // TODO(jacobr): last param should be null, see b/5045788 | |
532 return getComputedStyle(''); | |
533 } | |
534 | |
535 Future<CSSStyleDeclaration> getComputedStyle(String pseudoElement) { | |
536 return _createMeasurementFuture( | |
537 () => _window._getComputedStyle(this, pseudoElement), | |
538 new Completer<CSSStyleDeclaration>()); | |
539 } | |
540 $!MEMBERS | |
541 } | |
OLD | NEW |