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