| 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.$dom_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.$dom_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.$dom_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.$dom_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.$dom_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.$dom_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 setRange(int start, int rangeLength, List from, [int startFrom = 0]) { | |
| 111 throw const NotImplementedException(); | |
| 112 } | |
| 113 | |
| 114 void removeRange(int start, int rangeLength) { | |
| 115 throw const NotImplementedException(); | |
| 116 } | |
| 117 | |
| 118 void insertRange(int start, int rangeLength, [initialValue = null]) { | |
| 119 throw const NotImplementedException(); | |
| 120 } | |
| 121 | |
| 122 List getRange(int start, int rangeLength) => | |
| 123 new _FrozenElementList._wrap(_Lists.getRange(this, start, rangeLength, | |
| 124 <Element>[])); | |
| 125 | |
| 126 int indexOf(Element element, [int start = 0]) { | |
| 127 return _Lists.indexOf(this, element, start, this.length); | |
| 128 } | |
| 129 | |
| 130 int lastIndexOf(Element element, [int start = null]) { | |
| 131 if (start === null) start = length - 1; | |
| 132 return _Lists.lastIndexOf(this, element, start); | |
| 133 } | |
| 134 | |
| 135 void clear() { | |
| 136 // It is unclear if we want to keep non element nodes? | |
| 137 _element.text = ''; | |
| 138 } | |
| 139 | |
| 140 Element removeLast() { | |
| 141 final result = this.last(); | |
| 142 if (result != null) { | |
| 143 _element.$dom_removeChild(result); | |
| 144 } | |
| 145 return result; | |
| 146 } | |
| 147 | |
| 148 Element last() { | |
| 149 return _element.$dom_lastElementChild; | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 // TODO(jacobr): this is an inefficient implementation but it is hard to see | |
| 154 // a better option given that we cannot quite force NodeList to be an | |
| 155 // ElementList as there are valid cases where a NodeList JavaScript object | |
| 156 // contains Node objects that are not Elements. | |
| 157 class _FrozenElementList implements ElementList { | |
| 158 final List<Node> _nodeList; | |
| 159 | |
| 160 _FrozenElementList._wrap(this._nodeList); | |
| 161 | |
| 162 Element get first() { | |
| 163 return _nodeList[0]; | |
| 164 } | |
| 165 | |
| 166 void forEach(void f(Element element)) { | |
| 167 for (Element el in this) { | |
| 168 f(el); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 Collection map(f(Element element)) { | |
| 173 final out = []; | |
| 174 for (Element el in this) { | |
| 175 out.add(f(el)); | |
| 176 } | |
| 177 return out; | |
| 178 } | |
| 179 | |
| 180 ElementList filter(bool f(Element element)) { | |
| 181 final out = new _ElementList([]); | |
| 182 for (Element el in this) { | |
| 183 if (f(el)) out.add(el); | |
| 184 } | |
| 185 return out; | |
| 186 } | |
| 187 | |
| 188 bool every(bool f(Element element)) { | |
| 189 for(Element element in this) { | |
| 190 if (!f(element)) { | |
| 191 return false; | |
| 192 } | |
| 193 }; | |
| 194 return true; | |
| 195 } | |
| 196 | |
| 197 bool some(bool f(Element element)) { | |
| 198 for(Element element in this) { | |
| 199 if (f(element)) { | |
| 200 return true; | |
| 201 } | |
| 202 }; | |
| 203 return false; | |
| 204 } | |
| 205 | |
| 206 bool isEmpty() => _nodeList.isEmpty(); | |
| 207 | |
| 208 int get length() => _nodeList.length; | |
| 209 | |
| 210 Element operator [](int index) => _nodeList[index]; | |
| 211 | |
| 212 void operator []=(int index, Element value) { | |
| 213 throw const UnsupportedOperationException(''); | |
| 214 } | |
| 215 | |
| 216 void set length(int newLength) { | |
| 217 _nodeList.length = newLength; | |
| 218 } | |
| 219 | |
| 220 void add(Element value) { | |
| 221 throw const UnsupportedOperationException(''); | |
| 222 } | |
| 223 | |
| 224 void addLast(Element value) { | |
| 225 throw const UnsupportedOperationException(''); | |
| 226 } | |
| 227 | |
| 228 Iterator<Element> iterator() => new _FrozenElementListIterator(this); | |
| 229 | |
| 230 void addAll(Collection<Element> collection) { | |
| 231 throw const UnsupportedOperationException(''); | |
| 232 } | |
| 233 | |
| 234 void sort(int compare(Element a, Element b)) { | |
| 235 throw const UnsupportedOperationException(''); | |
| 236 } | |
| 237 | |
| 238 void setRange(int start, int rangeLength, List from, [int startFrom = 0]) { | |
| 239 throw const UnsupportedOperationException(''); | |
| 240 } | |
| 241 | |
| 242 void removeRange(int start, int rangeLength) { | |
| 243 throw const UnsupportedOperationException(''); | |
| 244 } | |
| 245 | |
| 246 void insertRange(int start, int rangeLength, [initialValue = null]) { | |
| 247 throw const UnsupportedOperationException(''); | |
| 248 } | |
| 249 | |
| 250 ElementList getRange(int start, int rangeLength) => | |
| 251 new _FrozenElementList._wrap(_nodeList.getRange(start, rangeLength)); | |
| 252 | |
| 253 int indexOf(Element element, [int start = 0]) => | |
| 254 _nodeList.indexOf(element, start); | |
| 255 | |
| 256 int lastIndexOf(Element element, [int start = null]) => | |
| 257 _nodeList.lastIndexOf(element, start); | |
| 258 | |
| 259 void clear() { | |
| 260 throw const UnsupportedOperationException(''); | |
| 261 } | |
| 262 | |
| 263 Element removeLast() { | |
| 264 throw const UnsupportedOperationException(''); | |
| 265 } | |
| 266 | |
| 267 Element last() => _nodeList.last(); | |
| 268 } | |
| 269 | |
| 270 class _FrozenElementListIterator implements Iterator<Element> { | |
| 271 final _FrozenElementList _list; | |
| 272 int _index = 0; | |
| 273 | |
| 274 _FrozenElementListIterator(this._list); | |
| 275 | |
| 276 /** | |
| 277 * Gets the next element in the iteration. Throws a | |
| 278 * [NoMoreElementsException] if no element is left. | |
| 279 */ | |
| 280 Element next() { | |
| 281 if (!hasNext()) { | |
| 282 throw const NoMoreElementsException(); | |
| 283 } | |
| 284 | |
| 285 return _list[_index++]; | |
| 286 } | |
| 287 | |
| 288 /** | |
| 289 * Returns whether the [Iterator] has elements left. | |
| 290 */ | |
| 291 bool hasNext() => _index < _list.length; | |
| 292 } | |
| 293 | |
| 294 class _ElementList extends _ListWrapper<Element> implements ElementList { | |
| 295 _ElementList(List<Element> list) : super(list); | |
| 296 | |
| 297 ElementList filter(bool f(Element element)) => | |
| 298 new _ElementList(super.filter(f)); | |
| 299 | |
| 300 ElementList getRange(int start, int rangeLength) => | |
| 301 new _ElementList(super.getRange(start, rangeLength)); | |
| 302 } | |
| 303 | |
| 304 class _ElementAttributeMap implements AttributeMap { | |
| 305 | |
| 306 final _ElementImpl _element; | |
| 307 | |
| 308 _ElementAttributeMap(this._element); | |
| 309 | |
| 310 bool containsValue(String value) { | |
| 311 final attributes = _element.$dom_attributes; | |
| 312 for (int i = 0, len = attributes.length; i < len; i++) { | |
| 313 if(value == attributes[i].value) { | |
| 314 return true; | |
| 315 } | |
| 316 } | |
| 317 return false; | |
| 318 } | |
| 319 | |
| 320 bool containsKey(String key) { | |
| 321 return _element.$dom_hasAttribute(key); | |
| 322 } | |
| 323 | |
| 324 String operator [](String key) { | |
| 325 return _element.$dom_getAttribute(key); | |
| 326 } | |
| 327 | |
| 328 void operator []=(String key, value) { | |
| 329 _element.$dom_setAttribute(key, '$value'); | |
| 330 } | |
| 331 | |
| 332 String putIfAbsent(String key, String ifAbsent()) { | |
| 333 if (!containsKey(key)) { | |
| 334 this[key] = ifAbsent(); | |
| 335 } | |
| 336 return this[key]; | |
| 337 } | |
| 338 | |
| 339 String remove(String key) { | |
| 340 String value = _element.$dom_getAttribute(key); | |
| 341 _element.$dom_removeAttribute(key); | |
| 342 return value; | |
| 343 } | |
| 344 | |
| 345 void clear() { | |
| 346 final attributes = _element.$dom_attributes; | |
| 347 for (int i = attributes.length - 1; i >= 0; i--) { | |
| 348 remove(attributes[i].name); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 void forEach(void f(String key, String value)) { | |
| 353 final attributes = _element.$dom_attributes; | |
| 354 for (int i = 0, len = attributes.length; i < len; i++) { | |
| 355 final item = attributes[i]; | |
| 356 f(item.name, item.value); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 Collection<String> getKeys() { | |
| 361 // TODO(jacobr): generate a lazy collection instead. | |
| 362 final attributes = _element.$dom_attributes; | |
| 363 final keys = new List<String>(attributes.length); | |
| 364 for (int i = 0, len = attributes.length; i < len; i++) { | |
| 365 keys[i] = attributes[i].name; | |
| 366 } | |
| 367 return keys; | |
| 368 } | |
| 369 | |
| 370 Collection<String> getValues() { | |
| 371 // TODO(jacobr): generate a lazy collection instead. | |
| 372 final attributes = _element.$dom_attributes; | |
| 373 final values = new List<String>(attributes.length); | |
| 374 for (int i = 0, len = attributes.length; i < len; i++) { | |
| 375 values[i] = attributes[i].value; | |
| 376 } | |
| 377 return values; | |
| 378 } | |
| 379 | |
| 380 /** | |
| 381 * The number of {key, value} pairs in the map. | |
| 382 */ | |
| 383 int get length() { | |
| 384 return _element.$dom_attributes.length; | |
| 385 } | |
| 386 | |
| 387 /** | |
| 388 * Returns true if there is no {key, value} pair in the map. | |
| 389 */ | |
| 390 bool isEmpty() { | |
| 391 return length == 0; | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 /** | |
| 396 * Provides a Map abstraction on top of data-* attributes, similar to the | |
| 397 * dataSet in the old DOM. | |
| 398 */ | |
| 399 class _DataAttributeMap implements AttributeMap { | |
| 400 | |
| 401 final Map<String, String> $dom_attributes; | |
| 402 | |
| 403 _DataAttributeMap(this.$dom_attributes); | |
| 404 | |
| 405 // interface Map | |
| 406 | |
| 407 // TODO: Use lazy iterator when it is available on Map. | |
| 408 bool containsValue(String value) => getValues().some((v) => v == value); | |
| 409 | |
| 410 bool containsKey(String key) => $dom_attributes.containsKey(_attr(key)); | |
| 411 | |
| 412 String operator [](String key) => $dom_attributes[_attr(key)]; | |
| 413 | |
| 414 void operator []=(String key, value) { | |
| 415 $dom_attributes[_attr(key)] = '$value'; | |
| 416 } | |
| 417 | |
| 418 String putIfAbsent(String key, String ifAbsent()) => | |
| 419 $dom_attributes.putIfAbsent(_attr(key), ifAbsent); | |
| 420 | |
| 421 String remove(String key) => $dom_attributes.remove(_attr(key)); | |
| 422 | |
| 423 void clear() { | |
| 424 // Needs to operate on a snapshot since we are mutating the collection. | |
| 425 for (String key in getKeys()) { | |
| 426 remove(key); | |
| 427 } | |
| 428 } | |
| 429 | |
| 430 void forEach(void f(String key, String value)) { | |
| 431 $dom_attributes.forEach((String key, String value) { | |
| 432 if (_matches(key)) { | |
| 433 f(_strip(key), value); | |
| 434 } | |
| 435 }); | |
| 436 } | |
| 437 | |
| 438 Collection<String> getKeys() { | |
| 439 final keys = new List<String>(); | |
| 440 $dom_attributes.forEach((String key, String value) { | |
| 441 if (_matches(key)) { | |
| 442 keys.add(_strip(key)); | |
| 443 } | |
| 444 }); | |
| 445 return keys; | |
| 446 } | |
| 447 | |
| 448 Collection<String> getValues() { | |
| 449 final values = new List<String>(); | |
| 450 $dom_attributes.forEach((String key, String value) { | |
| 451 if (_matches(key)) { | |
| 452 values.add(value); | |
| 453 } | |
| 454 }); | |
| 455 return values; | |
| 456 } | |
| 457 | |
| 458 int get length() => getKeys().length; | |
| 459 | |
| 460 // TODO: Use lazy iterator when it is available on Map. | |
| 461 bool isEmpty() => length == 0; | |
| 462 | |
| 463 // Helpers. | |
| 464 String _attr(String key) => 'data-$key'; | |
| 465 bool _matches(String key) => key.startsWith('data-'); | |
| 466 String _strip(String key) => key.substring(5); | |
| 467 } | |
| 468 | |
| 469 class _CssClassSet implements CSSClassSet { | |
| 470 | |
| 471 final _ElementImpl _element; | |
| 472 | |
| 473 _CssClassSet(this._element); | |
| 474 | |
| 475 String toString() => _formatSet(_read()); | |
| 476 | |
| 477 // interface Iterable - BEGIN | |
| 478 Iterator<String> iterator() => _read().iterator(); | |
| 479 // interface Iterable - END | |
| 480 | |
| 481 // interface Collection - BEGIN | |
| 482 void forEach(void f(String element)) { | |
| 483 _read().forEach(f); | |
| 484 } | |
| 485 | |
| 486 Collection map(f(String element)) => _read().map(f); | |
| 487 | |
| 488 Collection<String> filter(bool f(String element)) => _read().filter(f); | |
| 489 | |
| 490 bool every(bool f(String element)) => _read().every(f); | |
| 491 | |
| 492 bool some(bool f(String element)) => _read().some(f); | |
| 493 | |
| 494 bool isEmpty() => _read().isEmpty(); | |
| 495 | |
| 496 bool get isFrozen() => false; | |
| 497 | |
| 498 int get length() =>_read().length; | |
| 499 | |
| 500 // interface Collection - END | |
| 501 | |
| 502 // interface Set - BEGIN | |
| 503 bool contains(String value) => _read().contains(value); | |
| 504 | |
| 505 void add(String value) { | |
| 506 // TODO - figure out if we need to do any validation here | |
| 507 // or if the browser natively does enough | |
| 508 _modify((s) => s.add(value)); | |
| 509 } | |
| 510 | |
| 511 bool remove(String value) { | |
| 512 Set<String> s = _read(); | |
| 513 bool result = s.remove(value); | |
| 514 _write(s); | |
| 515 return result; | |
| 516 } | |
| 517 | |
| 518 bool toggle(String value) { | |
| 519 Set<String> s = _read(); | |
| 520 bool result = false; | |
| 521 if (s.contains(value)) { | |
| 522 s.remove(value); | |
| 523 } else { | |
| 524 s.add(value); | |
| 525 result = true; | |
| 526 } | |
| 527 _write(s); | |
| 528 return result; | |
| 529 } | |
| 530 | |
| 531 void addAll(Collection<String> collection) { | |
| 532 // TODO - see comment above about validation | |
| 533 _modify((s) => s.addAll(collection)); | |
| 534 } | |
| 535 | |
| 536 void removeAll(Collection<String> collection) { | |
| 537 _modify((s) => s.removeAll(collection)); | |
| 538 } | |
| 539 | |
| 540 bool isSubsetOf(Collection<String> collection) => | |
| 541 _read().isSubsetOf(collection); | |
| 542 | |
| 543 bool containsAll(Collection<String> collection) => | |
| 544 _read().containsAll(collection); | |
| 545 | |
| 546 Set<String> intersection(Collection<String> other) => | |
| 547 _read().intersection(other); | |
| 548 | |
| 549 void clear() { | |
| 550 _modify((s) => s.clear()); | |
| 551 } | |
| 552 // interface Set - END | |
| 553 | |
| 554 /** | |
| 555 * Helper method used to modify the set of css classes on this element. | |
| 556 * | |
| 557 * f - callback with: | |
| 558 * s - a Set of all the css class name currently on this element. | |
| 559 * | |
| 560 * After f returns, the modified set is written to the | |
| 561 * className property of this element. | |
| 562 */ | |
| 563 void _modify( f(Set<String> s)) { | |
| 564 Set<String> s = _read(); | |
| 565 f(s); | |
| 566 _write(s); | |
| 567 } | |
| 568 | |
| 569 /** | |
| 570 * Read the class names from the Element class property, | |
| 571 * and put them into a set (duplicates are discarded). | |
| 572 */ | |
| 573 Set<String> _read() { | |
| 574 // TODO(mattsh) simplify this once split can take regex. | |
| 575 Set<String> s = new Set<String>(); | |
| 576 for (String name in _classname().split(' ')) { | |
| 577 String trimmed = name.trim(); | |
| 578 if (!trimmed.isEmpty()) { | |
| 579 s.add(trimmed); | |
| 580 } | |
| 581 } | |
| 582 return s; | |
| 583 } | |
| 584 | |
| 585 /** | |
| 586 * Read the class names as a space-separated string. This is meant to be | |
| 587 * overridden by subclasses. | |
| 588 */ | |
| 589 String _classname() => _element.$dom_className; | |
| 590 | |
| 591 /** | |
| 592 * Join all the elements of a set into one string and write | |
| 593 * back to the element. | |
| 594 */ | |
| 595 void _write(Set s) { | |
| 596 _element.$dom_className = _formatSet(s); | |
| 597 } | |
| 598 | |
| 599 String _formatSet(Set<String> s) { | |
| 600 // TODO(mattsh) should be able to pass Set to String.joins http:/b/5398605 | |
| 601 List list = new List.from(s); | |
| 602 return Strings.join(list, ' '); | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 class _SimpleClientRect implements ClientRect { | |
| 607 final num left; | |
| 608 final num top; | |
| 609 final num width; | |
| 610 final num height; | |
| 611 num get right() => left + width; | |
| 612 num get bottom() => top + height; | |
| 613 | |
| 614 const _SimpleClientRect(this.left, this.top, this.width, this.height); | |
| 615 | |
| 616 bool operator ==(ClientRect other) { | |
| 617 return other !== null && left == other.left && top == other.top | |
| 618 && width == other.width && height == other.height; | |
| 619 } | |
| 620 | |
| 621 String toString() => "($left, $top, $width, $height)"; | |
| 622 } | |
| 623 | |
| 624 // TODO(jacobr): we cannot currently be lazy about calculating the client | |
| 625 // rects as we must perform all measurement queries at a safe point to avoid | |
| 626 // triggering unneeded layouts. | |
| 627 /** | |
| 628 * All your element measurement needs in one place | |
| 629 * @domName none | |
| 630 */ | |
| 631 class _ElementRectImpl implements ElementRect { | |
| 632 final ClientRect client; | |
| 633 final ClientRect offset; | |
| 634 final ClientRect scroll; | |
| 635 | |
| 636 // TODO(jacobr): should we move these outside of ElementRect to avoid the | |
| 637 // overhead of computing them every time even though they are rarely used. | |
| 638 final _ClientRectImpl _boundingClientRect; | |
| 639 final _ClientRectListImpl _clientRects; | |
| 640 | |
| 641 _ElementRectImpl(_ElementImpl element) : | |
| 642 client = new _SimpleClientRect(element.$dom_clientLeft, | |
| 643 element.$dom_clientTop, | |
| 644 element.$dom_clientWidth, | |
| 645 element.$dom_clientHeight), | |
| 646 offset = new _SimpleClientRect(element.$dom_offsetLeft, | |
| 647 element.$dom_offsetTop, | |
| 648 element.$dom_offsetWidth, | |
| 649 element.$dom_offsetHeight), | |
| 650 scroll = new _SimpleClientRect(element.$dom_scrollLeft, | |
| 651 element.$dom_scrollTop, | |
| 652 element.$dom_scrollWidth, | |
| 653 element.$dom_scrollHeight), | |
| 654 _boundingClientRect = element.$dom_getBoundingClientRect(), | |
| 655 _clientRects = element.$dom_getClientRects(); | |
| 656 | |
| 657 _ClientRectImpl get bounding() => _boundingClientRect; | |
| 658 | |
| 659 // TODO(jacobr): cleanup. | |
| 660 List<ClientRect> get clientRects() { | |
| 661 final out = new List(_clientRects.length); | |
| 662 for (num i = 0; i < _clientRects.length; i++) { | |
| 663 out[i] = _clientRects.item(i); | |
| 664 } | |
| 665 return out; | |
| 666 } | |
| 667 } | |
| 668 | |
| 669 class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | |
| 670 | |
| 671 /** | |
| 672 * @domName Element.hasAttribute, Element.getAttribute, Element.setAttribute, | |
| 673 * Element.removeAttribute | |
| 674 */ | |
| 675 _ElementAttributeMap get attributes() => new _ElementAttributeMap(this); | |
| 676 | |
| 677 void set attributes(Map<String, String> value) { | |
| 678 Map<String, String> attributes = this.attributes; | |
| 679 attributes.clear(); | |
| 680 for (String key in value.getKeys()) { | |
| 681 attributes[key] = value[key]; | |
| 682 } | |
| 683 } | |
| 684 | |
| 685 void set elements(Collection<Element> value) { | |
| 686 final elements = this.elements; | |
| 687 elements.clear(); | |
| 688 elements.addAll(value); | |
| 689 } | |
| 690 | |
| 691 ElementList get elements() => new _ChildrenElementList._wrap(this); | |
| 692 | |
| 693 _ElementImpl query(String selectors) => $dom_querySelector(selectors); | |
| 694 | |
| 695 List<Element> queryAll(String selectors) => | |
| 696 new _FrozenElementList._wrap($dom_querySelectorAll(selectors)); | |
| 697 | |
| 698 _CssClassSet get classes() => new _CssClassSet(this); | |
| 699 | |
| 700 void set classes(Collection<String> value) { | |
| 701 _CssClassSet classSet = classes; | |
| 702 classSet.clear(); | |
| 703 classSet.addAll(value); | |
| 704 } | |
| 705 | |
| 706 Map<String, String> get dataAttributes() => | |
| 707 new _DataAttributeMap(attributes); | |
| 708 | |
| 709 void set dataAttributes(Map<String, String> value) { | |
| 710 final dataAttributes = this.dataAttributes; | |
| 711 dataAttributes.clear(); | |
| 712 for (String key in value.getKeys()) { | |
| 713 dataAttributes[key] = value[key]; | |
| 714 } | |
| 715 } | |
| 716 | |
| 717 Future<ElementRect> get rect() { | |
| 718 return _createMeasurementFuture( | |
| 719 () => new _ElementRectImpl(this), | |
| 720 new Completer<ElementRect>()); | |
| 721 } | |
| 722 | |
| 723 Future<CSSStyleDeclaration> get computedStyle() { | |
| 724 // TODO(jacobr): last param should be null, see b/5045788 | |
| 725 return getComputedStyle(''); | |
| 726 } | |
| 727 | |
| 728 Future<CSSStyleDeclaration> getComputedStyle(String pseudoElement) { | |
| 729 return _createMeasurementFuture( | |
| 730 () => _window.$dom_getComputedStyle(this, pseudoElement), | |
| 731 new Completer<CSSStyleDeclaration>()); | |
| 732 } | |
| 733 | |
| 734 void addText(String text) { | |
| 735 this.insertAdjacentText('beforeend', text); | |
| 736 } | |
| 737 | |
| 738 void addHTML(String text) { | |
| 739 this.insertAdjacentHTML('beforeend', text); | |
| 740 } | |
| 741 | |
| 742 // Hooks to support custom WebComponents. | |
| 743 var xtag; | |
| 744 | |
| 745 $if DARTIUM | |
| 746 noSuchMethod(String name, List args) { | |
| 747 if (dynamicUnknownElementDispatcher == null) { | |
| 748 throw new NoSuchMethodException(this, name, args); | |
| 749 } else { | |
| 750 return dynamicUnknownElementDispatcher(this, name, args); | |
| 751 } | |
| 752 } | |
| 753 $else | |
| 754 // TODO(vsm): Implement noSuchMethod or similar for dart2js. | |
| 755 $endif | |
| 756 | |
| 757 $if DART2JS | |
| 758 /** @domName Element.insertAdjacentText */ | |
| 759 void insertAdjacentText(String where, String text) { | |
| 760 if (JS('bool', '!!this.insertAdjacentText')) { | |
| 761 _insertAdjacentText(where, text); | |
| 762 } else { | |
| 763 _insertAdjacentNode(where, new Text(text)); | |
| 764 } | |
| 765 } | |
| 766 | |
| 767 void _insertAdjacentText(String where, String text) | |
| 768 native 'insertAdjacentText'; | |
| 769 | |
| 770 /** @domName Element.insertAdjacentHTML */ | |
| 771 void insertAdjacentHTML(String where, String text) { | |
| 772 if (JS('bool', '!!this.insertAdjacentHTML')) { | |
| 773 _insertAdjacentHTML(where, text); | |
| 774 } else { | |
| 775 _insertAdjacentNode(where, new DocumentFragment.html(text)); | |
| 776 } | |
| 777 } | |
| 778 | |
| 779 void _insertAdjacentHTML(String where, String text) | |
| 780 native 'insertAdjacentHTML'; | |
| 781 | |
| 782 /** @domName Element.insertAdjacentHTML */ | |
| 783 Element insertAdjacentElement(String where, Element element) { | |
| 784 if (JS('bool', '!!this.insertAdjacentElement')) { | |
| 785 _insertAdjacentElement(where, element); | |
| 786 } else { | |
| 787 _insertAdjacentNode(where, element); | |
| 788 } | |
| 789 return element; | |
| 790 } | |
| 791 | |
| 792 void _insertAdjacentElement(String where, Element element) | |
| 793 native 'insertAdjacentElement'; | |
| 794 | |
| 795 void _insertAdjacentNode(String where, Node node) { | |
| 796 switch (where.toLowerCase()) { | |
| 797 case 'beforebegin': | |
| 798 this.parent.insertBefore(node, this); | |
| 799 break; | |
| 800 case 'afterbegin': | |
| 801 this.insertBefore(node, this.nodes.first); | |
| 802 break; | |
| 803 case 'beforeend': | |
| 804 this.nodes.add(node); | |
| 805 break; | |
| 806 case 'afterend': | |
| 807 this.parent.insertBefore(node, this.nextNode); | |
| 808 break; | |
| 809 default: | |
| 810 throw new IllegalArgumentException("Invalid position ${where}"); | |
| 811 } | |
| 812 } | |
| 813 $else | |
| 814 $endif | |
| 815 | |
| 816 $!MEMBERS | |
| 817 } | |
| 818 | |
| 819 // Temporary dispatch hook to support WebComponents. | |
| 820 Function dynamicUnknownElementDispatcher; | |
| 821 | |
| 822 final _START_TAG_REGEXP = const RegExp('<(\\w+)'); | |
| 823 class _ElementFactoryProvider { | |
| 824 static final _CUSTOM_PARENT_TAG_MAP = const { | |
| 825 'body' : 'html', | |
| 826 'head' : 'html', | |
| 827 'caption' : 'table', | |
| 828 'td': 'tr', | |
| 829 'colgroup': 'table', | |
| 830 'col' : 'colgroup', | |
| 831 'tr' : 'tbody', | |
| 832 'tbody' : 'table', | |
| 833 'tfoot' : 'table', | |
| 834 'thead' : 'table', | |
| 835 'track' : 'audio', | |
| 836 }; | |
| 837 | |
| 838 /** @domName Document.createElement */ | |
| 839 factory Element.html(String html) { | |
| 840 // TODO(jacobr): this method can be made more robust and performant. | |
| 841 // 1) Cache the dummy parent elements required to use innerHTML rather than | |
| 842 // creating them every call. | |
| 843 // 2) Verify that the html does not contain leading or trailing text nodes. | |
| 844 // 3) Verify that the html does not contain both <head> and <body> tags. | |
| 845 // 4) Detatch the created element from its dummy parent. | |
| 846 String parentTag = 'div'; | |
| 847 String tag; | |
| 848 final match = _START_TAG_REGEXP.firstMatch(html); | |
| 849 if (match !== null) { | |
| 850 tag = match.group(1).toLowerCase(); | |
| 851 if (_CUSTOM_PARENT_TAG_MAP.containsKey(tag)) { | |
| 852 parentTag = _CUSTOM_PARENT_TAG_MAP[tag]; | |
| 853 } | |
| 854 } | |
| 855 final _ElementImpl temp = new Element.tag(parentTag); | |
| 856 temp.innerHTML = html; | |
| 857 | |
| 858 Element element; | |
| 859 if (temp.elements.length == 1) { | |
| 860 element = temp.elements.first; | |
| 861 } else if (parentTag == 'html' && temp.elements.length == 2) { | |
| 862 // Work around for edge case in WebKit and possibly other browsers where | |
| 863 // both body and head elements are created even though the inner html | |
| 864 // only contains a head or body element. | |
| 865 element = temp.elements[tag == 'head' ? 0 : 1]; | |
| 866 } else { | |
| 867 throw new IllegalArgumentException('HTML had ${temp.elements.length} ' | |
| 868 'top level elements but 1 expected'); | |
| 869 } | |
| 870 element.remove(); | |
| 871 return element; | |
| 872 } | |
| 873 | |
| 874 /** @domName Document.createElement */ | |
| 875 $if DART2JS | |
| 876 // Optimization to improve performance until the dart2js compiler inlines this | |
| 877 // method. | |
| 878 factory Element.tag(String tag) native "return document.createElement(tag)"; | |
| 879 $else | |
| 880 factory Element.tag(String tag) => _document.$dom_createElement(tag); | |
| 881 $endif | |
| 882 } | |
| OLD | NEW |