| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of $LIBRARYNAME; |
| |
| class _ChildrenElementList extends ListBase<Element> |
| implements NodeListWrapper { |
| // Raw Element. |
| final Element _element; |
| final HtmlCollection _childElements; |
| |
| _ChildrenElementList._wrap(Element element) |
| : _childElements = element._children, |
| _element = element; |
| |
| bool contains(Object element) => _childElements.contains(element); |
| |
| |
| bool get isEmpty { |
| return _element._firstElementChild == null; |
| } |
| |
| int get length { |
| return _childElements.length; |
| } |
| |
| Element operator [](int index) { |
| return _childElements[index]; |
| } |
| |
| void operator []=(int index, Element value) { |
| _element._replaceChild(value, _childElements[index]); |
| } |
| |
| void set length(int newLength) { |
| // TODO(jacobr): remove children when length is reduced. |
| throw new UnsupportedError('Cannot resize element lists'); |
| } |
| |
| Element add(Element value) { |
| _element.append(value); |
| return value; |
| } |
| |
| Iterator<Element> get iterator => toList().iterator; |
| |
| void addAll(Iterable<Element> iterable) { |
| if (iterable is _ChildNodeListLazy) { |
| iterable = new List.from(iterable); |
| } |
| |
| for (Element element in iterable) { |
| _element.append(element); |
| } |
| } |
| |
| void sort([int compare(Element a, Element b)]) { |
| throw new UnsupportedError('Cannot sort element lists'); |
| } |
| |
| void shuffle([Random random]) { |
| throw new UnsupportedError('Cannot shuffle element lists'); |
| } |
| |
| void removeWhere(bool test(Element element)) { |
| _filter(test, false); |
| } |
| |
| void retainWhere(bool test(Element element)) { |
| _filter(test, true); |
| } |
| |
| void _filter(bool test(var element), bool retainMatching) { |
| var removed; |
| if (retainMatching) { |
| removed = _element.children.where((e) => !test(e)); |
| } else { |
| removed = _element.children.where(test); |
| } |
| for (var e in removed) e.remove(); |
| } |
| |
| void setRange(int start, int end, Iterable<Element> iterable, |
| [int skipCount = 0]) { |
| throw new UnimplementedError(); |
| } |
| |
| void replaceRange(int start, int end, Iterable<Element> iterable) { |
| throw new UnimplementedError(); |
| } |
| |
| void fillRange(int start, int end, [Element fillValue]) { |
| throw new UnimplementedError(); |
| } |
| |
| bool remove(Object object) { |
| if (object is Element) { |
| Element element = object; |
| if (identical(element.parentNode, _element)) { |
| _element._removeChild(element); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void insert(int index, Element element) { |
| if (index < 0 || index > length) { |
| throw new RangeError.range(index, 0, length); |
| } |
| if (index == length) { |
| _element.append(element); |
| } else { |
| _element.insertBefore(element, this[index]); |
| } |
| } |
| |
| void setAll(int index, Iterable<Element> iterable) { |
| throw new UnimplementedError(); |
| } |
| |
| void clear() { |
| _element._clearChildren(); |
| } |
| |
| Element removeAt(int index) { |
| final result = this[index]; |
| if (result != null) { |
| _element._removeChild(result); |
| } |
| return result; |
| } |
| |
| Element removeLast() { |
| final result = this.last; |
| if (result != null) { |
| _element._removeChild(result); |
| } |
| return result; |
| } |
| |
| Element get first { |
| Element result = _element._firstElementChild; |
| if (result == null) throw new StateError("No elements"); |
| return result; |
| } |
| |
| |
| Element get last { |
| Element result = _element._lastElementChild; |
| if (result == null) throw new StateError("No elements"); |
| return result; |
| } |
| |
| Element get single { |
| if (length > 1) throw new StateError("More than one element"); |
| return first; |
| } |
| |
| List<Node> get rawList => _childElements; |
| } |
| |
| /** |
| * An immutable list containing HTML elements. This list contains some |
| * additional methods when compared to regular lists for ease of CSS |
| * manipulation on a group of elements. |
| */ |
| abstract class ElementList<T extends Element> extends ListBase<T> { |
| /** |
| * The union of all CSS classes applied to the elements in this list. |
| * |
| * This set makes it easy to add, remove or toggle (add if not present, remove |
| * if present) the classes applied to a collection of elements. |
| * |
| * htmlList.classes.add('selected'); |
| * htmlList.classes.toggle('isOnline'); |
| * htmlList.classes.remove('selected'); |
| */ |
| CssClassSet get classes; |
| |
| /** Replace the classes with `value` for every element in this list. */ |
| set classes(Iterable<String> value); |
| |
| /** |
| * Access the union of all [CssStyleDeclaration]s that are associated with an |
| * [ElementList]. |
| * |
| * Grouping the style objects all together provides easy editing of specific |
| * properties of a collection of elements. Setting a specific property value |
| * will set that property in all [Element]s in the [ElementList]. Getting a |
| * specific property value will return the value of the property of the first |
| * element in the [ElementList]. |
| */ |
| CssStyleDeclarationBase get style; |
| |
| /** |
| * Access dimensions and position of the Elements in this list. |
| * |
| * Setting the height or width properties will set the height or width |
| * property for all elements in the list. This returns a rectangle with the |
| * dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Getting the height or width returns the height or width of the |
| * first Element in this list. |
| * |
| * Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. |
| */ |
| @Experimental() |
| CssRect get contentEdge; |
| |
| /** |
| * Access dimensions and position of the first Element's content + padding box |
| * in this list. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's `innerHeight` value for an element. This |
| * is also a rectangle equalling the dimensions of clientHeight and |
| * clientWidth. |
| */ |
| @Experimental() |
| CssRect get paddingEdge; |
| |
| /** |
| * Access dimensions and position of the first Element's content + padding + |
| * border box in this list. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's `outerHeight` value for an element. |
| */ |
| @Experimental() |
| CssRect get borderEdge; |
| |
| /** |
| * Access dimensions and position of the first Element's content + padding + |
| * border + margin box in this list. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's `outerHeight` value for an element. |
| */ |
| @Experimental() |
| CssRect get marginEdge; |
| $!STREAM_GETTER_SIGNATURES |
| } |
| |
| // TODO(jacobr): this is an inefficient implementation but it is hard to see |
| // a better option given that we cannot quite force NodeList to be an |
| // ElementList as there are valid cases where a NodeList JavaScript object |
| // contains Node objects that are not Elements. |
| class _FrozenElementList<T extends Element> extends ListBase<T> |
| implements ElementList<T>, NodeListWrapper { |
| final List<Node> _nodeList; |
| // The subset of _nodeList that are Elements. |
| List<Element> _elementList; |
| |
| _FrozenElementList._wrap(this._nodeList) { |
| _elementList = _nodeList.where((e) => e is Element).toList(); |
| } |
| |
| int get length => _nodeList.length; |
| |
| Element operator [](int index) => _nodeList[index]; |
| |
| void operator []=(int index, Element value) { |
| throw new UnsupportedError('Cannot modify list'); |
| } |
| |
| void set length(int newLength) { |
| throw new UnsupportedError('Cannot modify list'); |
| } |
| |
| void sort([Comparator<Element> compare]) { |
| throw new UnsupportedError('Cannot sort list'); |
| } |
| |
| void shuffle([Random random]) { |
| throw new UnsupportedError('Cannot shuffle list'); |
| } |
| |
| Element get first => _nodeList.first; |
| |
| Element get last => _nodeList.last; |
| |
| Element get single => _nodeList.single; |
| |
| CssClassSet get classes => new _MultiElementCssClassSet(_elementList); |
| |
| CssStyleDeclarationBase get style => |
| new _CssStyleDeclarationSet(_elementList); |
| |
| void set classes(Iterable<String> value) { |
| _elementList.forEach((e) => e.classes = value); |
| } |
| |
| CssRect get contentEdge => new _ContentCssListRect(_elementList); |
| |
| CssRect get paddingEdge => _elementList.first.paddingEdge; |
| |
| CssRect get borderEdge => _elementList.first.borderEdge; |
| |
| CssRect get marginEdge => _elementList.first.marginEdge; |
| |
| List<Node> get rawList => _nodeList; |
| |
| $!ELEMENT_STREAM_GETTERS |
| } |
| |
| @DocsEditable() |
| $(ANNOTATIONS)$(NATIVESPEC)abstract class $CLASSNAME$EXTENDS$IMPLEMENTS { |
| |
| /** |
| * Creates an HTML element from a valid fragment of HTML. |
| * |
| * var element = new Element.html('<div class="foo">content</div>'); |
| * |
| * The HTML fragment should contain only one single root element, any |
| * leading or trailing text nodes will be removed. |
| * |
| * The HTML fragment is parsed as if it occurred within the context of a |
| * `<body>` tag, this means that special elements such as `<caption>` which |
| * must be parsed within the scope of a `<table>` element will be dropped. Use |
| * [createFragment] to parse contextual HTML fragments. |
| * |
| * Unless a validator is provided this will perform the default validation |
| * and remove all scriptable elements and attributes. |
| * |
| * See also: |
| * |
| * * [NodeValidator] |
| * |
| */ |
| factory Element.html(String html, |
| {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) { |
| var fragment = document.body.createFragment(html, validator: validator, |
| treeSanitizer: treeSanitizer); |
| |
| return fragment.nodes.where((e) => e is Element).single; |
| } |
| |
| /** |
| * Custom element creation constructor. |
| * |
| * This constructor is used by the DOM when a custom element has been |
| * created. It can only be invoked by subclasses of Element from |
| * that classes created constructor. |
| * |
| * class CustomElement extends Element { |
| * factory CustomElement() => new Element.tag('x-custom'); |
| * |
| * CustomElement.created() : super.created() { |
| * // Perform any element initialization. |
| * } |
| * } |
| * document.registerElement('x-custom', CustomElement); |
| */ |
| Element.created() : super._created() { |
| // Validate that this is a custom element & perform any additional |
| // initialization. |
| _initializeCustomElement(this); |
| } |
| |
| /** |
| * Creates the HTML element specified by the tag name. |
| * |
| * This is similar to [Document.createElement]. |
| * [tag] should be a valid HTML tag name. If [tag] is an unknown tag then |
| * this will create an [UnknownElement]. |
| * |
| * var divElement = new Element.tag('div'); |
| * print(divElement is DivElement); // 'true' |
| * var myElement = new Element.tag('unknownTag'); |
| * print(myElement is UnknownElement); // 'true' |
| * |
| * For standard elements it is more preferable to use the type constructors: |
| * var element = new DivElement(); |
| * |
| * See also: |
| * |
| * * [isTagSupported] |
| */ |
| factory $CLASSNAME.tag(String tag, [String typeExtention]) => |
| _$(CLASSNAME)FactoryProvider.createElement_tag(tag, typeExtention); |
| |
| /// Creates a new `<a>` element. |
| /// |
| /// This is identical to calling `new Element.tag('a')`. |
| factory Element.a() => new Element.tag('a'); |
| |
| /// Creates a new `<article>` element. |
| /// |
| /// This is identical to calling `new Element.tag('article')`. |
| factory Element.article() => new Element.tag('article'); |
| |
| /// Creates a new `<aside>` element. |
| /// |
| /// This is identical to calling `new Element.tag('aside')`. |
| factory Element.aside() => new Element.tag('aside'); |
| |
| /// Creates a new `<audio>` element. |
| /// |
| /// This is identical to calling `new Element.tag('audio')`. |
| factory Element.audio() => new Element.tag('audio'); |
| |
| /// Creates a new `<br>` element. |
| /// |
| /// This is identical to calling `new Element.tag('br')`. |
| factory Element.br() => new Element.tag('br'); |
| |
| /// Creates a new `<canvas>` element. |
| /// |
| /// This is identical to calling `new Element.tag('canvas')`. |
| factory Element.canvas() => new Element.tag('canvas'); |
| |
| /// Creates a new `<div>` element. |
| /// |
| /// This is identical to calling `new Element.tag('div')`. |
| factory Element.div() => new Element.tag('div'); |
| |
| /// Creates a new `<footer>` element. |
| /// |
| /// This is identical to calling `new Element.tag('footer')`. |
| factory Element.footer() => new Element.tag('footer'); |
| |
| /// Creates a new `<header>` element. |
| /// |
| /// This is identical to calling `new Element.tag('header')`. |
| factory Element.header() => new Element.tag('header'); |
| |
| /// Creates a new `<hr>` element. |
| /// |
| /// This is identical to calling `new Element.tag('hr')`. |
| factory Element.hr() => new Element.tag('hr'); |
| |
| /// Creates a new `<iframe>` element. |
| /// |
| /// This is identical to calling `new Element.tag('iframe')`. |
| factory Element.iframe() => new Element.tag('iframe'); |
| |
| /// Creates a new `<img>` element. |
| /// |
| /// This is identical to calling `new Element.tag('img')`. |
| factory Element.img() => new Element.tag('img'); |
| |
| /// Creates a new `<li>` element. |
| /// |
| /// This is identical to calling `new Element.tag('li')`. |
| factory Element.li() => new Element.tag('li'); |
| |
| /// Creates a new `<nav>` element. |
| /// |
| /// This is identical to calling `new Element.tag('nav')`. |
| factory Element.nav() => new Element.tag('nav'); |
| |
| /// Creates a new `<ol>` element. |
| /// |
| /// This is identical to calling `new Element.tag('ol')`. |
| factory Element.ol() => new Element.tag('ol'); |
| |
| /// Creates a new `<option>` element. |
| /// |
| /// This is identical to calling `new Element.tag('option')`. |
| factory Element.option() => new Element.tag('option'); |
| |
| /// Creates a new `<p>` element. |
| /// |
| /// This is identical to calling `new Element.tag('p')`. |
| factory Element.p() => new Element.tag('p'); |
| |
| /// Creates a new `<pre>` element. |
| /// |
| /// This is identical to calling `new Element.tag('pre')`. |
| factory Element.pre() => new Element.tag('pre'); |
| |
| /// Creates a new `<section>` element. |
| /// |
| /// This is identical to calling `new Element.tag('section')`. |
| factory Element.section() => new Element.tag('section'); |
| |
| /// Creates a new `<select>` element. |
| /// |
| /// This is identical to calling `new Element.tag('select')`. |
| factory Element.select() => new Element.tag('select'); |
| |
| /// Creates a new `<span>` element. |
| /// |
| /// This is identical to calling `new Element.tag('span')`. |
| factory Element.span() => new Element.tag('span'); |
| |
| /// Creates a new `<svg>` element. |
| /// |
| /// This is identical to calling `new Element.tag('svg')`. |
| factory Element.svg() => new Element.tag('svg'); |
| |
| /// Creates a new `<table>` element. |
| /// |
| /// This is identical to calling `new Element.tag('table')`. |
| factory Element.table() => new Element.tag('table'); |
| |
| /// Creates a new `<td>` element. |
| /// |
| /// This is identical to calling `new Element.tag('td')`. |
| factory Element.td() => new Element.tag('td'); |
| |
| /// Creates a new `<textarea>` element. |
| /// |
| /// This is identical to calling `new Element.tag('textarea')`. |
| factory Element.textarea() => new Element.tag('textarea'); |
| |
| /// Creates a new `<th>` element. |
| /// |
| /// This is identical to calling `new Element.tag('th')`. |
| factory Element.th() => new Element.tag('th'); |
| |
| /// Creates a new `<tr>` element. |
| /// |
| /// This is identical to calling `new Element.tag('tr')`. |
| factory Element.tr() => new Element.tag('tr'); |
| |
| /// Creates a new `<ul>` element. |
| /// |
| /// This is identical to calling `new Element.tag('ul')`. |
| factory Element.ul() => new Element.tag('ul'); |
| |
| /// Creates a new `<video>` element. |
| /// |
| /// This is identical to calling `new Element.tag('video')`. |
| factory Element.video() => new Element.tag('video'); |
| |
| /** |
| * All attributes on this element. |
| * |
| * Any modifications to the attribute map will automatically be applied to |
| * this element. |
| * |
| * This only includes attributes which are not in a namespace |
| * (such as 'xlink:href'), additional attributes can be accessed via |
| * [getNamespacedAttributes]. |
| */ |
| Map<String, String> get attributes => new _ElementAttributeMap(this); |
| |
| void set attributes(Map<String, String> value) { |
| Map<String, String> attributes = this.attributes; |
| attributes.clear(); |
| for (String key in value.keys) { |
| attributes[key] = value[key]; |
| } |
| } |
| |
| /** |
| * List of the direct children of this element. |
| * |
| * This collection can be used to add and remove elements from the document. |
| * |
| * var item = new DivElement(); |
| * item.text = 'Something'; |
| * document.body.children.add(item) // Item is now displayed on the page. |
| * for (var element in document.body.children) { |
| * element.style.background = 'red'; // Turns every child of body red. |
| * } |
| */ |
| List<Element> get children => new _ChildrenElementList._wrap(this); |
| |
| void set children(List<Element> value) { |
| // Copy list first since we don't want liveness during iteration. |
| List copy = new List.from(value); |
| var children = this.children; |
| children.clear(); |
| children.addAll(copy); |
| } |
| |
| /** |
| * Finds all descendent elements of this element that match the specified |
| * group of selectors. |
| * |
| * [selectors] should be a string using CSS selector syntax. |
| * |
| * var items = element.querySelectorAll('.itemClassName'); |
| * |
| * For details about CSS selector syntax, see the |
| * [CSS selector specification](http://www.w3.org/TR/css3-selectors/). |
| */ |
| @DomName('Element.querySelectorAll') |
| ElementList querySelectorAll(String selectors) => |
| new _FrozenElementList._wrap(_querySelectorAll(selectors)); |
| |
| /** |
| * Alias for [querySelector]. Note this function is deprecated because its |
| * semantics will be changing in the future. |
| */ |
| @deprecated |
| @DomName('Element.querySelector') |
| @Experimental() |
| Element query(String relativeSelectors) => querySelector(relativeSelectors); |
| |
| /** |
| * Alias for [querySelectorAll]. Note this function is deprecated because its |
| * semantics will be changing in the future. |
| */ |
| @deprecated |
| @DomName('Element.querySelectorAll') |
| @Experimental() |
| ElementList queryAll(String relativeSelectors) => |
| querySelectorAll(relativeSelectors); |
| |
| /** |
| * The set of CSS classes applied to this element. |
| * |
| * This set makes it easy to add, remove or toggle the classes applied to |
| * this element. |
| * |
| * element.classes.add('selected'); |
| * element.classes.toggle('isOnline'); |
| * element.classes.remove('selected'); |
| */ |
| CssClassSet get classes => new _ElementCssClassSet(this); |
| |
| void set classes(Iterable<String> value) { |
| CssClassSet classSet = classes; |
| classSet.clear(); |
| classSet.addAll(value); |
| } |
| |
| /** |
| * Allows access to all custom data attributes (data-*) set on this element. |
| * |
| * The keys for the map must follow these rules: |
| * |
| * * The name must not begin with 'xml'. |
| * * The name cannot contain a semi-colon (';'). |
| * * The name cannot contain any capital letters. |
| * |
| * Any keys from markup will be converted to camel-cased keys in the map. |
| * |
| * For example, HTML specified as: |
| * |
| * <div data-my-random-value='value'></div> |
| * |
| * Would be accessed in Dart as: |
| * |
| * var value = element.dataset['myRandomValue']; |
| * |
| * See also: |
| * |
| * * [Custom data attributes](http://www.w3.org/TR/html5/global-attributes.html#custom-data-attribute) |
| */ |
| Map<String, String> get dataset => |
| new _DataAttributeMap(attributes); |
| |
| void set dataset(Map<String, String> value) { |
| final data = this.dataset; |
| data.clear(); |
| for (String key in value.keys) { |
| data[key] = value[key]; |
| } |
| } |
| |
| /** |
| * Gets a map for manipulating the attributes of a particular namespace. |
| * |
| * This is primarily useful for SVG attributes such as xref:link. |
| */ |
| Map<String, String> getNamespacedAttributes(String namespace) { |
| return new _NamespacedAttributeMap(this, namespace); |
| } |
| |
| /** |
| * The set of all CSS values applied to this element, including inherited |
| * and default values. |
| * |
| * The computedStyle contains values that are inherited from other |
| * sources, such as parent elements or stylesheets. This differs from the |
| * [style] property, which contains only the values specified directly on this |
| * element. |
| * |
| * PseudoElement can be values such as `::after`, `::before`, `::marker`, |
| * `::line-marker`. |
| * |
| * See also: |
| * |
| * * [CSS Inheritance and Cascade](http://docs.webplatform.org/wiki/tutorials/inheritance_and_cascade) |
| * * [Pseudo-elements](http://docs.webplatform.org/wiki/css/selectors/pseudo-elements) |
| */ |
| CssStyleDeclaration getComputedStyle([String pseudoElement]) { |
| if (pseudoElement == null) { |
| pseudoElement = ''; |
| } |
| // TODO(jacobr): last param should be null, see b/5045788 |
| return window._getComputedStyle(this, pseudoElement); |
| } |
| |
| /** |
| * Gets the position of this element relative to the client area of the page. |
| */ |
| Rectangle get client => new Rectangle(clientLeft, clientTop, clientWidth, |
| clientHeight); |
| |
| /** |
| * Gets the offset of this element relative to its offsetParent. |
| */ |
| Rectangle get offset => new Rectangle(offsetLeft, offsetTop, offsetWidth, |
| offsetHeight); |
| |
| /** |
| * Adds the specified text after the last child of this element. |
| */ |
| void appendText(String text) { |
| this.insertAdjacentText('beforeend', text); |
| } |
| |
| /** |
| * Parses the specified text as HTML and adds the resulting node after the |
| * last child of this element. |
| */ |
| void appendHtml(String text) { |
| this.insertAdjacentHtml('beforeend', text); |
| } |
| |
| /** |
| * Checks to see if the tag name is supported by the current platform. |
| * |
| * The tag should be a valid HTML tag name. |
| */ |
| static bool isTagSupported(String tag) { |
| var e = _ElementFactoryProvider.createElement_tag(tag, null); |
| return e is Element && !(e is UnknownElement); |
| } |
| |
| /** |
| * Called by the DOM when this element has been inserted into the live |
| * document. |
| * |
| * More information can be found in the |
| * [Custom Elements](http://w3c.github.io/webcomponents/spec/custom/#dfn-attached-callback) |
| * draft specification. |
| */ |
| @Experimental() |
| void attached() { |
| // For the deprecation period, call the old callback. |
| enteredView(); |
| } |
| |
| /** |
| * Called by the DOM when this element has been removed from the live |
| * document. |
| * |
| * More information can be found in the |
| * [Custom Elements](http://w3c.github.io/webcomponents/spec/custom/#dfn-detached-callback) |
| * draft specification. |
| */ |
| @Experimental() |
| void detached() { |
| // For the deprecation period, call the old callback. |
| leftView(); |
| } |
| |
| /** *Deprecated*: override [attached] instead. */ |
| @Experimental() |
| @deprecated |
| void enteredView() {} |
| |
| /** *Deprecated*: override [detached] instead. */ |
| @Experimental() |
| @deprecated |
| void leftView() {} |
| |
| /** |
| * Called by the DOM whenever an attribute on this has been changed. |
| */ |
| void attributeChanged(String name, String oldValue, String newValue) {} |
| |
| // Hooks to support custom WebComponents. |
| |
| $if DART2JS |
| @Creates('Null') // Set from Dart code; does not instantiate a native type. |
| $endif |
| Element _xtag; |
| |
| /** |
| * Experimental support for [web components][wc]. This field stores a |
| * reference to the component implementation. It was inspired by Mozilla's |
| * [x-tags][] project. Please note: in the future it may be possible to |
| * `extend Element` from your class, in which case this field will be |
| * deprecated. |
| * |
| * If xtag has not been set, it will simply return `this` [Element]. |
| * |
| * [wc]: http://dvcs.w3.org/hg/webcomponents/raw-file/tip/explainer/index.html |
| * [x-tags]: http://x-tags.org/ |
| */ |
| // Note: return type is `dynamic` for convenience to suppress warnings when |
| // members of the component are used. The actual type is a subtype of Element. |
| get xtag => _xtag != null ? _xtag : this; |
| |
| void set xtag(Element value) { |
| _xtag = value; |
| } |
| |
| @DomName('Element.localName') |
| @DocsEditable() |
| String get localName => _localName; |
| |
| /** |
| * A URI that identifies the XML namespace of this element. |
| * |
| * `null` if no namespace URI is specified. |
| * |
| * ## Other resources |
| * |
| * * [Node.namespaceURI] |
| * (http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-NodeNSname) from W3C. |
| */ |
| @DomName('Element.namespaceUri') |
| String get namespaceUri => _namespaceUri; |
| |
| /** |
| * The string representation of this element. |
| * |
| * This is equivalent to reading the [localName] property. |
| */ |
| String toString() => localName; |
| |
| /** |
| * Scrolls this element into view. |
| * |
| * Only one of of the alignment options may be specified at a time. |
| * |
| * If no options are specified then this will attempt to scroll the minimum |
| * amount needed to bring the element into view. |
| * |
| * Note that alignCenter is currently only supported on WebKit platforms. If |
| * alignCenter is specified but not supported then this will fall back to |
| * alignTop. |
| * |
| * See also: |
| * |
| * * [scrollIntoView](http://docs.webplatform.org/wiki/dom/methods/scrollIntoView) |
| * * [scrollIntoViewIfNeeded](http://docs.webplatform.org/wiki/dom/methods/scrollIntoViewIfNeeded) |
| */ |
| void scrollIntoView([ScrollAlignment alignment]) { |
| var hasScrollIntoViewIfNeeded = true; |
| $if DART2JS |
| hasScrollIntoViewIfNeeded = |
| JS('bool', '!!(#.scrollIntoViewIfNeeded)', this); |
| $endif |
| if (alignment == ScrollAlignment.TOP) { |
| this._scrollIntoView(true); |
| } else if (alignment == ScrollAlignment.BOTTOM) { |
| this._scrollIntoView(false); |
| } else if (hasScrollIntoViewIfNeeded) { |
| if (alignment == ScrollAlignment.CENTER) { |
| this._scrollIntoViewIfNeeded(true); |
| } else { |
| this._scrollIntoViewIfNeeded(); |
| } |
| } else { |
| this._scrollIntoView(); |
| } |
| } |
| |
| $if DART2JS |
| /** |
| * Static factory designed to expose `mousewheel` events to event |
| * handlers that are not necessarily instances of [Element]. |
| * |
| * See [EventStreamProvider] for usage information. |
| */ |
| @DomName('Element.mouseWheelEvent') |
| static const EventStreamProvider<WheelEvent> mouseWheelEvent = |
| const _CustomEventStreamProvider<WheelEvent>( |
| Element._determineMouseWheelEventType); |
| |
| static String _determineMouseWheelEventType(EventTarget e) { |
| if (JS('bool', '#.onwheel !== undefined', e)) { |
| // W3C spec, and should be IE9+, but IE has a bug exposing onwheel. |
| return 'wheel'; |
| } else if (JS('bool', '#.onmousewheel !== undefined', e)) { |
| // Chrome & IE |
| return 'mousewheel'; |
| } else { |
| // Firefox |
| return 'DOMMouseScroll'; |
| } |
| } |
| |
| /** |
| * Static factory designed to expose `transitionend` events to event |
| * handlers that are not necessarily instances of [Element]. |
| * |
| * See [EventStreamProvider] for usage information. |
| */ |
| @DomName('Element.transitionEndEvent') |
| static const EventStreamProvider<TransitionEvent> transitionEndEvent = |
| const _CustomEventStreamProvider<TransitionEvent>( |
| Element._determineTransitionEventType); |
| |
| static String _determineTransitionEventType(EventTarget e) { |
| // Unfortunately the normal 'ontransitionend' style checks don't work here. |
| if (Device.isWebKit) { |
| return 'webkitTransitionEnd'; |
| } else if (Device.isOpera) { |
| return 'oTransitionEnd'; |
| } |
| return 'transitionend'; |
| } |
| /** |
| * Inserts text into the DOM at the specified location. |
| * |
| * To see the possible values for [where], read the doc for |
| * [insertAdjacentHtml]. |
| * |
| * See also: |
| * |
| * * [insertAdjacentHtml] |
| */ |
| void insertAdjacentText(String where, String text) { |
| if (JS('bool', '!!#.insertAdjacentText', this)) { |
| _insertAdjacentText(where, text); |
| } else { |
| _insertAdjacentNode(where, new Text(text)); |
| } |
| } |
| |
| @JSName('insertAdjacentText') |
| void _insertAdjacentText(String where, String text) native; |
| |
| /** |
| * Parses text as an HTML fragment and inserts it into the DOM at the |
| * specified location. |
| * |
| * The [where] parameter indicates where to insert the HTML fragment: |
| * |
| * * 'beforeBegin': Immediately before this element. |
| * * 'afterBegin': As the first child of this element. |
| * * 'beforeEnd': As the last child of this element. |
| * * 'afterEnd': Immediately after this element. |
| * |
| * var html = '<div class="something">content</div>'; |
| * // Inserts as the first child |
| * document.body.insertAdjacentHtml('afterBegin', html); |
| * var createdElement = document.body.children[0]; |
| * print(createdElement.classes[0]); // Prints 'something' |
| * |
| * See also: |
| * |
| * * [insertAdjacentText] |
| * * [insertAdjacentElement] |
| */ |
| void insertAdjacentHtml(String where, String html) { |
| if (JS('bool', '!!#.insertAdjacentHTML', this)) { |
| _insertAdjacentHtml(where, html); |
| } else { |
| _insertAdjacentNode(where, new DocumentFragment.html(html)); |
| } |
| } |
| |
| @JSName('insertAdjacentHTML') |
| void _insertAdjacentHtml(String where, String text) native; |
| |
| /** |
| * Inserts [element] into the DOM at the specified location. |
| * |
| * To see the possible values for [where], read the doc for |
| * [insertAdjacentHtml]. |
| * |
| * See also: |
| * |
| * * [insertAdjacentHtml] |
| */ |
| Element insertAdjacentElement(String where, Element element) { |
| if (JS('bool', '!!#.insertAdjacentElement', this)) { |
| _insertAdjacentElement(where, element); |
| } else { |
| _insertAdjacentNode(where, element); |
| } |
| return element; |
| } |
| |
| @JSName('insertAdjacentElement') |
| void _insertAdjacentElement(String where, Element element) native; |
| |
| void _insertAdjacentNode(String where, Node node) { |
| switch (where.toLowerCase()) { |
| case 'beforebegin': |
| this.parentNode.insertBefore(node, this); |
| break; |
| case 'afterbegin': |
| var first = this.nodes.length > 0 ? this.nodes[0] : null; |
| this.insertBefore(node, first); |
| break; |
| case 'beforeend': |
| this.append(node); |
| break; |
| case 'afterend': |
| this.parentNode.insertBefore(node, this.nextNode); |
| break; |
| default: |
| throw new ArgumentError("Invalid position ${where}"); |
| } |
| } |
| |
| /** |
| * Checks if this element matches the CSS selectors. |
| */ |
| @Experimental() |
| bool matches(String selectors) { |
| if (JS('bool', '!!#.matches', this)) { |
| return JS('bool', '#.matches(#)', this, selectors); |
| } else if (JS('bool', '!!#.webkitMatchesSelector', this)) { |
| return JS('bool', '#.webkitMatchesSelector(#)', this, selectors); |
| } else if (JS('bool', '!!#.mozMatchesSelector', this)) { |
| return JS('bool', '#.mozMatchesSelector(#)', this, selectors); |
| } else if (JS('bool', '!!#.msMatchesSelector', this)) { |
| return JS('bool', '#.msMatchesSelector(#)', this, selectors); |
| } else if (JS('bool', '!!#.oMatchesSelector', this)) { |
| return JS('bool', '#.oMatchesSelector(#)', this, selectors); |
| } else { |
| throw new UnsupportedError("Not supported on this platform"); |
| } |
| } |
| $else |
| $endif |
| |
| /** Checks if this element or any of its parents match the CSS selectors. */ |
| @Experimental() |
| bool matchesWithAncestors(String selectors) { |
| var elem = this; |
| do { |
| if (elem.matches(selectors)) return true; |
| elem = elem.parent; |
| } while(elem != null); |
| return false; |
| } |
| |
| $if DART2JS |
| /** |
| * Creates a new shadow root for this shadow host. |
| * |
| * ## Other resources |
| * |
| * * [Shadow DOM 101] |
| * (http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/) |
| * from HTML5Rocks. |
| * * [Shadow DOM specification] |
| * (http://www.w3.org/TR/shadow-dom/) from W3C. |
| */ |
| @DomName('Element.createShadowRoot') |
| @SupportedBrowser(SupportedBrowser.CHROME, '25') |
| @Experimental() |
| ShadowRoot createShadowRoot() { |
| return JS('ShadowRoot', |
| '(#.createShadowRoot || #.webkitCreateShadowRoot).call(#)', |
| this, this, this); |
| } |
| |
| /** |
| * The shadow root of this shadow host. |
| * |
| * ## Other resources |
| * |
| * * [Shadow DOM 101] |
| * (http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/) |
| * from HTML5Rocks. |
| * * [Shadow DOM specification] |
| * (http://www.w3.org/TR/shadow-dom/) from W3C. |
| */ |
| @DomName('Element.shadowRoot') |
| @SupportedBrowser(SupportedBrowser.CHROME, '25') |
| @Experimental() |
| ShadowRoot get shadowRoot => |
| JS('ShadowRoot|Null', '#.shadowRoot || #.webkitShadowRoot', this, this); |
| $endif |
| |
| /** |
| * Access this element's content position. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. |
| * |
| * _Important_ _note_: use of this method _will_ perform CSS calculations that |
| * can trigger a browser reflow. Therefore, use of this property _during_ an |
| * animation frame is discouraged. See also: |
| * [Browser Reflow](https://developers.google.com/speed/articles/reflow) |
| */ |
| @Experimental() |
| CssRect get contentEdge => new _ContentCssRect(this); |
| |
| /** |
| * Access the dimensions and position of this element's content + padding box. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's |
| * [innerHeight](http://api.jquery.com/innerHeight/) value for an element. |
| * This is also a rectangle equalling the dimensions of clientHeight and |
| * clientWidth. |
| * |
| * _Important_ _note_: use of this method _will_ perform CSS calculations that |
| * can trigger a browser reflow. Therefore, use of this property _during_ an |
| * animation frame is discouraged. See also: |
| * [Browser Reflow](https://developers.google.com/speed/articles/reflow) |
| */ |
| @Experimental() |
| CssRect get paddingEdge => new _PaddingCssRect(this); |
| |
| /** |
| * Access the dimensions and position of this element's content + padding + |
| * border box. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's |
| * [outerHeight](http://api.jquery.com/outerHeight/) value for an element. |
| * |
| * _Important_ _note_: use of this method _will_ perform CSS calculations that |
| * can trigger a browser reflow. Therefore, use of this property _during_ an |
| * animation frame is discouraged. See also: |
| * [Browser Reflow](https://developers.google.com/speed/articles/reflow) |
| */ |
| @Experimental() |
| CssRect get borderEdge => new _BorderCssRect(this); |
| |
| /** |
| * Access the dimensions and position of this element's content + padding + |
| * border + margin box. |
| * |
| * This returns a rectangle with the dimenions actually available for content |
| * in this element, in pixels, regardless of this element's box-sizing |
| * property. Unlike [getBoundingClientRect], the dimensions of this rectangle |
| * will return the same numerical height if the element is hidden or not. This |
| * can be used to retrieve jQuery's |
| * [outerHeight](http://api.jquery.com/outerHeight/) value for an element. |
| * |
| * _Important_ _note_: use of this method will perform CSS calculations that |
| * can trigger a browser reflow. Therefore, use of this property _during_ an |
| * animation frame is discouraged. See also: |
| * [Browser Reflow](https://developers.google.com/speed/articles/reflow) |
| */ |
| @Experimental() |
| CssRect get marginEdge => new _MarginCssRect(this); |
| |
| /** |
| * Provides the coordinates of the element relative to the top of the |
| * document. |
| * |
| * This method is the Dart equivalent to jQuery's |
| * [offset](http://api.jquery.com/offset/) method. |
| */ |
| @Experimental() |
| Point get documentOffset => offsetTo(document.documentElement); |
| |
| /** |
| * Provides the offset of this element's [borderEdge] relative to the |
| * specified [parent]. |
| * |
| * This is the Dart equivalent of jQuery's |
| * [position](http://api.jquery.com/position/) method. Unlike jQuery's |
| * position, however, [parent] can be any parent element of `this`, |
| * rather than only `this`'s immediate [offsetParent]. If the specified |
| * element is _not_ an offset parent or transitive offset parent to this |
| * element, an [ArgumentError] is thrown. |
| */ |
| @Experimental() |
| Point offsetTo(Element parent) { |
| return Element._offsetToHelper(this, parent); |
| } |
| |
| static Point _offsetToHelper(Element current, Element parent) { |
| // We're hopping from _offsetParent_ to offsetParent (not just parent), so |
| // offsetParent, "tops out" at BODY. But people could conceivably pass in |
| // the document.documentElement and I want it to return an absolute offset, |
| // so we have the special case checking for HTML. |
| bool foundAsParent = identical(current, parent) || parent.tagName == 'HTML'; |
| if (current == null || identical(current, parent)) { |
| if (foundAsParent) return new Point(0, 0); |
| throw new ArgumentError("Specified element is not a transitive offset " |
| "parent of this element."); |
| } |
| Element parentOffset = current.offsetParent; |
| Point p = Element._offsetToHelper(parentOffset, parent); |
| return new Point(p.x + current.offsetLeft, p.y + current.offsetTop); |
| } |
| |
| static HtmlDocument _parseDocument; |
| static Range _parseRange; |
| static NodeValidatorBuilder _defaultValidator; |
| static _ValidatingTreeSanitizer _defaultSanitizer; |
| |
| /** |
| * Create a DocumentFragment from the HTML fragment and ensure that it follows |
| * the sanitization rules specified by the validator or treeSanitizer. |
| * |
| * If the default validation behavior is too restrictive then a new |
| * NodeValidator should be created, either extending or wrapping a default |
| * validator and overriding the validation APIs. |
| * |
| * The treeSanitizer is used to walk the generated node tree and sanitize it. |
| * A custom treeSanitizer can also be provided to perform special validation |
| * rules but since the API is more complex to implement this is discouraged. |
| * |
| * The returned tree is guaranteed to only contain nodes and attributes which |
| * are allowed by the provided validator. |
| * |
| * See also: |
| * |
| * * [NodeValidator] |
| * * [NodeTreeSanitizer] |
| */ |
| DocumentFragment createFragment(String html, |
| {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) { |
| if (treeSanitizer == null) { |
| if (validator == null) { |
| if (_defaultValidator == null) { |
| _defaultValidator = new NodeValidatorBuilder.common(); |
| } |
| validator = _defaultValidator; |
| } |
| if (_defaultSanitizer == null) { |
| _defaultSanitizer = new _ValidatingTreeSanitizer(validator); |
| } else { |
| _defaultSanitizer.validator = validator; |
| } |
| treeSanitizer = _defaultSanitizer; |
| } else if (validator != null) { |
| throw new ArgumentError( |
| 'validator can only be passed if treeSanitizer is null'); |
| } |
| |
| if (_parseDocument == null) { |
| _parseDocument = document.implementation.createHtmlDocument(''); |
| _parseRange = _parseDocument.createRange(); |
| |
| // Workaround for Chrome bug 229142- URIs are not resolved in new doc. |
| var base = _parseDocument.createElement('base'); |
| base.href = document.baseUri; |
| _parseDocument.head.append(base); |
| } |
| var contextElement; |
| if (this is BodyElement) { |
| contextElement = _parseDocument.body; |
| } else { |
| contextElement = _parseDocument.createElement(tagName); |
| _parseDocument.body.append(contextElement); |
| } |
| var fragment; |
| if (Range.supportsCreateContextualFragment) { |
| _parseRange.selectNodeContents(contextElement); |
| fragment = _parseRange.createContextualFragment(html); |
| } else { |
| contextElement._innerHtml = html; |
| |
| fragment = _parseDocument.createDocumentFragment(); |
| while (contextElement.firstChild != null) { |
| fragment.append(contextElement.firstChild); |
| } |
| } |
| if (contextElement != _parseDocument.body) { |
| contextElement.remove(); |
| } |
| |
| treeSanitizer.sanitizeTree(fragment); |
| // Copy the fragment over to the main document (fix for 14184) |
| document.adoptNode(fragment); |
| |
| return fragment; |
| } |
| |
| /** |
| * Parses the HTML fragment and sets it as the contents of this element. |
| * |
| * This uses the default sanitization behavior to sanitize the HTML fragment, |
| * use [setInnerHtml] to override the default behavior. |
| */ |
| void set innerHtml(String html) { |
| this.setInnerHtml(html); |
| } |
| |
| /** |
| * Parses the HTML fragment and sets it as the contents of this element. |
| * This ensures that the generated content follows the sanitization rules |
| * specified by the validator or treeSanitizer. |
| * |
| * If the default validation behavior is too restrictive then a new |
| * NodeValidator should be created, either extending or wrapping a default |
| * validator and overriding the validation APIs. |
| * |
| * The treeSanitizer is used to walk the generated node tree and sanitize it. |
| * A custom treeSanitizer can also be provided to perform special validation |
| * rules but since the API is more complex to implement this is discouraged. |
| * |
| * The resulting tree is guaranteed to only contain nodes and attributes which |
| * are allowed by the provided validator. |
| * |
| * See also: |
| * |
| * * [NodeValidator] |
| * * [NodeTreeSanitizer] |
| */ |
| void setInnerHtml(String html, |
| {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) { |
| text = null; |
| append(createFragment( |
| html, validator: validator, treeSanitizer: treeSanitizer)); |
| } |
| String get innerHtml => _innerHtml; |
| |
| /** |
| * This is an ease-of-use accessor for event streams which should only be |
| * used when an explicit accessor is not available. |
| */ |
| ElementEvents get on => new ElementEvents(this); |
| |
| $if DART2JS |
| @DomName('Element.offsetHeight') |
| @DocsEditable() |
| int get offsetHeight => JS('num', '#.offsetHeight', this).round(); |
| |
| @DomName('Element.offsetLeft') |
| @DocsEditable() |
| int get offsetLeft => JS('num', '#.offsetLeft', this).round(); |
| |
| @DomName('Element.offsetTop') |
| @DocsEditable() |
| int get offsetTop => JS('num', '#.offsetTop', this).round(); |
| |
| @DomName('Element.offsetWidth') |
| @DocsEditable() |
| int get offsetWidth => JS('num', '#.offsetWidth', this).round(); |
| |
| @DomName('Element.clientHeight') |
| @DocsEditable() |
| int get clientHeight => JS('num', '#.clientHeight', this).round(); |
| |
| @DomName('Element.clientLeft') |
| @DocsEditable() |
| int get clientLeft => JS('num', '#.clientLeft', this).round(); |
| |
| @DomName('Element.clientTop') |
| @DocsEditable() |
| int get clientTop => JS('num', '#.clientTop', this).round(); |
| |
| @DomName('Element.clientWidth') |
| @DocsEditable() |
| int get clientWidth => JS('num', '#.clientWidth', this).round(); |
| |
| @DomName('Element.scrollHeight') |
| @DocsEditable() |
| int get scrollHeight => JS('num', '#.scrollHeight', this).round(); |
| |
| @DomName('Element.scrollLeft') |
| @DocsEditable() |
| int get scrollLeft => JS('num', '#.scrollLeft', this).round(); |
| |
| @DomName('Element.scrollLeft') |
| @DocsEditable() |
| void set scrollLeft(int value) => JS("void", "#.scrollLeft = #", this, value.round()); |
| |
| @DomName('Element.scrollTop') |
| @DocsEditable() |
| int get scrollTop => JS('num', '#.scrollTop', this).round(); |
| |
| @DomName('Element.scrollTop') |
| @DocsEditable() |
| void set scrollTop(int value) => JS("void", "#.scrollTop = #", this, value.round()); |
| |
| @DomName('Element.scrollWidth') |
| @DocsEditable() |
| int get scrollWidth => JS('num', '#.scrollWidth', this).round(); |
| |
| $else |
| @DomName('Element.offsetHeight') |
| @DocsEditable() |
| int get offsetHeight => _blink.BlinkElement.$offsetHeight_Getter(this).round(); |
| |
| @DomName('Element.offsetLeft') |
| @DocsEditable() |
| int get offsetLeft => _blink.BlinkElement.$offsetLeft_Getter(this).round(); |
| |
| @DomName('Element.offsetTop') |
| @DocsEditable() |
| int get offsetTop => _blink.BlinkElement.$offsetTop_Getter(this).round(); |
| |
| @DomName('Element.offsetWidth') |
| @DocsEditable() |
| int get offsetWidth => _blink.BlinkElement.$offsetWidth_Getter(this).round(); |
| |
| @DomName('Element.clientHeight') |
| @DocsEditable() |
| int get clientHeight => _blink.BlinkElement.$clientHeight_Getter(this).round(); |
| |
| @DomName('Element.clientLeft') |
| @DocsEditable() |
| int get clientLeft => _blink.BlinkElement.$clientLeft_Getter(this).round(); |
| |
| @DomName('Element.clientTop') |
| @DocsEditable() |
| int get clientTop => _blink.BlinkElement.$clientTop_Getter(this).round(); |
| |
| @DomName('Element.clientWidth') |
| @DocsEditable() |
| int get clientWidth => _blink.BlinkElement.$clientWidth_Getter(this).round(); |
| |
| @DomName('Element.scrollHeight') |
| @DocsEditable() |
| int get scrollHeight => _blink.BlinkElement.$scrollHeight_Getter(this).round(); |
| |
| @DomName('Element.scrollLeft') |
| @DocsEditable() |
| int get scrollLeft => _blink.BlinkElement.$scrollLeft_Getter(this).round(); |
| |
| @DomName('Element.scrollLeft') |
| @DocsEditable() |
| void set scrollLeft(int value) => _blink.BlinkElement.$scrollLeft_Setter(this, value.round()); |
| |
| @DomName('Element.scrollTop') |
| @DocsEditable() |
| int get scrollTop => _blink.BlinkElement.$scrollTop_Getter(this).round(); |
| |
| @DomName('Element.scrollTop') |
| @DocsEditable() |
| void set scrollTop(int value) => _blink.BlinkElement.$scrollTop_Setter(this, value.round()); |
| |
| @DomName('Element.scrollWidth') |
| @DocsEditable() |
| int get scrollWidth => _blink.BlinkElement.$scrollWidth_Getter(this).round(); |
| $endif |
| |
| $!MEMBERS |
| } |
| |
| |
| class _ElementFactoryProvider { |
| |
| @DomName('Document.createElement') |
| $if DART2JS |
| // Optimization to improve performance until the dart2js compiler inlines this |
| // method. |
| static dynamic createElement_tag(String tag, String typeExtension) { |
| // Firefox may return a JS function for some types (Embed, Object). |
| if (typeExtension != null) { |
| return JS('Element|=Object', 'document.createElement(#, #)', |
| tag, typeExtension); |
| } |
| // Should be able to eliminate this and just call the two-arg version above |
| // with null typeExtension, but Chrome treats the tag as case-sensitive if |
| // typeExtension is null. |
| // https://code.google.com/p/chromium/issues/detail?id=282467 |
| return JS('Element|=Object', 'document.createElement(#)', tag); |
| } |
| |
| $else |
| static Element createElement_tag(String tag, String typeExtension) => |
| document.createElement(tag, typeExtension); |
| $endif |
| } |
| |
| |
| /** |
| * Options for Element.scrollIntoView. |
| */ |
| class ScrollAlignment { |
| final _value; |
| const ScrollAlignment._internal(this._value); |
| toString() => 'ScrollAlignment.$_value'; |
| |
| /// Attempt to align the element to the top of the scrollable area. |
| static const TOP = const ScrollAlignment._internal('TOP'); |
| /// Attempt to center the element in the scrollable area. |
| static const CENTER = const ScrollAlignment._internal('CENTER'); |
| /// Attempt to align the element to the bottom of the scrollable area. |
| static const BOTTOM = const ScrollAlignment._internal('BOTTOM'); |
| } |