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