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 <= index <= 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 }