0001: /*
0002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * 1. Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: * 2. Redistributions in binary form must reproduce the above copyright notice,
0010: * this list of conditions and the following disclaimer in the documentation
0011: * and/or other materials provided with the distribution.
0012: * 3. The end-user documentation included with the redistribution, if any, must
0013: * include the following acknowledgment:
0014: *
0015: * "This product includes software developed by Gargoyle Software Inc.
0016: * (http://www.GargoyleSoftware.com/)."
0017: *
0018: * Alternately, this acknowledgment may appear in the software itself, if
0019: * and wherever such third-party acknowledgments normally appear.
0020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
0021: * products derived from this software without prior written permission.
0022: * For written permission, please contact info@GargoyleSoftware.com.
0023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
0024: * "HtmlUnit" appear in their name, without prior written permission of
0025: * Gargoyle Software Inc.
0026: *
0027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
0028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
0030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
0033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0037: */
0038: package com.gargoylesoftware.htmlunit.javascript.host;
0039:
0040: import java.io.IOException;
0041: import java.lang.reflect.InvocationTargetException;
0042: import java.lang.reflect.Method;
0043: import java.net.MalformedURLException;
0044: import java.net.URL;
0045: import java.util.Arrays;
0046: import java.util.HashSet;
0047: import java.util.Iterator;
0048: import java.util.List;
0049: import java.util.Set;
0050:
0051: import org.apache.commons.lang.StringUtils;
0052: import org.jaxen.JaxenException;
0053: import org.jaxen.XPath;
0054: import org.mozilla.javascript.BaseFunction;
0055: import org.mozilla.javascript.Context;
0056: import org.mozilla.javascript.ContextAction;
0057: import org.mozilla.javascript.Function;
0058: import org.mozilla.javascript.NativeArray;
0059: import org.mozilla.javascript.Scriptable;
0060: import org.xml.sax.SAXException;
0061: import org.xml.sax.helpers.AttributesImpl;
0062:
0063: import com.gargoylesoftware.htmlunit.BrowserVersion;
0064: import com.gargoylesoftware.htmlunit.ScriptResult;
0065: import com.gargoylesoftware.htmlunit.WebClient;
0066: import com.gargoylesoftware.htmlunit.WebRequestSettings;
0067: import com.gargoylesoftware.htmlunit.WebResponse;
0068: import com.gargoylesoftware.htmlunit.html.ClickableElement;
0069: import com.gargoylesoftware.htmlunit.html.DomCharacterData;
0070: import com.gargoylesoftware.htmlunit.html.DomComment;
0071: import com.gargoylesoftware.htmlunit.html.DomDocumentFragment;
0072: import com.gargoylesoftware.htmlunit.html.DomNode;
0073: import com.gargoylesoftware.htmlunit.html.DomText;
0074: import com.gargoylesoftware.htmlunit.html.HTMLParser;
0075: import com.gargoylesoftware.htmlunit.html.HtmlAttr;
0076: import com.gargoylesoftware.htmlunit.html.HtmlBody;
0077: import com.gargoylesoftware.htmlunit.html.HtmlDivision;
0078: import com.gargoylesoftware.htmlunit.html.HtmlElement;
0079: import com.gargoylesoftware.htmlunit.html.HtmlFrameSet;
0080: import com.gargoylesoftware.htmlunit.html.HtmlPage;
0081: import com.gargoylesoftware.htmlunit.html.HtmlTable;
0082: import com.gargoylesoftware.htmlunit.html.HtmlTableDataCell;
0083: import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath;
0084: import com.gargoylesoftware.htmlunit.javascript.HTMLCollection;
0085: import com.gargoylesoftware.htmlunit.javascript.NamedNodeMap;
0086: import com.gargoylesoftware.htmlunit.javascript.ScriptableWithFallbackGetter;
0087:
0088: /**
0089: * The javascript object "HTMLElement" which is the base class for all HTML
0090: * objects. This will typically wrap an instance of {@link HtmlElement}.
0091: *
0092: * @version $Revision: 2155 $
0093: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
0094: * @author David K. Taylor
0095: * @author Barnaby Court
0096: * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
0097: * @author Chris Erskine
0098: * @author David D. Kilzer
0099: * @author Daniel Gredler
0100: * @author Marc Guillemot
0101: * @author Hans Donner
0102: * @author Bruce Faulkner
0103: * @author Ahmed Ashour
0104: */
0105: public class HTMLElement extends Element implements
0106: ScriptableWithFallbackGetter {
0107:
0108: private static final long serialVersionUID = -6864034414262085851L;
0109: private static final int BEHAVIOR_ID_UNKNOWN = -1;
0110: private static final int BEHAVIOR_ID_CLIENT_CAPS = 0;
0111: private static final int BEHAVIOR_ID_HOMEPAGE = 1;
0112: private static final int BEHAVIOR_ID_DOWNLOAD = 2;
0113: private static final String BEHAVIOR_CLIENT_CAPS = "#default#clientCaps";
0114: private static final String BEHAVIOR_HOMEPAGE = "#default#homePage";
0115: private static final String BEHAVIOR_DOWNLOAD = "#default#download";
0116: static final String POSITION_BEFORE_BEGIN = "beforeBegin";
0117: static final String POSITION_AFTER_BEGIN = "afterBegin";
0118: static final String POSITION_BEFORE_END = "beforeEnd";
0119: static final String POSITION_AFTER_END = "afterEnd";
0120:
0121: /**
0122: * Static counter for {@link #uniqueID_}.
0123: */
0124: private static int UniqueID_Counter_ = 1;
0125:
0126: private final Set behaviors_ = new HashSet();
0127: private BoxObject boxObject_; // lazy init
0128: private HTMLCollection all_; // has to be a member to have equality (==) working
0129: private int scrollLeft_;
0130: private int scrollTop_;
0131: private String uniqueID_;
0132:
0133: /**
0134: * The tag names of the objects for which outerHTML is readonly
0135: */
0136: private static final List OUTER_HTML_READONLY = Arrays
0137: .asList(new String[] { "caption", "col", "colgroup",
0138: "frameset", "html", "tbody", "td", "tfoot", "th",
0139: "thead", "tr" });
0140:
0141: private Style style_;
0142:
0143: /**
0144: * Create an instance.
0145: */
0146: public HTMLElement() {
0147: // Empty.
0148: }
0149:
0150: /**
0151: * Return the value of the "all" property.
0152: * @return The value of the "all" property
0153: */
0154: public HTMLCollection jsxGet_all() {
0155: if (all_ == null) {
0156: all_ = new HTMLCollection(this );
0157: try {
0158: all_.init(getDomNodeOrDie(), new HtmlUnitXPath(".//*"));
0159: } catch (final JaxenException e) {
0160: throw Context
0161: .reportRuntimeError("Failed to initialize collection all: "
0162: + e.getMessage());
0163: }
0164: }
0165: return all_;
0166: }
0167:
0168: /**
0169: * Return the style object for this element.
0170: *
0171: * @return The style object
0172: */
0173: public Object jsxGet_style() {
0174: return style_;
0175: }
0176:
0177: /**
0178: * Returns the current style object for this element.
0179: * @return The current style object
0180: */
0181: public Object jsxGet_currentStyle() {
0182: return style_;
0183: }
0184:
0185: /**
0186: * Returns the runtime style object for this element.
0187: * @return The runtime style object
0188: */
0189: public Object jsxGet_runtimeStyle() {
0190: return style_;
0191: }
0192:
0193: /**
0194: * Set the DOM node that corresponds to this javascript object
0195: * @param domNode The DOM node
0196: */
0197: public void setDomNode(final DomNode domNode) {
0198: super .setDomNode(domNode);
0199:
0200: style_ = new Style(this );
0201:
0202: /**
0203: * Convert javascript snippets defined in the attribute map to executable event handlers.
0204: * Should be called only on construction.
0205: */
0206: final HtmlElement htmlElt = (HtmlElement) domNode;
0207: for (final Iterator iter = htmlElt
0208: .getAttributeEntriesIterator(); iter.hasNext();) {
0209: final HtmlAttr entry = (HtmlAttr) iter.next();
0210: final String eventName = entry.getName();
0211: if (eventName.startsWith("on")) {
0212: // TODO: check that it is an "allowed" event for the browser, and take care to the case
0213: final BaseFunction eventHandler = new EventHandler(
0214: htmlElt, eventName, (String) entry
0215: .getHtmlValue());
0216: setEventHandler(eventName, eventHandler);
0217: // forward onload, onclick, ondblclick, ... to window
0218: if ((domNode instanceof HtmlBody || domNode instanceof HtmlFrameSet)) {
0219: getWindow().getEventListenersContainer()
0220: .setEventHandlerProp(
0221: eventName.substring(2),
0222: eventHandler);
0223: }
0224: }
0225: }
0226: }
0227:
0228: /**
0229: * Return the element ID.
0230: * @return The ID of this element.
0231: */
0232: public String jsxGet_id() {
0233: return getHtmlElementOrDie().getId();
0234: }
0235:
0236: /**
0237: * Set the identifier this element.
0238: * @param newId The new identifier of this element.
0239: */
0240: public void jsxSet_id(final String newId) {
0241: getHtmlElementOrDie().setId(newId);
0242: }
0243:
0244: /**
0245: * Return the element title.
0246: * @return The ID of this element.
0247: */
0248: public String jsxGet_title() {
0249: return getHtmlElementOrDie().getAttributeValue("title");
0250: }
0251:
0252: /**
0253: * Set the title of this element.
0254: * @param newTitle The new identifier of this element.
0255: */
0256: public void jsxSet_title(final String newTitle) {
0257: getHtmlElementOrDie().setAttributeValue("title", newTitle);
0258: }
0259:
0260: /**
0261: * Return true if this element is disabled.
0262: * @return True if this element is disabled.
0263: */
0264: public boolean jsxGet_disabled() {
0265: return getHtmlElementOrDie().isAttributeDefined("disabled");
0266: }
0267:
0268: /**
0269: * Set whether or not to disable this element
0270: * @param disabled True if this is to be disabled.
0271: */
0272: public void jsxSet_disabled(final boolean disabled) {
0273: final HtmlElement element = getHtmlElementOrDie();
0274: if (disabled) {
0275: element.setAttributeValue("disabled", "disabled");
0276: } else {
0277: element.removeAttribute("disabled");
0278: }
0279: }
0280:
0281: /**
0282: * Return the tag name of this element.
0283: * @return The tag name in uppercase.
0284: */
0285: public String jsxGet_tagName() {
0286: String tagName = getHtmlElementOrDie().getTagName();
0287: if (jsxGet_namespaceURI() == null) {
0288: tagName = tagName.toUpperCase();
0289: }
0290: return tagName;
0291: }
0292:
0293: /**
0294: * Returns The URI that identifies an XML namespace.
0295: * @return The URI that identifies an XML namespace.
0296: */
0297: public String jsxGet_namespaceURI() {
0298: return getHtmlElementOrDie().getNamespaceURI();
0299: }
0300:
0301: /**
0302: * Returns The local name (without prefix).
0303: * @return The local name (without prefix).
0304: */
0305: public String jsxGet_localName() {
0306: String localName = getHtmlElementOrDie().getLocalName();
0307: if (jsxGet_namespaceURI() == null) {
0308: localName = localName.toUpperCase();
0309: }
0310: return localName;
0311: }
0312:
0313: /**
0314: * Returns The Namespace prefix
0315: * @return The Namespace prefix.
0316: */
0317: public String jsxGet_prefix() {
0318: return getHtmlElementOrDie().getPrefix();
0319: }
0320:
0321: /**
0322: * Return the owner document
0323: * @return the document
0324: */
0325: public Object jsxGet_ownerDocument() {
0326: return getWindow().jsxGet_document();
0327: }
0328:
0329: /**
0330: * Looks at attributes with the given name
0331: * {@inheritDoc}
0332: */
0333: public Object getWithFallback(final String name) {
0334: final HtmlElement htmlElement = getHtmlElementOrNull();
0335: // can name be an attribute of current element?
0336: // first approximation: attribute are all lowercase
0337: // this should be improved because it's wrong. For instance: tabIndex, hideFocus, acceptCharset
0338: if (htmlElement != null && name.toLowerCase().equals(name)) {
0339: final String value = htmlElement.getAttributeValue(name);
0340: if (HtmlElement.ATTRIBUTE_NOT_DEFINED != value) {
0341: getLog().debug(
0342: "Found attribute for evaluation of property \""
0343: + name + "\" for of " + this );
0344: return value;
0345: }
0346: }
0347:
0348: return NOT_FOUND;
0349: }
0350:
0351: /**
0352: * Returns a collection of the attributes of this element.
0353: * @return a collection of the attributes of this element
0354: * @see <a href="http://developer.mozilla.org/en/docs/DOM:element.attributes">Gecko DOM Reference</a>
0355: */
0356: public NamedNodeMap jsxGet_attributes() {
0357: return new NamedNodeMap(getHtmlElementOrDie());
0358: }
0359:
0360: /**
0361: * Gets the specified attribute.
0362: * @param attributeName attribute name.
0363: * @return The value of the specified attribute, <code>null</code> if the attribute is not defined
0364: */
0365: public String jsxFunction_getAttribute(final String attributeName) {
0366: final String value = getHtmlElementOrDie().getAttributeValue(
0367: attributeName);
0368: if (value == HtmlElement.ATTRIBUTE_NOT_DEFINED) {
0369: return null;
0370: } else {
0371: return value;
0372: }
0373: }
0374:
0375: /**
0376: * Gets the specified attribute.
0377: * @param namespaceURI the namespace URI
0378: * @param localName the local name of the attribute to look for
0379: * @return The value of the specified attribute, <code>null</code> if the attribute is not defined
0380: */
0381: public String jsxFunction_getAttributeNS(final String namespaceURI,
0382: final String localName) {
0383: return getHtmlElementOrDie().getAttributeNS(namespaceURI,
0384: localName);
0385: }
0386:
0387: /**
0388: * Test for attribute.
0389: * See also <a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-ElHasAttr">
0390: * the DOM reference</a>
0391: *
0392: * @param name Name of the attribute to test
0393: * @return <code>true</code> if the node has this attribute
0394: */
0395: public boolean jsxFunction_hasAttribute(final String name) {
0396: return getHtmlElementOrDie().getAttribute(name) != HtmlElement.ATTRIBUTE_NOT_DEFINED;
0397: }
0398:
0399: /**
0400: * Test for attribute.
0401: * See also <a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-ElHasAttrNS">
0402: * the DOM reference</a>
0403: *
0404: * @param namespaceURI the namespace URI
0405: * @param localName the local name of the attribute to look for
0406: * @return <code>true</code> if the node has this attribute
0407: */
0408: public boolean jsxFunction_hasAttributeNS(
0409: final String namespaceURI, final String localName) {
0410: return getHtmlElementOrDie().getAttributeNS(namespaceURI,
0411: localName) != HtmlElement.ATTRIBUTE_NOT_DEFINED;
0412: }
0413:
0414: /**
0415: * Set an attribute.
0416: * See also <a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-F68F082">
0417: * the DOM reference</a>
0418: *
0419: * @param name Name of the attribute to set
0420: * @param value Value to set the attribute to
0421: */
0422: public void jsxFunction_setAttribute(final String name,
0423: final String value) {
0424: getHtmlElementOrDie().setAttributeValue(name, value);
0425:
0426: //FF: call corresponding event handler jsxSet_onxxx if found
0427: if (getWindow().getWebWindow().getWebClient()
0428: .getBrowserVersion().isNetscape()) {
0429: try {
0430: final Method method = getClass().getMethod(
0431: "jsxSet_" + name, new Class[] { Object.class });
0432: final String source = "function(){" + value + "}";
0433: method.invoke(this , new Object[] { Context
0434: .getCurrentContext().compileFunction(
0435: getWindow(), source, "", 0, null) });
0436: } catch (final NoSuchMethodException e) {
0437: //silently ignore
0438: } catch (final IllegalAccessException e) {
0439: //silently ignore
0440: } catch (final InvocationTargetException e) {
0441: throw new RuntimeException(e.getCause());
0442: }
0443: }
0444: }
0445:
0446: /**
0447: * Sets the specified attribute.
0448: * @param namespaceURI the namespace URI
0449: * @param qualifiedName the local name of the attribute to look for
0450: * @param value the new attribute value
0451: */
0452: public void jsxFunction_setAttributeNS(final String namespaceURI,
0453: final String qualifiedName, final String value) {
0454: getHtmlElementOrDie().setAttributeValue(namespaceURI,
0455: qualifiedName, value);
0456: }
0457:
0458: /**
0459: * Remove an attribute.
0460: *
0461: * @param name Name of the attribute to remove
0462: */
0463: public void jsxFunction_removeAttribute(final String name) {
0464: getHtmlElementOrDie().removeAttribute(name);
0465: }
0466:
0467: /**
0468: * Gets the attribute node for the specified attribute.
0469: * @param attributeName the name of the attribute to retrieve
0470: * @return the attribute node for the specified attribute.
0471: */
0472: public Object jsxFunction_getAttributeNode(
0473: final String attributeName) {
0474: final Attribute att = new Attribute();
0475: att.setPrototype(getPrototype(Attribute.class));
0476: att.setParentScope(getWindow());
0477: att.init(attributeName, getHtmlElementOrDie());
0478: return att;
0479: }
0480:
0481: /**
0482: * Sets the attribute node for the specified attribute.
0483: * @param newAtt the attribute to set.
0484: * @return the replaced attribute node, if any.
0485: */
0486: public Attribute jsxFunction_setAttributeNode(final Attribute newAtt) {
0487: final String name = newAtt.jsxGet_name();
0488: final String value = newAtt.jsxGet_value();
0489: final Attribute replacedAtt = (Attribute) jsxFunction_getAttributeNode(name);
0490: replacedAtt.detachFromParent();
0491: getHtmlElementOrDie().setAttributeValue(name, value);
0492: return replacedAtt;
0493: }
0494:
0495: /**
0496: * Returns all the descendant elements with the specified tag name.
0497: * @param tagName the name to search for
0498: * @return all the descendant elements with the specified tag name
0499: */
0500: public Object jsxFunction_getElementsByTagName(final String tagName) {
0501: final DomNode node = getDomNodeOrDie();
0502: final HTMLCollection collection = new HTMLCollection(this );
0503: try {
0504: final String xpath;
0505: if ("*".equals(tagName)) {
0506: xpath = "//*";
0507: } else {
0508: xpath = "//node()[name() = '" + tagName.toLowerCase()
0509: + "']";
0510: }
0511: collection.init(node, new HtmlUnitXPath(xpath,
0512: HtmlUnitXPath.buildSubtreeNavigator(node)));
0513: } catch (final JaxenException e) {
0514: final String msg = "Error initializing collection getElementsByTagName("
0515: + tagName + "): ";
0516: throw Context.reportRuntimeError(msg + e.getMessage());
0517: }
0518: return collection;
0519: }
0520:
0521: /**
0522: * Return the class defined for this element
0523: * @return the class name
0524: */
0525: public Object jsxGet_className() {
0526: return getHtmlElementOrDie().getAttributeValue("class");
0527: }
0528:
0529: /**
0530: * Set the class attribute for this element.
0531: * @param className - the new class name
0532: */
0533: public void jsxSet_className(final String className) {
0534: getHtmlElementOrDie().setAttributeValue("class", className);
0535: }
0536:
0537: /**
0538: * Get the innerHTML attribute
0539: * @return the contents of this node as html
0540: */
0541: public String jsxGet_innerHTML() {
0542: final StringBuffer buf = new StringBuffer();
0543: // we can't rely on DomNode.asXml because it adds indentation and new lines
0544: printChildren(buf, getDomNodeOrDie(), !"SCRIPT"
0545: .equals(jsxGet_tagName()));
0546: return buf.toString();
0547: }
0548:
0549: /**
0550: * Get the innerText attribute
0551: * @return the contents of this node as text
0552: */
0553: public String jsxGet_innerText() {
0554: final StringBuffer buf = new StringBuffer();
0555: // we can't rely on DomNode.asXml because it adds indentation and new lines
0556: printChildren(buf, getDomNodeOrDie(), false);
0557: return buf.toString();
0558: }
0559:
0560: /**
0561: * Gets the outerHTML of the node.
0562: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp">
0563: * MSDN documentation</a>
0564: * @return the contents of this node as HTML
0565: */
0566: public String jsxGet_outerHTML() {
0567: final StringBuffer buf = new StringBuffer();
0568: // we can't rely on DomNode.asXml because it adds indentation and new lines
0569: printNode(buf, getDomNodeOrDie(), true);
0570: return buf.toString();
0571: }
0572:
0573: private void printChildren(final StringBuffer buffer,
0574: final DomNode node, final boolean html) {
0575: for (final Iterator iter = node.getChildIterator(); iter
0576: .hasNext();) {
0577: printNode(buffer, (DomNode) iter.next(), html);
0578: }
0579: }
0580:
0581: private void printNode(final StringBuffer buffer,
0582: final DomNode node, final boolean html) {
0583: if (node instanceof DomComment) {
0584: // Remove whitespace sequences.
0585: final String s = node.getNodeValue().replaceAll(" ", " ");
0586: buffer.append("<!--").append(s).append("-->");
0587: } else if (node instanceof DomCharacterData) {
0588: // Remove whitespace sequences, possibly escape XML characters.
0589: String s = node.getNodeValue().replaceAll(" ", " ");
0590: if (html) {
0591: s = com.gargoylesoftware.htmlunit.util.StringUtils
0592: .escapeXmlChars(s);
0593: }
0594: buffer.append(s);
0595: } else if (html) {
0596: // Start the tag name. IE does it in uppercase, FF in lowercase.
0597: final HtmlElement element = (HtmlElement) node;
0598: final boolean ie = getWindow().getWebWindow()
0599: .getWebClient().getBrowserVersion().isIE();
0600: String tag = element.getTagName();
0601: if (ie) {
0602: tag = tag.toUpperCase();
0603: }
0604: buffer.append("<").append(tag);
0605: // Add the attributes. IE does not use quotes, FF does.
0606: for (final Iterator iterator = element
0607: .getAttributeEntriesIterator(); iterator.hasNext();) {
0608: final HtmlAttr entry = (HtmlAttr) iterator.next();
0609: final String name = entry.getName();
0610: final String value = (String) entry.getHtmlValue();
0611: final boolean quote = !ie
0612: || com.gargoylesoftware.htmlunit.util.StringUtils
0613: .containsWhitespace(value);
0614: buffer.append(' ').append(name).append("=");
0615: if (quote) {
0616: buffer.append("\"");
0617: }
0618: buffer.append(value);
0619: if (quote) {
0620: buffer.append("\"");
0621: }
0622: }
0623: buffer.append(">");
0624: // Add the children.
0625: printChildren(buffer, node, html);
0626: // Close the tag. IE does it in uppercase, FF in lowercase.
0627: buffer.append("</").append(tag).append(">");
0628: } else {
0629: final HtmlElement element = (HtmlElement) node;
0630: if (element.getTagName().equals("p")) {
0631: buffer.append("\r\n"); // \r\n because it's to implement something IE specific
0632: }
0633: if (!element.getTagName().equals("script")) {
0634: printChildren(buffer, node, html);
0635: }
0636: }
0637: }
0638:
0639: /**
0640: * Replace all children elements of this element with the supplied value.
0641: * @param value - the new value for the contents of this node
0642: */
0643: public void jsxSet_innerHTML(final Object value) {
0644: final DomNode domNode = getDomNodeOrDie();
0645: domNode.removeAllChildren();
0646:
0647: final BrowserVersion browserVersion = getWindow()
0648: .getWebWindow().getWebClient().getBrowserVersion();
0649:
0650: // null && IE -> add child
0651: // null && non-IE -> Don't add
0652: // '' -> Don't add
0653: if ((value == null && browserVersion.isIE())
0654: || (value != null && !"".equals(value))) {
0655:
0656: final String valueAsString = Context.toString(value);
0657: parseHtmlSnippet(domNode, true, valueAsString);
0658:
0659: //if the parentNode has null parentNode in IE,
0660: //create a DocumentFragment to be the parentNode's parentNode.
0661: if (domNode.getParentDomNode() == null
0662: && getWindow().getWebWindow().getWebClient()
0663: .getBrowserVersion().isIE()) {
0664: final DomDocumentFragment fragment = domNode.getPage()
0665: .createDomDocumentFragment();
0666: fragment.appendDomChild(domNode);
0667: }
0668: }
0669: }
0670:
0671: /**
0672: * Replace all children elements of this element with the supplied value.
0673: * @param value - the new value for the contents of this node
0674: */
0675: public void jsxSet_innerText(final String value) {
0676: final DomNode domNode = getDomNodeOrDie();
0677: domNode.removeAllChildren();
0678:
0679: final DomNode node = new DomText(getDomNodeOrDie().getPage(),
0680: value);
0681: domNode.appendDomChild(node);
0682:
0683: //if the parentNode has null parentNode in IE,
0684: //create a DocumentFragment to be the parentNode's parentNode.
0685: if (domNode.getParentDomNode() == null
0686: && getWindow().getWebWindow().getWebClient()
0687: .getBrowserVersion().isIE()) {
0688: final DomDocumentFragment fragment = domNode.getPage()
0689: .createDomDocumentFragment();
0690: fragment.appendDomChild(domNode);
0691: }
0692: }
0693:
0694: /**
0695: * Replace all children elements of this element with the supplied value.
0696: * Sets the outerHTML of the node.
0697: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/outerhtml.asp">
0698: * MSDN documentation</a>
0699: * @param value - the new value for replacing this node
0700: */
0701: public void jsxSet_outerHTML(final String value) {
0702: final DomNode domNode = getDomNodeOrDie();
0703:
0704: if (OUTER_HTML_READONLY.contains(domNode.getNodeName())) {
0705: throw Context
0706: .reportRuntimeError("outerHTML is read-only for tag "
0707: + domNode.getNodeName());
0708: }
0709:
0710: parseHtmlSnippet(domNode, false, value);
0711: domNode.remove();
0712: }
0713:
0714: /**
0715: * Parses the html code
0716: * @param htmlSnippet the html code extract to parse
0717: */
0718: private void parseHtmlSnippet(final DomNode target,
0719: final boolean append, final String source) {
0720:
0721: final DomNode proxyNode = new HtmlDivision(null,
0722: HtmlDivision.TAG_NAME, target.getPage(), null) {
0723: private static final long serialVersionUID = 2108037256628269797L;
0724:
0725: public DomNode appendDomChild(final DomNode node) {
0726: if (append) {
0727: return target.appendDomChild(node);
0728: } else {
0729: target.insertBefore(node);
0730: return node;
0731: }
0732: }
0733: };
0734:
0735: try {
0736: HTMLParser.parseFragment(proxyNode, source);
0737: } catch (final IOException e) {
0738: getLog()
0739: .error(
0740: "Unexpected exception occurred while parsing html snippet",
0741: e);
0742: throw Context
0743: .reportRuntimeError("Unexpected exception occurred while parsing html snippet: "
0744: + e.getMessage());
0745: } catch (final SAXException e) {
0746: getLog()
0747: .error(
0748: "Unexpected exception occurred while parsing html snippet",
0749: e);
0750: throw Context
0751: .reportRuntimeError("Unexpected exception occurred while parsing html snippet: "
0752: + e.getMessage());
0753: }
0754: }
0755:
0756: /**
0757: * Gets the attributes of the element in the form of a {@link org.xml.sax.Attributes}
0758: * @param element the element to read the attributes from
0759: * @return the attributes
0760: */
0761: protected AttributesImpl readAttributes(final HtmlElement element) {
0762: final AttributesImpl attributes = new AttributesImpl();
0763: for (final Iterator iter = element
0764: .getAttributeEntriesIterator(); iter.hasNext();) {
0765: final HtmlAttr entry = (HtmlAttr) iter.next();
0766: final String name = entry.getName();
0767: final String value = (String) entry.getHtmlValue();
0768: attributes.addAttribute(null, name, name, null, value);
0769: }
0770:
0771: return attributes;
0772: }
0773:
0774: /**
0775: * Inserts the given HTML text into the element at the location.
0776: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms536452.aspx">
0777: * MSDN documentation</a>
0778: * @param where specifies where to insert the HTML text, using one of the following value:
0779: * beforeBegin, afterBegin, beforeEnd, afterEnd
0780: * @param text the HTML text to insert
0781: */
0782: public void jsxFunction_insertAdjacentHTML(final String where,
0783: final String text) {
0784: final Object[] values = getInsertAdjacentLocation(where);
0785: final DomNode node = (DomNode) values[0];
0786: final boolean append = ((Boolean) values[1]).booleanValue();
0787:
0788: // add the new nodes
0789: parseHtmlSnippet(node, append, text);
0790: }
0791:
0792: /**
0793: * Inserts the given element into the element at the location.
0794: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms536451.aspx">
0795: * MSDN documentation</a>
0796: * @param where specifies where to insert the element, using one of the following value:
0797: * beforeBegin, afterBegin, beforeEnd, afterEnd
0798: * @param object the element to insert
0799: * @return an element object
0800: */
0801: public Object jsxFunction_insertAdjacentElement(final String where,
0802: final Object object) {
0803: if (object instanceof Node) {
0804: final DomNode childNode = ((Node) object).getDomNodeOrDie();
0805: final Object[] values = getInsertAdjacentLocation(where);
0806: final DomNode node = (DomNode) values[0];
0807: final boolean append = ((Boolean) values[1]).booleanValue();
0808:
0809: if (append) {
0810: node.appendDomChild(childNode);
0811: } else {
0812: node.insertBefore(childNode);
0813: }
0814: return object;
0815: } else {
0816: throw Context
0817: .reportRuntimeError("Passed object is not an element: "
0818: + object);
0819: }
0820: }
0821:
0822: /**
0823: * Returns where and how to add the new node.
0824: * Used by {@link #jsxFunction_insertAdjacentHTML(String, String)} and
0825: * {@link #jsxFunction_insertAdjacentElement(String, Object)}.
0826: *
0827: * @param where specifies where to insert the element, using one of the following value:
0828: * beforeBegin, afterBegin, beforeEnd, afterEnd
0829: *
0830: * @return an array of 1-DomNode:parentNode and 2-Boolean:append
0831: */
0832: private Object[] getInsertAdjacentLocation(final String where) {
0833: final DomNode currentNode = getDomNodeOrDie();
0834: final DomNode node;
0835: final boolean append;
0836:
0837: // compute the where and how the new nodes should be added
0838: if (POSITION_AFTER_BEGIN.equalsIgnoreCase(where)) {
0839: if (currentNode.getFirstDomChild() == null) {
0840: // new nodes should appended to the children of current node
0841: node = currentNode;
0842: append = true;
0843: } else {
0844: // new nodes should be inserted before first child
0845: node = currentNode.getFirstDomChild();
0846: append = false;
0847: }
0848: } else if (POSITION_BEFORE_BEGIN.equalsIgnoreCase(where)) {
0849: // new nodes should be inserted before current node
0850: node = currentNode;
0851: append = false;
0852: } else if (POSITION_BEFORE_END.equalsIgnoreCase(where)) {
0853: // new nodes should appended to the children of current node
0854: node = currentNode;
0855: append = true;
0856: } else if (POSITION_AFTER_END.equalsIgnoreCase(where)) {
0857: if (currentNode.getNextDomSibling() == null) {
0858: // new nodes should appended to the children of parent node
0859: node = currentNode.getParentDomNode();
0860: append = true;
0861: } else {
0862: // new nodes should be inserted before current node's next sibling
0863: node = currentNode.getNextDomSibling();
0864: append = false;
0865: }
0866: } else {
0867: throw Context
0868: .reportRuntimeError("Illegal position value: \""
0869: + where + "\"");
0870: }
0871:
0872: if (append) {
0873: return new Object[] { node, Boolean.TRUE };
0874: } else {
0875: return new Object[] { node, Boolean.FALSE };
0876: }
0877: }
0878:
0879: /**
0880: * Adds the specified behavior to this HTML element. Currently only supports
0881: * the following default IE behaviors:
0882: * <ul>
0883: * <li>#default#clientCaps</li>
0884: * <li>#default#homePage</li>
0885: * <li>#default#download</li>
0886: * </ul>
0887: * @param behavior the URL of the behavior to add, or a default behavior name
0888: * @return an identifier that can be user later to detach the behavior from the element
0889: */
0890: public int jsxFunction_addBehavior(final String behavior) {
0891: // if behavior already defined, then nothing to do
0892: if (behaviors_.contains(behavior)) {
0893: return 0;
0894: }
0895:
0896: if (BEHAVIOR_CLIENT_CAPS.equalsIgnoreCase(behavior)) {
0897: final Class c = getClass();
0898: defineProperty("availHeight", c, 0);
0899: defineProperty("availWidth", c, 0);
0900: defineProperty("bufferDepth", c, 0);
0901: defineProperty("colorDepth", c, 0);
0902: defineProperty("connectionType", c, 0);
0903: defineProperty("cookieEnabled", c, 0);
0904: defineProperty("cpuClass", c, 0);
0905: defineProperty("height", c, 0);
0906: defineProperty("javaEnabled", c, 0);
0907: defineProperty("platform", c, 0);
0908: defineProperty("systemLanguage", c, 0);
0909: defineProperty("userLanguage", c, 0);
0910: defineProperty("width", c, 0);
0911: defineFunctionProperties(
0912: new String[] { "addComponentRequest" }, c, 0);
0913: defineFunctionProperties(
0914: new String[] { "clearComponentRequest" }, c, 0);
0915: defineFunctionProperties(
0916: new String[] { "compareVersions" }, c, 0);
0917: defineFunctionProperties(
0918: new String[] { "doComponentRequest" }, c, 0);
0919: defineFunctionProperties(
0920: new String[] { "getComponentVersion" }, c, 0);
0921: defineFunctionProperties(
0922: new String[] { "isComponentInstalled" }, c, 0);
0923: behaviors_.add(BEHAVIOR_CLIENT_CAPS);
0924: return BEHAVIOR_ID_CLIENT_CAPS;
0925: } else if (BEHAVIOR_HOMEPAGE.equalsIgnoreCase(behavior)) {
0926: final Class c = getClass();
0927: defineFunctionProperties(new String[] { "isHomePage" }, c,
0928: 0);
0929: defineFunctionProperties(new String[] { "setHomePage" }, c,
0930: 0);
0931: defineFunctionProperties(
0932: new String[] { "navigateHomePage" }, c, 0);
0933: behaviors_.add(BEHAVIOR_CLIENT_CAPS);
0934: return BEHAVIOR_ID_HOMEPAGE;
0935: } else if (BEHAVIOR_DOWNLOAD.equalsIgnoreCase(behavior)) {
0936: final Class c = getClass();
0937: defineFunctionProperties(new String[] { "startDownload" },
0938: c, 0);
0939: behaviors_.add(BEHAVIOR_DOWNLOAD);
0940: return BEHAVIOR_ID_DOWNLOAD;
0941: } else {
0942: getLog().warn("Unimplemented behavior: " + behavior);
0943: return BEHAVIOR_ID_UNKNOWN;
0944: }
0945: }
0946:
0947: /**
0948: * Removes the behavior corresponding to the specified identifier from this element.
0949: * @param id the identifier for the behavior to remove
0950: */
0951: public void jsxFunction_removeBehavior(final int id) {
0952: switch (id) {
0953: case BEHAVIOR_ID_CLIENT_CAPS:
0954: delete("availHeight");
0955: delete("availWidth");
0956: delete("bufferDepth");
0957: delete("colorDepth");
0958: delete("connectionType");
0959: delete("cookieEnabled");
0960: delete("cpuClass");
0961: delete("height");
0962: delete("javaEnabled");
0963: delete("platform");
0964: delete("systemLanguage");
0965: delete("userLanguage");
0966: delete("width");
0967: delete("addComponentRequest");
0968: delete("clearComponentRequest");
0969: delete("compareVersions");
0970: delete("doComponentRequest");
0971: delete("getComponentVersion");
0972: delete("isComponentInstalled");
0973: behaviors_.remove(BEHAVIOR_CLIENT_CAPS);
0974: break;
0975: case BEHAVIOR_ID_HOMEPAGE:
0976: delete("isHomePage");
0977: delete("setHomePage");
0978: delete("navigateHomePage");
0979: behaviors_.remove(BEHAVIOR_HOMEPAGE);
0980: break;
0981: case BEHAVIOR_ID_DOWNLOAD:
0982: delete("startDownload");
0983: behaviors_.remove(BEHAVIOR_DOWNLOAD);
0984: break;
0985: default:
0986: getLog().warn(
0987: "Unexpected behavior id: " + id + ". Ignoring.");
0988: }
0989: }
0990:
0991: //----------------------- START #default#clientCaps BEHAVIOR -----------------------
0992:
0993: /**
0994: * Returns the screen's available height. Part of the <tt>#default#clientCaps</tt>
0995: * default IE behavior implementation.
0996: * @return the screen's available height.
0997: */
0998: public int getAvailHeight() {
0999: return getWindow().jsxGet_screen().jsxGet_availHeight();
1000: }
1001:
1002: /**
1003: * Returns the screen's available width. Part of the <tt>#default#clientCaps</tt>
1004: * default IE behavior implementation.
1005: * @return the screen's available width.
1006: */
1007: public int getAvailWidth() {
1008: return getWindow().jsxGet_screen().jsxGet_availWidth();
1009: }
1010:
1011: /**
1012: * Returns the screen's buffer depth. Part of the <tt>#default#clientCaps</tt>
1013: * default IE behavior implementation.
1014: * @return the screen's buffer depth.
1015: */
1016: public int getBufferDepth() {
1017: return getWindow().jsxGet_screen().jsxGet_bufferDepth();
1018: }
1019:
1020: /**
1021: * Returns BoxObject for this element.
1022: * @return BoxObject for this element.
1023: */
1024: public BoxObject getBoxObject() {
1025: if (boxObject_ == null) {
1026: boxObject_ = new BoxObject();
1027: boxObject_.setParentScope(getWindow());
1028: boxObject_
1029: .setPrototype(getPrototype(boxObject_.getClass()));
1030: }
1031: return boxObject_;
1032: }
1033:
1034: /**
1035: * Returns the screen's color depth. Part of the <tt>#default#clientCaps</tt>
1036: * default IE behavior implementation.
1037: * @return the screen's color depth.
1038: */
1039: public int getColorDepth() {
1040: return getWindow().jsxGet_screen().jsxGet_colorDepth();
1041: }
1042:
1043: /**
1044: * Returns the connection type being used. Part of the <tt>#default#clientCaps</tt>
1045: * default IE behavior implementation.
1046: * @return the connection type being used.
1047: * Current implementation always return "modem"
1048: */
1049: public String getConnectionType() {
1050: return "modem";
1051: }
1052:
1053: /**
1054: * Returns <tt>true</tt> if cookies are enabled. Part of the <tt>#default#clientCaps</tt>
1055: * default IE behavior implementation.
1056: * @return whether or not cookies are enabled.
1057: */
1058: public boolean getCookieEnabled() {
1059: return getWindow().jsxGet_navigator().jsxGet_cookieEnabled();
1060: }
1061:
1062: /**
1063: * Returns the type of CPU used. Part of the <tt>#default#clientCaps</tt>
1064: * default IE behavior implementation.
1065: * @return the type of CPU used.
1066: */
1067: public String getCpuClass() {
1068: return getWindow().jsxGet_navigator().jsxGet_cpuClass();
1069: }
1070:
1071: /**
1072: * Returns the screen's height. Part of the <tt>#default#clientCaps</tt>
1073: * default IE behavior implementation.
1074: * @return the screen's height.
1075: */
1076: public int getHeight() {
1077: return getWindow().jsxGet_screen().jsxGet_height();
1078: }
1079:
1080: /**
1081: * Returns <tt>true</tt> if Java is enabled. Part of the <tt>#default#clientCaps</tt>
1082: * default IE behavior implementation.
1083: * @return whether or not Java is enabled.
1084: */
1085: public boolean getJavaEnabled() {
1086: return getWindow().jsxGet_navigator().jsxFunction_javaEnabled();
1087: }
1088:
1089: /**
1090: * Returns the platform used. Part of the <tt>#default#clientCaps</tt>
1091: * default IE behavior implementation.
1092: * @return the platform used.
1093: */
1094: public String getPlatform() {
1095: return getWindow().jsxGet_navigator().jsxGet_platform();
1096: }
1097:
1098: /**
1099: * Returns the system language. Part of the <tt>#default#clientCaps</tt>
1100: * default IE behavior implementation.
1101: * @return the system language.
1102: */
1103: public String getSystemLanguage() {
1104: return getWindow().jsxGet_navigator().jsxGet_systemLanguage();
1105: }
1106:
1107: /**
1108: * Returns the user language. Part of the <tt>#default#clientCaps</tt>
1109: * default IE behavior implementation.
1110: * @return the user language.
1111: */
1112: public String getUserLanguage() {
1113: return getWindow().jsxGet_navigator().jsxGet_userLanguage();
1114: }
1115:
1116: /**
1117: * Returns the screen's width. Part of the <tt>#default#clientCaps</tt>
1118: * default IE behavior implementation.
1119: * @return the screen's width.
1120: */
1121: public int getWidth() {
1122: return getWindow().jsxGet_screen().jsxGet_width();
1123: }
1124:
1125: /**
1126: * Adds the specified component to the queue of components to be installed. Note
1127: * that no components ever get installed, and this call is always ignored. Part of
1128: * the <tt>#default#clientCaps</tt> default IE behavior implementation.
1129: * @param id the identifier for the component to install
1130: * @param idType the type of identifier specified
1131: * @param minVersion the minimum version of the component to install
1132: */
1133: public void addComponentRequest(final String id,
1134: final String idType, final String minVersion) {
1135: getLog().debug(
1136: "Call to addComponentRequest(" + id + ", " + idType
1137: + ", " + minVersion + ") ignored.");
1138: }
1139:
1140: /**
1141: * Clears the component install queue of all component requests. Note that no components
1142: * ever get installed, and this call is always ignored. Part of the <tt>#default#clientCaps</tt>
1143: * default IE behavior implementation.
1144: */
1145: public void clearComponentRequest() {
1146: getLog().debug("Call to clearComponentRequest() ignored.");
1147: }
1148:
1149: /**
1150: * Compares the two specified version numbers. Part of the <tt>#default#clientCaps</tt>
1151: * default IE behavior implementation.
1152: * @param v1 the first of the two version numbers to compare
1153: * @param v2 the second of the two version numbers to compare
1154: * @return -1 if v1 < v2, 0 if v1 = v2, and 1 if v1 > v2
1155: */
1156: public int compareVersions(final String v1, final String v2) {
1157: final int i = v1.compareTo(v2);
1158: if (i == 0) {
1159: return 0;
1160: } else if (i < 0) {
1161: return -1;
1162: } else {
1163: return 1;
1164: }
1165: }
1166:
1167: /**
1168: * Downloads all the components queued via {@link #addComponentRequest(String, String, String)}.
1169: * @return <tt>true</tt> if the components are downloaded successfully.
1170: * Current implementation always return <code>false</code>
1171: */
1172: public boolean doComponentRequest() {
1173: return false;
1174: }
1175:
1176: /**
1177: * Returns the version of the specified component.
1178: * @param id the identifier for the component whose version is to be returned
1179: * @param idType the type of identifier specified
1180: * @return the version of the specified component.
1181: * Current implementation always return "1.0"
1182: */
1183: public String getComponentVersion(final String id,
1184: final String idType) {
1185: return "1.0";
1186: }
1187:
1188: /**
1189: * Returns <tt>true</tt> if the specified component is installed.
1190: * @param id the identifier for the component to check for
1191: * @param idType the type of id specified
1192: * @param minVersion the minimum version to check for
1193: * @return <tt>true</tt> if the specified component is installed.
1194: */
1195: public boolean isComponentInstalled(final String id,
1196: final String idType, final String minVersion) {
1197: return false;
1198: }
1199:
1200: //----------------------- START #default#download BEHAVIOR -----------------------
1201: /**
1202: * Implementation of the IE behavior #default#download
1203: * @param uri The URI of the download source
1204: * @param callback the method which should be called when the download is finished
1205: * @see <a href="http://msdn.microsoft.com/workshop/author/behaviors/reference/methods/startdownload.asp">
1206: * MSDN documentation</a>
1207: * @throws MalformedURLException If the url cannot be created
1208: */
1209: public void startDownload(final String uri, final Function callback)
1210: throws MalformedURLException {
1211: final HtmlPage page = (HtmlPage) getWindow().getWebWindow()
1212: .getEnclosedPage();
1213: final URL url = page.getFullyQualifiedUrl(uri);
1214: if (!page.getWebResponse().getUrl().getHost().equals(
1215: url.getHost())) {
1216: throw Context.reportRuntimeError("Not authorized url: "
1217: + url);
1218: }
1219: final Thread t = new DownloadBehaviorDownloader(url, callback);
1220: getLog().debug("Starting download thread for " + url);
1221: t.start();
1222: }
1223:
1224: /**
1225: * A helper class for the IE behavior #default#download
1226: * This represents a download action. The download is handled
1227: * asynchronously, when the download is finished, the method specified
1228: * by callback is called with one argument - the content of the response as string.
1229: * @see #startDownload(String, Function)
1230: * @author <a href="mailto:stefan@anzinger.net">Stefan Anzinger</a>
1231: */
1232: private class DownloadBehaviorDownloader extends Thread {
1233: private final URL url_;
1234: private final Function callback_;
1235:
1236: /**
1237: * @param url The URL to download
1238: * @param callback The function to callback
1239: */
1240: public DownloadBehaviorDownloader(final URL url,
1241: final Function callback) {
1242: super ("Downloader for behavior #default#download '" + url
1243: + "'");
1244: url_ = url;
1245: callback_ = callback;
1246: }
1247:
1248: /**
1249: * Does the download and calls the callback method
1250: */
1251: public void run() {
1252: final WebClient wc = getWindow().getWebWindow()
1253: .getWebClient();
1254: final Scriptable scope = callback_.getParentScope();
1255: final WebRequestSettings settings = new WebRequestSettings(
1256: url_);
1257:
1258: try {
1259: final WebResponse webResponse = wc
1260: .loadWebResponse(settings);
1261: final String content = webResponse.getContentAsString();
1262: getLog().debug(
1263: "Downloaded content: "
1264: + StringUtils.abbreviate(content, 512));
1265: final Object[] args = new Object[] { content };
1266: final ContextAction action = new ContextAction() {
1267: public Object run(final Context cx) {
1268: callback_.call(cx, scope, scope, args);
1269: return null;
1270: }
1271: };
1272: Context.call(action);
1273: } catch (final Exception e) {
1274: getLog().error(
1275: "Behavior #default#download: Cannot download "
1276: + url_, e);
1277: }
1278: }
1279: }
1280:
1281: //----------------------- END #default#download BEHAVIOR -----------------------
1282:
1283: //----------------------- START #default#homePage BEHAVIOR -----------------------
1284:
1285: /**
1286: * Returns <tt>true</tt> if the specified URL is the web client's current
1287: * homepage and the document calling the method is on the same domain as the
1288: * user's homepage. Part of the <tt>#default#homePage</tt> default IE behavior
1289: * implementation.
1290: * @param url the URL to check
1291: * @return <tt>true</tt> if the specified URL is the current homepage
1292: */
1293: public boolean isHomePage(final String url) {
1294: try {
1295: final URL newUrl = new URL(url);
1296: final URL currentUrl = getDomNodeOrDie().getPage()
1297: .getWebResponse().getUrl();
1298: final String home = getDomNodeOrDie().getPage()
1299: .getWebClient().getHomePage();
1300: final boolean sameDomains = newUrl.getHost()
1301: .equalsIgnoreCase(currentUrl.getHost());
1302: final boolean isHomePage = (home != null && home
1303: .equals(url));
1304: return (sameDomains && isHomePage);
1305: } catch (final MalformedURLException e) {
1306: return false;
1307: }
1308: }
1309:
1310: /**
1311: * Sets the web client's current homepage. Part of the <tt>#default#homePage</tt>
1312: * default IE behavior implementation.
1313: * @param url the new homepage URL
1314: */
1315: public void setHomePage(final String url) {
1316: getDomNodeOrDie().getPage().getWebClient().setHomePage(url);
1317: }
1318:
1319: /**
1320: * Causes the web client to navigate to the current home page. Part of the
1321: * <tt>#default#homePage</tt> default IE behavior implementation.
1322: * @throws IOException if loading home page fails
1323: */
1324: public void navigateHomePage() throws IOException {
1325: final WebClient webClient = getDomNodeOrDie().getPage()
1326: .getWebClient();
1327: webClient.getPage(webClient.getHomePage());
1328: }
1329:
1330: //----------------------- END #default#homePage BEHAVIOR -----------------------
1331:
1332: /**
1333: * Get the children of the current node.
1334: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/collections/children.asp">
1335: * MSDN documentation</a>
1336: * @return the child at the given position
1337: */
1338: public Object jsxGet_children() {
1339: final DomNode element = getDomNodeOrDie();
1340: final HTMLCollection children = new HTMLCollection(this );
1341:
1342: try {
1343: final XPath xpath = new HtmlUnitXPath("./*", HtmlUnitXPath
1344: .buildSubtreeNavigator(element));
1345: children.init(element, xpath);
1346: } catch (final JaxenException e) {
1347: // should never occur
1348: throw Context
1349: .reportRuntimeError("Failed initializing children: "
1350: + e.getMessage());
1351: }
1352: return children;
1353: }
1354:
1355: /**
1356: * Set the onclick event handler for this element.
1357: * @param handler the new handler
1358: */
1359: public void jsxSet_onclick(final Object handler) {
1360: setEventHandlerProp("onclick", handler);
1361: }
1362:
1363: /**
1364: * Get the onclick event handler for this element.
1365: * @return <code>org.mozilla.javascript.Function</code>
1366: */
1367: public Object jsxGet_onclick() {
1368: return getEventHandlerProp("onclick");
1369: }
1370:
1371: /**
1372: * Set the ondblclick event handler for this element.
1373: * @param handler the new handler
1374: **/
1375: public void jsxSet_ondblclick(final Object handler) {
1376: setEventHandlerProp("ondblclick", handler);
1377: }
1378:
1379: /**
1380: * Get the ondblclick event handler for this element.
1381: * @return <code>org.mozilla.javascript.Function</code>
1382: */
1383: public Object jsxGet_ondblclick() {
1384: return getEventHandlerProp("ondblclick");
1385: }
1386:
1387: /**
1388: * Set the onblur event handler for this element.
1389: * @param handler the new handler
1390: */
1391: public void jsxSet_onblur(final Object handler) {
1392: setEventHandlerProp("onblur", handler);
1393: }
1394:
1395: /**
1396: * Get the onblur event handler for this element.
1397: * @return <code>org.mozilla.javascript.Function</code>
1398: */
1399: public Object jsxGet_onblur() {
1400: return getEventHandlerProp("onblur");
1401: }
1402:
1403: /**
1404: * Set the onfocus event handler for this element.
1405: * @param handler the new handler
1406: */
1407: public void jsxSet_onfocus(final Object handler) {
1408: setEventHandlerProp("onfocus", handler);
1409: }
1410:
1411: /**
1412: * Get the onfocus event handler for this element.
1413: * @return <code>org.mozilla.javascript.Function</code>
1414: */
1415: public Object jsxGet_onfocus() {
1416: return getEventHandlerProp("onfocus");
1417: }
1418:
1419: /**
1420: * Set the onkeydown event handler for this element.
1421: * @param handler the new handler
1422: */
1423: public void jsxSet_onkeydown(final Object handler) {
1424: setEventHandlerProp("onkeydown", handler);
1425: }
1426:
1427: /**
1428: * Get the onkeydown event handler for this element.
1429: * @return <code>org.mozilla.javascript.Function</code>
1430: */
1431: public Object jsxGet_onkeydown() {
1432: return getEventHandlerProp("onkeydown");
1433: }
1434:
1435: /**
1436: * Set the onkeypress event handler for this element.
1437: * @param handler the new handler
1438: */
1439: public void jsxSet_onkeypress(final Object handler) {
1440: setEventHandlerProp("onkeypress", handler);
1441: }
1442:
1443: /**
1444: * Get the onkeypress event handler for this element.
1445: * @return <code>org.mozilla.javascript.Function</code>
1446: */
1447: public Object jsxGet_onkeypress() {
1448: return getEventHandlerProp("onkeypress");
1449: }
1450:
1451: /**
1452: * Set the onkeyup event handler for this element.
1453: * @param handler the new handler
1454: */
1455: public void jsxSet_onkeyup(final Object handler) {
1456: setEventHandlerProp("onkeyup", handler);
1457: }
1458:
1459: /**
1460: * Get the onkeyup event handler for this element.
1461: * @return <code>org.mozilla.javascript.Function</code>
1462: */
1463: public Object jsxGet_onkeyup() {
1464: return getEventHandlerProp("onkeyup");
1465: }
1466:
1467: /**
1468: * Set the onmousedown event handler for this element.
1469: * @param handler the new handler
1470: */
1471: public void jsxSet_onmousedown(final Object handler) {
1472: setEventHandlerProp("onmousedown", handler);
1473: }
1474:
1475: /**
1476: * Get the onmousedown event handler for this element.
1477: * @return <code>org.mozilla.javascript.Function</code>
1478: */
1479: public Object jsxGet_onmousedown() {
1480: return getEventHandlerProp("onmousedown");
1481: }
1482:
1483: /**
1484: * Set the onmousemove event handler for this element.
1485: * @param handler the new handler
1486: */
1487: public void jsxSet_onmousemove(final Object handler) {
1488: setEventHandlerProp("onmousemove", handler);
1489: }
1490:
1491: /**
1492: * Get the onmousemove event handler for this element.
1493: * @return <code>org.mozilla.javascript.Function</code>
1494: */
1495: public Object jsxGet_onmousemove() {
1496: return getEventHandlerProp("onmousemove");
1497: }
1498:
1499: /**
1500: * Set the onmouseout event handler for this element.
1501: * @param handler the new handler
1502: */
1503: public void jsxSet_onmouseout(final Object handler) {
1504: setEventHandlerProp("onmouseout", handler);
1505: }
1506:
1507: /**
1508: * Get the onmouseout event handler for this element.
1509: * @return <code>org.mozilla.javascript.Function</code>
1510: */
1511: public Object jsxGet_onmouseout() {
1512: return getEventHandlerProp("onmouseout");
1513: }
1514:
1515: /**
1516: * Set the onmouseover event handler for this element.
1517: * @param handler the new handler
1518: */
1519: public void jsxSet_onmouseover(final Object handler) {
1520: setEventHandlerProp("onmouseover", handler);
1521: }
1522:
1523: /**
1524: * Get the onmouseover event handler for this element.
1525: * @return <code>org.mozilla.javascript.Function</code>
1526: */
1527: public Object jsxGet_onmouseover() {
1528: return getEventHandlerProp("onmouseover");
1529: }
1530:
1531: /**
1532: * Set the onmouseup event handler for this element.
1533: * @param handler the new handler
1534: */
1535: public void jsxSet_onmouseup(final Object handler) {
1536: setEventHandlerProp("onmouseup", handler);
1537: }
1538:
1539: /**
1540: * Get the onmouseup event handler for this element.
1541: * @return <code>org.mozilla.javascript.Function</code>
1542: */
1543: public Object jsxGet_onmouseup() {
1544: return getEventHandlerProp("onmouseup");
1545: }
1546:
1547: /**
1548: * Set the oncontextmenu event handler for this element.
1549: * @param handler the new handler
1550: */
1551: public void jsxSet_oncontextmenu(final Object handler) {
1552: setEventHandlerProp("oncontextmenu", handler);
1553: }
1554:
1555: /**
1556: * Get the oncontextmenu event handler for this element.
1557: * @return <code>org.mozilla.javascript.Function</code>
1558: */
1559: public Object jsxGet_oncontextmenu() {
1560: return getEventHandlerProp("oncontextmenu");
1561: }
1562:
1563: /**
1564: * Set the onresize event handler for this element.
1565: * @param handler the new handler
1566: */
1567: public void jsxSet_onresize(final Object handler) {
1568: setEventHandlerProp("onresize", handler);
1569: }
1570:
1571: /**
1572: * Get the onresize event handler for this element.
1573: * @return <code>org.mozilla.javascript.Function</code>
1574: */
1575: public Object jsxGet_onresize() {
1576: return getEventHandlerProp("onresize");
1577: }
1578:
1579: /**
1580: * Returns this element's <tt>offsetHeight</tt>, which is the element height plus the element's padding
1581: * plus the element's border. This method returns a dummy value compatible with mouse event coordinates
1582: * during mouse events.
1583: * @return this element's <tt>offsetHeight</tt>
1584: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms534199.aspx">MSDN Documentation</a>
1585: * @see <a href="http://www.quirksmode.org/js/elementdimensions.html">Element Dimensions</a>
1586: */
1587: public int jsxGet_offsetHeight() {
1588: final MouseEvent event = MouseEvent.getCurrentMouseEvent();
1589: if (isAncestorOfEventTarget(event)) {
1590: // compute appropriate offsetHeight to make as if mouse event produced within this element
1591: return event.jsxGet_clientY() - getPosY() + 50;
1592: } else {
1593: return 1;
1594: }
1595: }
1596:
1597: /**
1598: * Returns this element's <tt>offsetWidth</tt>, which is the element width plus the element's padding
1599: * plus the element's border. This method returns a dummy value compatible with mouse event coordinates
1600: * during mouse events.
1601: * @return this element's <tt>offsetWidth</tt>
1602: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms534304.aspx">MSDN Documentation</a>
1603: * @see <a href="http://www.quirksmode.org/js/elementdimensions.html">Element Dimensions</a>
1604: */
1605: public int jsxGet_offsetWidth() {
1606: final MouseEvent event = MouseEvent.getCurrentMouseEvent();
1607: if (isAncestorOfEventTarget(event)) {
1608: // compute appropriate offsetwidth to make as if mouse event produced within this element
1609: return event.jsxGet_clientX() - getPosX() + 50;
1610: } else {
1611: return 1;
1612: }
1613: }
1614:
1615: /**
1616: * Returns <tt>true</tt> if this element's node is an ancestor of the specified event's target node.
1617: * @param event the event whose target node is to be checked
1618: * @return <tt>true</tt> if this element's node is an ancestor of the specified event's target node
1619: */
1620: private boolean isAncestorOfEventTarget(final MouseEvent event) {
1621: if (event == null) {
1622: return false;
1623: }
1624: final HTMLElement target = (HTMLElement) event.jsxGet_target();
1625: return getHtmlElementOrDie().isAncestorOf(
1626: target.getHtmlElementOrDie());
1627: }
1628:
1629: /**
1630: * Gets the x position of the element. The value returned doesn't need to be "correct" as it
1631: * just needs to be compatible with mouse event coordinates.
1632: *
1633: * @return the x position
1634: */
1635: int getPosX() {
1636: int cumulativeOffset = 0;
1637: HTMLElement element = this ;
1638: while (element != null) {
1639: cumulativeOffset += element.jsxGet_offsetLeft();
1640: element = (HTMLElement) element.jsxGet_offsetParent();
1641: }
1642: return cumulativeOffset;
1643: }
1644:
1645: /**
1646: * Gets the y position of the element. The value returned doesn't need to be "correct" as it
1647: * just needs to be compatible with mouse event coordinates.
1648: * @return the y position
1649: */
1650: int getPosY() {
1651: int cumulativeOffset = 0;
1652: HTMLElement element = this ;
1653: while (element != null) {
1654: cumulativeOffset += element.jsxGet_offsetTop();
1655: element = (HTMLElement) element.jsxGet_offsetParent();
1656: }
1657: return cumulativeOffset;
1658: }
1659:
1660: /**
1661: * Returns this element's <tt>offsetLeft</tt>, which is the calculated left position of this
1662: * element relative to the <tt>offsetParent</tt>.
1663: * @return this element's <tt>offsetLeft</tt>
1664: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms534200.aspx">MSDN Documentation</a>
1665: * @see <a href="http://www.quirksmode.org/js/elementdimensions.html">Element Dimensions</a>
1666: */
1667: public int jsxGet_offsetLeft() {
1668: return 1;
1669: }
1670:
1671: /**
1672: * Returns this element's <tt>offsetTop</tt>, which is the calculated top position of this
1673: * element relative to the <tt>offsetParent</tt>.
1674: * @return this element's <tt>offsetTop</tt>
1675: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms534303.aspx">MSDN Documentation</a>
1676: * @see <a href="http://www.quirksmode.org/js/elementdimensions.html">Element Dimensions</a>
1677: */
1678: public int jsxGet_offsetTop() {
1679: return 1;
1680: }
1681:
1682: /**
1683: * Returns this element's <tt>offsetParent</tt>. The <tt>offsetLeft</tt> and <tt>offsetTop</tt>
1684: * attributes are relative to the <tt>offsetParent</tt>.
1685: * @return this element's <tt>offsetParent</tt>
1686: * @see <a href="http://msdn2.microsoft.com/en-us/library/ms534302.aspx">MSDN Documentation</a>
1687: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_el_ref20.html">Gecko DOM Reference</a>
1688: * @see <a href="http://www.quirksmode.org/js/elementdimensions.html">Element Dimensions</a>
1689: * @see <a href="http://www.w3.org/TR/REC-CSS2/box.html">Box Model</a>
1690: */
1691: public Object jsxGet_offsetParent() {
1692: DomNode currentElement = getHtmlElementOrDie();
1693: Object offsetParent = null;
1694: while (currentElement != null) {
1695: final DomNode parentNode = currentElement
1696: .getParentDomNode();
1697: // According to the Microsoft and Mozilla documentation, and from experimentation
1698: // in the IE and Firefox browsers, the offsetParent is the container
1699: // (<td>, <table>, <body>) nearest to the node
1700: if ((parentNode instanceof HtmlTableDataCell)
1701: || (parentNode instanceof HtmlTable)
1702: || (parentNode instanceof HtmlBody)) {
1703: offsetParent = parentNode.getScriptObject();
1704: break;
1705: }
1706: currentElement = currentElement.getParentDomNode();
1707: }
1708: return offsetParent;
1709: }
1710:
1711: /**
1712: * Just for debug purposes.
1713: * {@inheritDoc}
1714: */
1715: public String toString() {
1716: return "HTMLElement for " + getHtmlElementOrNull();
1717: }
1718:
1719: /**
1720: * Get the scrollTop for this element.
1721: * @return a dummy value (default is 0)
1722: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/scrollTop.asp">
1723: * MSDN documentation</a>
1724: */
1725: public int jsxGet_scrollTop() {
1726: return scrollTop_;
1727: }
1728:
1729: /**
1730: * Set the scrollTop for this element.
1731: * @param scroll the new value
1732: */
1733: public void jsxSet_scrollTop(final int scroll) {
1734: scrollTop_ = scroll;
1735: }
1736:
1737: /**
1738: * Get the scrollLeft for this element.
1739: * @return a dummy value (default is 0)
1740: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/scrollLeft.asp">
1741: * MSDN documentation</a>
1742: */
1743: public int jsxGet_scrollLeft() {
1744: return scrollLeft_;
1745: }
1746:
1747: /**
1748: * Set the scrollLeft for this element.
1749: * @param scroll the new value
1750: */
1751: public void jsxSet_scrollLeft(final int scroll) {
1752: scrollLeft_ = scroll;
1753: }
1754:
1755: /**
1756: * Get the scrollHeight for this element.
1757: * @return a dummy value of 10
1758: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/scrollHeight.asp">
1759: * MSDN documentation</a>
1760: */
1761: public int jsxGet_scrollHeight() {
1762: return 10;
1763: }
1764:
1765: /**
1766: * Get the scrollWidth for this element.
1767: * @return a dummy value of 10
1768: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/scrollWidth.asp">
1769: * MSDN documentation</a>
1770: */
1771: public int jsxGet_scrollWidth() {
1772: return 10;
1773: }
1774:
1775: /**
1776: * Get the JavaScript property "parentElement".
1777: * <p>It is identical to {@link #jsxGet_parentNode()}
1778: * with the exception of <tt>HTML</tt>, which has a <tt>null</tt> parent element.
1779: * @return The parent element
1780: * @see #jsxGet_parentNode()
1781: */
1782: public Object jsxGet_parentElement() {
1783: if ("html".equalsIgnoreCase(getDomNodeOrDie().getNodeName())) {
1784: return null;
1785: } else {
1786: return jsxGet_parentNode();
1787: }
1788: }
1789:
1790: /**
1791: * Implement the scrollIntoView() javascript function but don't actually do
1792: * anything. The requirement
1793: * is just to prevent scripts that call that method from failing
1794: */
1795: public void jsxFunction_scrollIntoView() {
1796: }
1797:
1798: /**
1799: * Retrieves an object that specifies the bounds of a collection of TextRectangle objects.
1800: * @return an object that specifies the bounds of a collection of TextRectangle objects.
1801: */
1802: public TextRectangle jsxFunction_getBoundingClientRect() {
1803: final TextRectangle textRectangle = new TextRectangle();
1804: textRectangle.setParentScope(getWindow());
1805: textRectangle.setPrototype(getPrototype(textRectangle
1806: .getClass()));
1807: return textRectangle;
1808: }
1809:
1810: /**
1811: * Retrieves a collection of rectangles that describes the layout of the contents of an object
1812: * or range within the client. Each rectangle describes a single line.
1813: * @return a collection of rectangles that describes the layout of the contents.
1814: */
1815: public Object jsxFunction_getClientRects() {
1816: return new NativeArray(0);
1817: }
1818:
1819: /**
1820: * Sets an expression for the specified HTMLElement.
1821: *
1822: * @param propertyName Specifies the name of the property to which expression is added.
1823: * @param expression specifies any valid script statement without quotations or semicolons.
1824: * This string can include references to other properties on the current page.
1825: * Array references are not allowed on object properties included in this script.
1826: * @param language specified the language used.
1827: */
1828: public void jsxFunction_setExpression(final String propertyName,
1829: final String expression, final String language) {
1830: //empty implementation
1831: }
1832:
1833: /**
1834: * Removes the expression from the specified property.
1835: *
1836: * @param propertyName Specifies the name of the property from which to remove an expression.
1837: * @return true if the expression was successfully removed.
1838: */
1839: public boolean jsxFunction_removeExpression(
1840: final String propertyName) {
1841: return true;
1842: }
1843:
1844: /**
1845: * Retrieves an auto-generated, unique identifier for the object.
1846: * <b>Note</b> The unique ID generated is not guaranteed to be the same every time the page is loaded.
1847: * @return an auto-generated, unique identifier for the object.
1848: */
1849: public String jsxGet_uniqueID() {
1850: if (uniqueID_ == null) {
1851: uniqueID_ = "ms__id" + UniqueID_Counter_++;
1852: }
1853: return uniqueID_;
1854: }
1855:
1856: /**
1857: * Dispatches an event into the event system (standards-conformant browsers only). See
1858: * <a href="http://developer.mozilla.org/en/docs/DOM:element.dispatchEvent">the Gecko
1859: * DOM reference</a> for more information.
1860: *
1861: * @param event the event to be dispatched
1862: * @return <tt>false</tt> if at least one of the event handlers which handled the event
1863: * called <tt>preventDefault</tt>; <tt>true</tt> otherwise
1864: */
1865: public boolean jsxFunction_dispatchEvent(final Event event) {
1866: final HtmlElement element = getHtmlElementOrDie();
1867: if (event instanceof MouseEvent
1868: && element instanceof ClickableElement) {
1869: if (event.jsxGet_type().equals(MouseEvent.TYPE_CLICK)) {
1870: try {
1871: ((ClickableElement) element).click(event);
1872: } catch (final IOException e) {
1873: throw Context
1874: .reportRuntimeError("Error calling click(): "
1875: + e.getMessage());
1876: }
1877: } else if (event.jsxGet_type().equals(
1878: MouseEvent.TYPE_DBL_CLICK)) {
1879: try {
1880: ((ClickableElement) element).dblClick(event
1881: .jsxGet_shiftKey(), event.jsxGet_ctrlKey(),
1882: event.jsxGet_altKey());
1883: } catch (final IOException e) {
1884: throw Context
1885: .reportRuntimeError("Error calling dblClick(): "
1886: + e.getMessage());
1887: }
1888: } else {
1889: fireEvent(event);
1890: }
1891: } else {
1892: fireEvent(event);
1893: }
1894: return !event.isPreventDefault();
1895: }
1896:
1897: /**
1898: * Fires a specified event on this element (IE only). See the
1899: * <a href="http://msdn2.microsoft.com/en-us/library/ms536423.aspx">MSDN documentation</a>
1900: * for more information.
1901: * @param cx the JavaScript context
1902: * @param thisObj the element instance on which this method was invoked
1903: * @param args contains the event type as a string, and an optional event template
1904: * @param f the function being invoked
1905: * @return <tt>true</tt> if the event fired successfully, <tt>false</tt> if it was cancelled
1906: */
1907: public static ScriptResult jsxFunction_fireEvent(final Context cx,
1908: final Scriptable this Obj, final Object[] args,
1909: final Function f) {
1910:
1911: final HTMLElement me = (HTMLElement) this Obj;
1912:
1913: // Extract the function arguments.
1914: final String type = (String) args[0];
1915: final Event template;
1916: if (args.length > 1) {
1917: template = (Event) args[1];
1918: } else {
1919: template = null;
1920: }
1921:
1922: // Create the event, whose class will depend on the type specified.
1923: final Event event;
1924: final String cleanedType = StringUtils.removeStart(type
1925: .toLowerCase(), "on");
1926: if (MouseEvent.isMouseEvent(cleanedType)) {
1927: event = new MouseEvent();
1928: event.setPrototype(me.getPrototype(MouseEvent.class));
1929: } else {
1930: event = new Event();
1931: event.setPrototype(me.getPrototype(Event.class));
1932: }
1933: event.setParentScope(me.getWindow());
1934:
1935: // Initialize the event using the template, if provided.
1936: if (template != null) {
1937: event.copyPropertiesFrom(template);
1938: }
1939:
1940: // These four properties have predefined values, independent of the template.
1941: event.jsxSet_cancelBubble(false);
1942: event.jsxSet_returnValue(Boolean.TRUE);
1943: event.jsxSet_srcElement(me);
1944: event.setEventType(cleanedType);
1945:
1946: return me.fireEvent(event);
1947: }
1948:
1949: /**
1950: * Return the html element that corresponds to this javascript object or throw an exception
1951: * if one cannot be found.
1952: * @return The html element
1953: * @exception IllegalStateException If the html element could not be found.
1954: */
1955: public final HtmlElement getHtmlElementOrDie()
1956: throws IllegalStateException {
1957: return (HtmlElement) getDomNodeOrDie();
1958: }
1959:
1960: /**
1961: * Return the html element that corresponds to this javascript object
1962: * or null if an element hasn't been set.
1963: * @return The html element or null
1964: */
1965: public final HtmlElement getHtmlElementOrNull() {
1966: return (HtmlElement) getDomNodeOrNull();
1967: }
1968:
1969: /**
1970: * Remove focus from this element.
1971: */
1972: public void jsxFunction_blur() {
1973: final HtmlElement element = (HtmlElement) getDomNodeOrDie();
1974: element.blur();
1975: }
1976:
1977: /**
1978: * Set the focus to this element.
1979: */
1980: public void jsxFunction_focus() {
1981: final HtmlElement element = (HtmlElement) getDomNodeOrDie();
1982: element.focus();
1983: }
1984: }
|