View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.html;
16  
17  import java.io.Serializable;
18  import java.util.function.Predicate;
19  
20  import org.htmlunit.html.DomElement;
21  import org.htmlunit.html.DomNode;
22  import org.htmlunit.html.HtmlPage;
23  import org.htmlunit.html.HtmlTableBody;
24  import org.htmlunit.html.HtmlTableFooter;
25  import org.htmlunit.html.HtmlTableHeader;
26  import org.htmlunit.html.HtmlTableRow;
27  import org.htmlunit.javascript.HtmlUnitScriptable;
28  import org.htmlunit.javascript.JavaScriptEngine;
29  import org.htmlunit.javascript.configuration.JsxClass;
30  import org.htmlunit.javascript.configuration.JsxConstructor;
31  import org.htmlunit.javascript.configuration.JsxFunction;
32  import org.htmlunit.javascript.configuration.JsxGetter;
33  import org.htmlunit.javascript.configuration.JsxSetter;
34  import org.htmlunit.javascript.host.dom.DOMException;
35  
36  /**
37   * A JavaScript object representing "HTMLTableSectionElement", it is used by
38   * {@link HtmlTableBody}, {@link HtmlTableHeader}, and {@link HtmlTableFooter}.
39   *
40   * @author Daniel Gredler
41   * @author Ahmed Ashour
42   * @author Ronald Brill
43   *
44   * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableSectionElement">MDN Documentation</a>
45   */
46  @JsxClass(domClass = HtmlTableBody.class)
47  @JsxClass(domClass = HtmlTableHeader.class)
48  @JsxClass(domClass = HtmlTableFooter.class)
49  public class HTMLTableSectionElement extends HTMLElement {
50  
51      /** The default value of the "vAlign" property. */
52      private static final String VALIGN_DEFAULT_VALUE = "top";
53  
54      /**
55       * JavaScript constructor.
56       */
57      @Override
58      @JsxConstructor
59      public void jsConstructor() {
60          super.jsConstructor();
61      }
62  
63      /**
64       * Returns the value of the {@code vAlign} property.
65       * @return the value of the {@code vAlign} property
66       */
67      @JsxGetter
68      public String getVAlign() {
69          return getVAlign(getValidVAlignValues(), VALIGN_DEFAULT_VALUE);
70      }
71  
72      /**
73       * Sets the value of the {@code vAlign} property.
74       * @param vAlign the value of the {@code vAlign} property
75       */
76      @JsxSetter
77      public void setVAlign(final Object vAlign) {
78          setVAlign(vAlign, getValidVAlignValues());
79      }
80  
81      /**
82       * Returns the valid "vAlign" values for this element, depending on the browser being emulated.
83       * @return the valid "vAlign" values for this element, depending on the browser being emulated
84       */
85      private String[] getValidVAlignValues() {
86          return null;
87      }
88  
89      /**
90       * Returns the value of the {@code ch} property.
91       * @return the value of the {@code ch} property
92       */
93      @Override
94      @JsxGetter
95      public String getCh() {
96          return super.getCh();
97      }
98  
99      /**
100      * Sets the value of the {@code ch} property.
101      * @param ch the value of the {@code ch} property
102      */
103     @Override
104     @JsxSetter
105     public void setCh(final String ch) {
106         super.setCh(ch);
107     }
108 
109     /**
110      * Returns the value of the {@code chOff} property.
111      * @return the value of the {@code chOff} property
112      */
113     @Override
114     @JsxGetter
115     public String getChOff() {
116         return super.getChOff();
117     }
118 
119     /**
120      * Sets the value of the {@code chOff} property.
121      * @param chOff the value of the {@code chOff} property
122      */
123     @Override
124     @JsxSetter
125     public void setChOff(final String chOff) {
126         super.setChOff(chOff);
127     }
128 
129     /**
130      * Returns the rows in the element.
131      * @return the rows in the element
132      */
133     @JsxGetter
134     public HTMLCollection getRows() {
135         final HTMLCollection rows = new HTMLCollection(getDomNodeOrDie(), false);
136         rows.setIsMatchingPredicate(
137                 (Predicate<DomNode> & Serializable)
138                 node -> node instanceof HtmlTableRow htr && isContainedRow(htr));
139         return rows;
140     }
141 
142     /**
143      * Indicates if the row belongs to this container.
144      * @param row the row to test
145      * @return {@code true} if it belongs to this container
146      */
147     protected boolean isContainedRow(final HtmlTableRow row) {
148         return row.getParentNode() == getDomNodeOrDie();
149     }
150 
151     /**
152      * Deletes the row at the specified index.
153      * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableSectionElement/deleteRow">MDN Documentation</a>
154      * @param rowIndex the zero-based index of the row to delete
155      */
156     @JsxFunction
157     public void deleteRow(int rowIndex) {
158         final HTMLCollection rows = getRows();
159         final int rowCount = rows.getLength();
160         if (rowIndex == -1) {
161             rowIndex = rowCount - 1;
162         }
163         final boolean rowIndexValid = rowIndex >= 0 && rowIndex < rowCount;
164         if (rowIndexValid) {
165             final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(rowIndex));
166             row.getDomNodeOrDie().remove();
167         }
168     }
169 
170     /**
171      * Inserts a new row at the specified index in the element's row collection. If the index
172      * is -1 or there is no index specified, then the row is appended at the end of the
173      * element's row collection.
174      * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableSectionElement/insertRow">MDN Documentation</a>
175      * @param index specifies where to insert the row in the row's collection.
176      *        The default value is -1, which appends the new row to the end of the rows collection
177      * @return the newly-created row
178      */
179     @JsxFunction
180     public HtmlUnitScriptable insertRow(final Object index) {
181         int rowIndex = -1;
182         if (!JavaScriptEngine.isUndefined(index)) {
183             rowIndex = (int) JavaScriptEngine.toNumber(index);
184         }
185         final HTMLCollection rows = getRows();
186         final int rowCount = rows.getLength();
187         final int r;
188         if (rowIndex == -1 || rowIndex == rowCount) {
189             r = Math.max(0, rowCount);
190         }
191         else {
192             r = rowIndex;
193         }
194 
195         if (r < 0 || r > rowCount) {
196             throw JavaScriptEngine.asJavaScriptException(
197                     getWindow(),
198                     "Index or size is negative or greater than the allowed amount "
199                             + "(index: " + rowIndex + ", " + rowCount + " rows)",
200                     DOMException.INDEX_SIZE_ERR);
201         }
202 
203         return insertRow(r);
204     }
205 
206     /**
207      * Inserts a new row at the given position.
208      * @param index the index where the row should be inserted (0 &lt;= index &lt;= nbRows)
209      * @return the inserted row
210      */
211     public HtmlUnitScriptable insertRow(final int index) {
212         final HTMLCollection rows = getRows();
213         final int rowCount = rows.getLength();
214         final DomElement newRow = ((HtmlPage) getDomNodeOrDie().getPage()).createElement("tr");
215         if (rowCount == 0) {
216             getDomNodeOrDie().appendChild(newRow);
217         }
218         else if (index == rowCount) {
219             final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(index - 1));
220             row.getDomNodeOrDie().getParentNode().appendChild(newRow);
221         }
222         else {
223             final HtmlUnitScriptable row = (HtmlUnitScriptable) rows.item(Integer.valueOf(index));
224             // if at the end, then in the same "sub-container" as the last existing row
225             if (index > rowCount - 1) {
226                 row.getDomNodeOrDie().getParentNode().appendChild(newRow);
227             }
228             else {
229                 row.getDomNodeOrDie().insertBefore(newRow);
230             }
231         }
232         return getScriptableFor(newRow);
233     }
234 
235     /**
236      * Returns the value of the {@code align} property.
237      * @return the value of the {@code align} property
238      */
239     @JsxGetter
240     public String getAlign() {
241         return getAlign(true);
242     }
243 
244     /**
245      * Sets the value of the {@code align} property.
246      * @param align the value of the {@code align} property
247      */
248     @JsxSetter
249     public void setAlign(final String align) {
250         setAlign(align, false);
251     }
252 }