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 org.htmlunit.css.ComputedCssStyleDeclaration;
18 import org.htmlunit.css.StyleAttributes;
19 import org.htmlunit.html.DomNode;
20 import org.htmlunit.html.HtmlTableCell;
21 import org.htmlunit.html.HtmlTableRow;
22 import org.htmlunit.javascript.JavaScriptEngine;
23 import org.htmlunit.javascript.configuration.JsxClass;
24 import org.htmlunit.javascript.configuration.JsxConstructor;
25 import org.htmlunit.javascript.configuration.JsxGetter;
26 import org.htmlunit.javascript.configuration.JsxSetter;
27 import org.htmlunit.javascript.host.event.MouseEvent;
28
29 /**
30 * The JavaScript object representing a TD or TH.
31 *
32 * @author Mark van Leeuwen
33 * @author Ahmed Ashour
34 * @author Sudhan Moghe
35 * @author Daniel Gredler
36 * @author Ronald Brill
37 * @author Frank Danek
38 * @author Lai Quang Duong
39 *
40 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement">MDN Documentation</a>
41 */
42 @JsxClass(domClass = HtmlTableCell.class)
43 public class HTMLTableCellElement extends HTMLElement {
44
45 /** The default value of the "vAlign" property. */
46 private static final String VALIGN_DEFAULT_VALUE = "top";
47
48 /**
49 * JavaScript constructor.
50 */
51 @Override
52 @JsxConstructor
53 public void jsConstructor() {
54 super.jsConstructor();
55 }
56
57 /**
58 * {@inheritDoc}
59 */
60 @Override
61 public int getOffsetHeight() {
62 final MouseEvent event = MouseEvent.getCurrentMouseEvent();
63 if (isAncestorOfEventTarget(event)) {
64 return super.getOffsetHeight();
65 }
66
67 if (isDisplayNone()) {
68 return 0;
69 }
70 final ComputedCssStyleDeclaration style = getWindow().getWebWindow().getComputedStyle(getDomNodeOrDie(), null);
71 return style.getCalculatedHeight(false, true);
72 }
73
74 /**
75 * {@inheritDoc}
76 */
77 @Override
78 public int getOffsetWidth() {
79 float w = super.getOffsetWidth();
80 final MouseEvent event = MouseEvent.getCurrentMouseEvent();
81 if (isAncestorOfEventTarget(event)) {
82 return (int) w;
83 }
84
85 if (isDisplayNone()) {
86 return 0;
87 }
88
89 final ComputedCssStyleDeclaration style = getWindow().getWebWindow().getComputedStyle(getDomNodeOrDie(), null);
90 if ("collapse".equals(style.getStyleAttribute(StyleAttributes.Definition.BORDER_COLLAPSE, true))) {
91 final HtmlTableRow row = getRow();
92 if (row != null) {
93 w -= 0.5f * style.getBorderLeftValue();
94 w -= 0.5f * style.getBorderRightValue();
95 }
96 }
97
98 return (int) w;
99 }
100
101 /**
102 * Returns the index of this cell within the parent row.
103 * @return the index of this cell within the parent row
104 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement/cellIndex">MDN Documentation</a>
105 */
106 @JsxGetter
107 public int getCellIndex() {
108 final HtmlTableCell cell = (HtmlTableCell) getDomNodeOrDie();
109 final HtmlTableRow row = cell.getEnclosingRow();
110 if (row == null) { // a not attached document.createElement('TD')
111 return Integer.valueOf(-1);
112 }
113 return Integer.valueOf(row.getCells().indexOf(cell));
114 }
115
116 /**
117 * Returns the value of the {@code abbr} attribute.
118 * @return the value of the {@code abbr} attribute
119 */
120 @JsxGetter
121 public String getAbbr() {
122 return getDomNodeOrDie().getAttributeDirect("abbr");
123 }
124
125 /**
126 * Sets the value of the {@code abbr} attribute.
127 * @param abbr the value of the {@code abbr} attribute
128 */
129 @JsxSetter
130 public void setAbbr(final String abbr) {
131 getDomNodeOrDie().setAttribute("abbr", abbr);
132 }
133
134 /**
135 * Returns the value of the {@code axis} attribute.
136 * @return the value of the {@code axis} attribute
137 */
138 @JsxGetter
139 public String getAxis() {
140 return getDomNodeOrDie().getAttributeDirect("axis");
141 }
142
143 /**
144 * Sets the value of the {@code axis} attribute.
145 * @param axis the value of the {@code axis} attribute
146 */
147 @JsxSetter
148 public void setAxis(final String axis) {
149 getDomNodeOrDie().setAttribute("axis", axis);
150 }
151
152 /**
153 * Returns the value of the {@code bgColor} attribute.
154 * @return the value of the {@code bgColor} attribute
155 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement#bgcolor">MDN Documentation</a>
156 */
157 @JsxGetter
158 public String getBgColor() {
159 return getDomNodeOrDie().getAttribute("bgColor");
160 }
161
162 /**
163 * Sets the value of the {@code bgColor} attribute.
164 * @param bgColor the value of the {@code bgColor} attribute
165 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement#bgcolor">MDN Documentation</a>
166 */
167 @JsxSetter
168 public void setBgColor(final String bgColor) {
169 setColorAttribute("bgColor", bgColor);
170 }
171
172 /**
173 * Returns the value of the {@code colSpan} attribute.
174 * @return the value of the {@code colSpan} attribute
175 */
176 @JsxGetter
177 public int getColSpan() {
178 return ((HtmlTableCell) getDomNodeOrDie()).getColumnSpan();
179 }
180
181 /**
182 * Sets the value of the {@code colSpan} attribute.
183 * @param colSpan the value of the {@code colSpan} attribute
184 */
185 @JsxSetter
186 public void setColSpan(final String colSpan) {
187 try {
188 final int i = (int) Double.parseDouble(colSpan);
189 if (i <= 0) {
190 throw new NumberFormatException(colSpan);
191 }
192 getDomNodeOrDie().setAttribute("colSpan", Integer.toString(i));
193 }
194 catch (final NumberFormatException e) {
195 getDomNodeOrDie().setAttribute("colSpan", "1");
196 }
197 }
198
199 /**
200 * Returns the value of the {@code rowSpan} attribute.
201 * @return the value of the {@code rowSpan} attribute
202 */
203 @JsxGetter
204 public int getRowSpan() {
205 return ((HtmlTableCell) getDomNodeOrDie()).getRowSpan();
206 }
207
208 /**
209 * Sets the value of the {@code rowSpan} attribute.
210 * @param rowSpan the value of the {@code rowSpan} attribute
211 */
212 @JsxSetter
213 public void setRowSpan(final String rowSpan) {
214 try {
215 final int i = (int) Double.parseDouble(rowSpan);
216 if (i < 0) {
217 getDomNodeOrDie().setAttribute("rowSpan", "1");
218 return;
219 }
220 if (i == 0) {
221 throw new NumberFormatException(rowSpan);
222 }
223 getDomNodeOrDie().setAttribute("rowSpan", Integer.toString(i));
224 }
225 catch (final NumberFormatException e) {
226 getDomNodeOrDie().setAttribute("rowSpan", "0");
227 }
228 }
229
230 /**
231 * Returns the value of the {@code noWrap} attribute.
232 * @return the value of the {@code noWrap} attribute
233 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement#nowrap">MDN Documentation</a>
234 */
235 @JsxGetter
236 public boolean isNoWrap() {
237 return getDomNodeOrDie().hasAttribute("noWrap");
238 }
239
240 /**
241 * Sets the value of the {@code noWrap} attribute.
242 * @param noWrap the value of the {@code noWrap} attribute
243 * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement#nowrap">MDN Documentation</a>
244 */
245 @JsxSetter
246 public void setNoWrap(final boolean noWrap) {
247 if (noWrap) {
248 getDomNodeOrDie().setAttribute("noWrap", "");
249 }
250 else {
251 getDomNodeOrDie().removeAttribute("noWrap");
252 }
253 }
254
255 /**
256 * Returns the row element which contains this cell's HTML element; may return {@code null}.
257 * @return the row element which contains this cell's HTML element
258 */
259 private HtmlTableRow getRow() {
260 DomNode node = getDomNodeOrDie();
261 while (node != null && !(node instanceof HtmlTableRow)) {
262 node = node.getParentNode();
263 }
264 return (HtmlTableRow) node;
265 }
266
267 /**
268 * Returns the value of the {@code width} property.
269 * @return the value of the {@code width} property
270 */
271 @JsxGetter(propertyName = "width")
272 public String getWidth_js() {
273 return getWidthOrHeight("width", null);
274 }
275
276 /**
277 * Sets the value of the {@code width} property.
278 * @param width the value of the {@code width} property
279 */
280 @JsxSetter(propertyName = "width")
281 public void setWidth_js(final String width) {
282 setWidthOrHeight("width", width, true);
283 }
284
285 /**
286 * Returns the value of the {@code height} property.
287 * @return the value of the {@code height} property
288 */
289 @JsxGetter(propertyName = "height")
290 public String getHeight_js() {
291 return getWidthOrHeight("height", null);
292 }
293
294 /**
295 * Sets the value of the {@code height} property.
296 * @param height the value of the {@code height} property
297 */
298 @JsxSetter(propertyName = "height")
299 public void setHeight_js(final String height) {
300 setWidthOrHeight("height", height, true);
301 }
302
303 /**
304 * Overwritten to throw an exception.
305 * @param value the new value for replacing this node
306 */
307 @Override
308 public void setOuterHTML(final Object value) {
309 throw JavaScriptEngine.reportRuntimeError("outerHTML is read-only for tag '"
310 + getDomNodeOrDie().getTagName() + "'");
311 }
312
313 /**
314 * Returns the {@code headers} attribute.
315 * @return the {@code headers} attribute
316 */
317 @JsxGetter
318 public String getHeaders() {
319 return getDomNodeOrDie().getAttributeDirect("headers");
320 }
321
322 /**
323 * Sets the {@code headers} attribute.
324 * @param headers the new {@code headers} attribute value
325 */
326 @JsxSetter
327 public void setHeaders(final String headers) {
328 getDomNodeOrDie().setAttribute("headers", headers);
329 }
330
331 /**
332 * Returns the {@code scope} attribute.
333 * @return the {@code scope} attribute
334 */
335 @JsxGetter
336 public String getScope() {
337 return getDomNodeOrDie().getAttributeDirect("scope");
338 }
339
340 /**
341 * Sets the {@code scope} attribute.
342 * @param scope the new {@code scope} attribute value
343 */
344 @JsxSetter
345 public void setScope(final String scope) {
346 getDomNodeOrDie().setAttribute("scope", scope);
347 }
348
349 /**
350 * Returns the value of the {@code align} property.
351 * @return the value of the {@code align} property
352 */
353 @JsxGetter
354 public String getAlign() {
355 return getAlign(true);
356 }
357
358 /**
359 * Sets the value of the {@code align} property.
360 * @param align the value of the {@code align} property
361 */
362 @JsxSetter
363 public void setAlign(final String align) {
364 setAlign(align, false);
365 }
366
367 /**
368 * Returns the value of the {@code vAlign} property.
369 * @return the value of the {@code vAlign} property
370 */
371 @JsxGetter
372 public String getVAlign() {
373 return getVAlign(getValidVAlignValues(), VALIGN_DEFAULT_VALUE);
374 }
375
376 /**
377 * Sets the value of the {@code vAlign} property.
378 * @param vAlign the value of the {@code vAlign} property
379 */
380 @JsxSetter
381 public void setVAlign(final Object vAlign) {
382 setVAlign(vAlign, getValidVAlignValues());
383 }
384
385 /**
386 * Returns the valid "vAlign" values for this element, depending on the browser being emulated.
387 * @return the valid "vAlign" values for this element, depending on the browser being emulated
388 */
389 private String[] getValidVAlignValues() {
390 return null;
391 }
392
393 /**
394 * Returns the value of the {@code ch} property.
395 * @return the value of the {@code ch} property
396 */
397 @Override
398 @JsxGetter
399 public String getCh() {
400 return super.getCh();
401 }
402
403 /**
404 * Sets the value of the {@code ch} property.
405 * @param ch the value of the {@code ch} property
406 */
407 @Override
408 @JsxSetter
409 public void setCh(final String ch) {
410 super.setCh(ch);
411 }
412
413 /**
414 * Returns the value of the {@code chOff} property.
415 * @return the value of the {@code chOff} property
416 */
417 @Override
418 @JsxGetter
419 public String getChOff() {
420 return super.getChOff();
421 }
422
423 /**
424 * Sets the value of the {@code chOff} property.
425 * @param chOff the value of the {@code chOff} property
426 */
427 @Override
428 @JsxSetter
429 public void setChOff(final String chOff) {
430 super.setChOff(chOff);
431 }
432 }