All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.javascript.host.html.HTMLCollection Maven / Gradle / Ivy

Go to download

XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.

The newest version!
/*
 * Copyright (c) 2002-2024 Gargoyle Software Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.htmlunit.javascript.host.html;

import static org.htmlunit.BrowserVersionFeatures.HTMLCOLLECTION_ITEM_SUPPORTS_DOUBLE_INDEX_ALSO;
import static org.htmlunit.BrowserVersionFeatures.HTMLCOLLECTION_ITEM_SUPPORTS_ID_SEARCH_ALSO;
import static org.htmlunit.BrowserVersionFeatures.HTMLCOLLECTION_NAMED_ITEM_ID_FIRST;
import static org.htmlunit.BrowserVersionFeatures.HTMLCOLLECTION_NULL_IF_NOT_FOUND;
import static org.htmlunit.BrowserVersionFeatures.HTMLCOLLECTION_SUPPORTS_PARANTHESES;
import static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
import static org.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
import static org.htmlunit.javascript.configuration.SupportedBrowser.FF;
import static org.htmlunit.javascript.configuration.SupportedBrowser.FF_ESR;
import static org.htmlunit.javascript.configuration.SupportedBrowser.IE;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;

import org.htmlunit.BrowserVersion;
import org.htmlunit.corejs.javascript.Callable;
import org.htmlunit.corejs.javascript.Context;
import org.htmlunit.corejs.javascript.ES6Iterator;
import org.htmlunit.corejs.javascript.NativeArrayIterator;
import org.htmlunit.corejs.javascript.Scriptable;
import org.htmlunit.corejs.javascript.Undefined;
import org.htmlunit.html.DomElement;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.HtmlForm;
import org.htmlunit.html.HtmlInput;
import org.htmlunit.javascript.JavaScriptEngine;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxConstructor;
import org.htmlunit.javascript.configuration.JsxFunction;
import org.htmlunit.javascript.configuration.JsxGetter;
import org.htmlunit.javascript.configuration.JsxSymbol;
import org.htmlunit.javascript.host.dom.AbstractList;

/**
 * An array of elements. Used for the element arrays returned by document.all,
 * document.all.tags('x'), document.forms, window.frames, etc.
 * Note that this class must not be used for collections that can be modified, for example
 * map.areas and select.options.
 * 
* This class (like all classes in this package) is specific for the JavaScript engine. * Users of HtmlUnit shouldn't use it directly. * * @author Daniel Gredler * @author Marc Guillemot * @author Chris Erskine * @author Ahmed Ashour * @author Frank Danek * @author Ronald Brill */ @JsxClass public class HTMLCollection extends AbstractList implements Callable { /** * Creates an instance. */ public HTMLCollection() { } /** * JavaScript constructor. */ @JsxConstructor({CHROME, EDGE, FF, FF_ESR}) public void jsConstructor() { } /** * Creates an instance. * @param domNode parent scope * @param attributeChangeSensitive indicates if the content of the collection may change when an attribute * of a descendant node of parentScope changes (attribute added, modified or removed) */ public HTMLCollection(final DomNode domNode, final boolean attributeChangeSensitive) { super(domNode, attributeChangeSensitive, null); } /** * Constructs an instance with an initial cache value. * @param domNode the parent scope, on which we listen for changes * @param initialElements the initial content for the cache */ HTMLCollection(final DomNode domNode, final List initialElements) { super(domNode, true, new ArrayList<>(initialElements)); } private HTMLCollection(final DomNode domNode, final boolean attributeChangeSensitive, final List initialElements) { super(domNode, attributeChangeSensitive, new ArrayList<>(initialElements)); } /** * Gets an empty collection. * @param domNode the DOM node * @return an empty collection */ public static HTMLCollection emptyCollection(final DomNode domNode) { return new HTMLCollection(domNode, false, Collections.emptyList()); } /** * {@inheritDoc} */ @Override protected HTMLCollection create(final DomNode parentScope, final List initialElements) { return new HTMLCollection(parentScope, initialElements); } @JsxSymbol({CHROME, EDGE, FF, FF_ESR}) public ES6Iterator iterator() { return new NativeArrayIterator(getParentScope(), this, NativeArrayIterator.ARRAY_ITERATOR_TYPE.VALUES); } /** * Returns the length. * @return the length */ @JsxGetter @Override public final int getLength() { return super.getLength(); } /** * {@inheritDoc} */ @Override public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args) { if (supportsParentheses()) { if (args.length == 0) { throw JavaScriptEngine.reportRuntimeError("Zero arguments; need an index or a key."); } final Object object = getIt(args[0]); if (object == NOT_FOUND) { if (getBrowserVersion().hasFeature(HTMLCOLLECTION_NULL_IF_NOT_FOUND)) { return null; } return Undefined.instance; } return object; } throw JavaScriptEngine.typeError("HTMLCollection does nont support function like access"); } /** * Is parentheses supported. * * @return true or false */ protected boolean supportsParentheses() { return getBrowserVersion().hasFeature(HTMLCOLLECTION_SUPPORTS_PARANTHESES); } /** * {@inheritDoc} */ @Override protected Object getWithPreemptionByName(final String name, final List elements) { final List matchingElements = new ArrayList<>(); final boolean searchName = isGetWithPreemptionSearchName(); for (final DomNode next : elements) { if (next instanceof DomElement && (searchName || next instanceof HtmlInput || next instanceof HtmlForm)) { final String nodeName = ((DomElement) next).getAttributeDirect(DomElement.NAME_ATTRIBUTE); if (name.equals(nodeName)) { matchingElements.add(next); } } } if (matchingElements.isEmpty()) { if (getBrowserVersion().hasFeature(HTMLCOLLECTION_ITEM_SUPPORTS_DOUBLE_INDEX_ALSO)) { final double doubleValue = JavaScriptEngine.toNumber(name); if (!Double.isNaN(doubleValue)) { return get((int) doubleValue, this); } } return NOT_FOUND; } else if (matchingElements.size() == 1) { return getScriptableForElement(matchingElements.get(0)); } // many elements => build a sub collection final DomNode domNode = getDomNodeOrNull(); final HTMLCollection collection = new HTMLCollection(domNode, matchingElements); collection.setAvoidObjectDetection(true); return collection; } /** * Returns whether {@link #getWithPreemption(String)} should search by name or not. * @return whether {@link #getWithPreemption(String)} should search by name or not */ protected boolean isGetWithPreemptionSearchName() { return true; } /** * Returns the item or items corresponding to the specified index or key. * @param index the index or key corresponding to the element or elements to return * @return the element or elements corresponding to the specified index or key * @see MSDN doc */ @JsxFunction public Object item(final Object index) { if (index instanceof String && getBrowserVersion().hasFeature(HTMLCOLLECTION_ITEM_SUPPORTS_ID_SEARCH_ALSO)) { final String name = (String) index; return namedItem(name); } int idx = 0; final double doubleValue = JavaScriptEngine.toNumber(index); if (!Double.isNaN(doubleValue)) { idx = (int) doubleValue; } final Object object = get(idx, this); if (object == NOT_FOUND) { return null; } return object; } /** * Retrieves the item or items corresponding to the specified name (checks ids, and if * that does not work, then names). * @param name the name or id the element or elements to return * @return the element or elements corresponding to the specified name or id * @see MSDN doc */ @JsxFunction public Object namedItem(final String name) { final List elements = getElements(); final BrowserVersion browserVersion = getBrowserVersion(); if (browserVersion.hasFeature(HTMLCOLLECTION_NAMED_ITEM_ID_FIRST)) { for (final Object next : elements) { if (next instanceof DomElement) { final DomElement elem = (DomElement) next; final String id = elem.getId(); if (name.equals(id)) { return getScriptableForElement(elem); } } } } for (final Object next : elements) { if (next instanceof DomElement) { final DomElement elem = (DomElement) next; final String nodeName = elem.getAttributeDirect(DomElement.NAME_ATTRIBUTE); if (name.equals(nodeName)) { return getScriptableForElement(elem); } final String id = elem.getId(); if (name.equals(id)) { return getScriptableForElement(elem); } } } return null; } /** * Returns all the elements in this element array that have the specified tag name. * This method returns an empty element array if there are no elements with the * specified tag name. * @param tagName the name of the tag of the elements to return * @return all the elements in this element array that have the specified tag name * @see MSDN doc */ @JsxFunction(IE) public Object tags(final String tagName) { final HTMLCollection tags = new HTMLCollection(getDomNodeOrDie(), false); tags.setElementsSupplier( (Supplier> & Serializable) () -> { final List list = new ArrayList<>(); for (final DomNode elem : this.getElements()) { if (tagName.equalsIgnoreCase(elem.getLocalName())) { list.add(elem); } } return list; }); return tags; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy