Source Code Cross Referenced for HtmlElement.java in  » Testing » htmlunit » com » gargoylesoftware » htmlunit » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Testing » htmlunit » com.gargoylesoftware.htmlunit.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.html;
0039:
0040:        import java.io.IOException;
0041:        import java.io.PrintWriter;
0042:        import java.io.StringWriter;
0043:        import java.util.ArrayList;
0044:        import java.util.Collections;
0045:        import java.util.HashMap;
0046:        import java.util.Iterator;
0047:        import java.util.List;
0048:        import java.util.Map;
0049:        import java.util.NoSuchElementException;
0050:        import java.util.Set;
0051:
0052:        import org.apache.commons.collections.map.ListOrderedMap;
0053:        import org.apache.commons.lang.ClassUtils;
0054:        import org.apache.commons.lang.StringEscapeUtils;
0055:        import org.mozilla.javascript.BaseFunction;
0056:        import org.mozilla.javascript.Context;
0057:        import org.mozilla.javascript.ContextAction;
0058:        import org.mozilla.javascript.Function;
0059:
0060:        import com.gargoylesoftware.htmlunit.Assert;
0061:        import com.gargoylesoftware.htmlunit.ElementNotFoundException;
0062:        import com.gargoylesoftware.htmlunit.Page;
0063:        import com.gargoylesoftware.htmlunit.ScriptResult;
0064:        import com.gargoylesoftware.htmlunit.javascript.host.Event;
0065:        import com.gargoylesoftware.htmlunit.javascript.host.EventHandler;
0066:        import com.gargoylesoftware.htmlunit.javascript.host.HTMLElement;
0067:        import com.gargoylesoftware.htmlunit.javascript.host.MouseEvent;
0068:
0069:        /**
0070:         * An abstract wrapper for html elements
0071:         *
0072:         * @version $Revision: 2149 $
0073:         * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
0074:         * @author <a href="mailto:gudujarlson@sf.net">Mike J. Bresnahan</a>
0075:         * @author David K. Taylor
0076:         * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
0077:         * @author David D. Kilzer
0078:         * @author Mike Gallaher
0079:         * @author Denis N. Antonioli
0080:         * @author Marc Guillemot
0081:         * @author Ahmed Ashour
0082:         */
0083:        public abstract class HtmlElement extends DomElement {
0084:
0085:            /** Constant meaning that the specified attribute was not defined. */
0086:            public static final String ATTRIBUTE_NOT_DEFINED = new String("");
0087:
0088:            /** Constant meaning that the specified attribute was found but its value was empty. */
0089:            public static final String ATTRIBUTE_VALUE_EMPTY = new String("");
0090:
0091:            /**
0092:             * Constant indicating that a tab index value is out of bounds (less than <tt>0</tt> or greater
0093:             * than <tt>32767</tt>).
0094:             *
0095:             * @see #getTabIndex()
0096:             */
0097:            public static final Short TAB_INDEX_OUT_OF_BOUNDS = new Short(
0098:                    Short.MIN_VALUE);
0099:
0100:            /** The map holding the attributes, keyed by name. */
0101:            private Map attributes_;
0102:
0103:            /** The map holding the namespaces, keyed by URI. */
0104:            private Map namespaces_ = new HashMap();
0105:
0106:            private List/* HtmlAttributeChangeListener */attributeListeners_;
0107:
0108:            /**
0109:             * Creates an instance.
0110:             *
0111:             * @param namespaceURI the URI that identifies an XML namespace.
0112:             * @param qualifiedName The qualified name of the element type to instantiate
0113:             * @param htmlPage The page that contains this element
0114:             * @param attributes a map ready initialized with the attributes for this element, or
0115:             * <code>null</code>. The map will be stored as is, not copied.
0116:             */
0117:            protected HtmlElement(final String namespaceURI,
0118:                    final String qualifiedName, final HtmlPage htmlPage,
0119:                    final Map attributes) {
0120:                super (namespaceURI, qualifiedName, htmlPage);
0121:                if (attributes != null) {
0122:                    attributes_ = upgradeAttributes(htmlPage, attributes);
0123:                    // The HtmlAttr objects are created before the HtmlElement, so we need to go set the
0124:                    // parent HtmlElement, now.  Also index the namespaces while we are at it.
0125:                    final Iterator entryIterator = attributes_.values()
0126:                            .iterator();
0127:                    while (entryIterator.hasNext()) {
0128:                        final HtmlAttr entry = (HtmlAttr) entryIterator.next();
0129:                        entry.setParentNode(this );
0130:                        final String attrNamespaceURI = entry.getNamespaceURI();
0131:                        if (attrNamespaceURI != null) {
0132:                            namespaces_
0133:                                    .put(attrNamespaceURI, entry.getPrefix());
0134:                        }
0135:                    }
0136:                } else {
0137:                    attributes_ = Collections.EMPTY_MAP;
0138:                }
0139:            }
0140:
0141:            /**
0142:             * Convert old attribute Map<String, String> to Map<String, HtmlAttr> to support backwards
0143:             * compatibility.
0144:             * @param attributes old attributes to be upgraded
0145:             * @return upgraded attribute map
0146:             */
0147:            private Map upgradeAttributes(final HtmlPage htmlPage,
0148:                    final Map attributes) {
0149:                Map upgradedAttributes = attributes;
0150:                if (attributes != null && attributes.size() > 0) {
0151:                    final Object firstValue = attributes.values().iterator()
0152:                            .next();
0153:                    if (firstValue instanceof  String) {
0154:                        upgradedAttributes = createAttributeMap(attributes
0155:                                .size());
0156:                        final Iterator entryIterator = attributes.entrySet()
0157:                                .iterator();
0158:                        while (entryIterator.hasNext()) {
0159:                            final Map.Entry entry = (Map.Entry) entryIterator
0160:                                    .next();
0161:                            addAttributeToMap(htmlPage, upgradedAttributes,
0162:                                    null, (String) entry.getKey(),
0163:                                    (String) entry.getValue());
0164:                        }
0165:                    }
0166:                }
0167:                return upgradedAttributes;
0168:            }
0169:
0170:            /**
0171:             * Overrides {@link DomNode#cloneNode(boolean)} so clone gets its own Map of attributes.
0172:             * {@inheritDoc}
0173:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0174:             * different.  Use {@link #cloneDomNode(boolean)} instead.
0175:             */
0176:            public DomNode cloneNode(final boolean deep) {
0177:                return cloneDomNode(deep);
0178:            }
0179:
0180:            /**
0181:             * Overrides {@link DomNode#cloneDomNode(boolean)} so clone gets its own Map of attributes.
0182:             * {@inheritDoc}
0183:             */
0184:            public DomNode cloneDomNode(final boolean deep) {
0185:                final HtmlElement newNode = (HtmlElement) super 
0186:                        .cloneDomNode(deep);
0187:                final Set keySet = attributes_.keySet();
0188:                newNode.attributes_ = createAttributeMap(keySet.size());
0189:                for (final Iterator it = keySet.iterator(); it.hasNext();) {
0190:                    final Object key = it.next();
0191:                    final HtmlAttr attr = (HtmlAttr) attributes_.get(key);
0192:                    newNode.setAttributeValue(attr.getNamespaceURI(), attr
0193:                            .getQualifiedName(), attr.getNodeValue());
0194:                }
0195:                return newNode;
0196:            }
0197:
0198:            /**
0199:             * Return the value of the attribute specified by name or an empty string.  If the
0200:             * result is an empty string then it will be either {@link #ATTRIBUTE_NOT_DEFINED}
0201:             * if the attribute wasn't specified or {@link #ATTRIBUTE_VALUE_EMPTY} if the
0202:             * attribute was specified but it was empty.
0203:             *
0204:             * @param attributeName the name of the attribute
0205:             * @return The value of the attribute or {@link #ATTRIBUTE_NOT_DEFINED}
0206:             * or {@link #ATTRIBUTE_VALUE_EMPTY}
0207:             */
0208:            public final String getAttribute(final String attributeName) {
0209:                return getAttributeValue(attributeName);
0210:            }
0211:
0212:            /**
0213:             * Return the qualified name (prefix:local) for the namespace and local name.
0214:             *
0215:             * @param namespaceURI the URI that identifies an XML namespace.
0216:             * @param localName The name within the namespace.
0217:             * @return The qualified name or just local name if the namespace is not fully defined.
0218:             */
0219:            private String getQualifiedName(final String namespaceURI,
0220:                    final String localName) {
0221:                final String qualifiedName;
0222:                if (namespaceURI != null) {
0223:                    final String prefix = (String) namespaces_
0224:                            .get(namespaceURI);
0225:                    if (prefix != null) {
0226:                        qualifiedName = prefix + ':' + localName;
0227:                    } else {
0228:                        qualifiedName = localName;
0229:                    }
0230:                } else {
0231:                    qualifiedName = localName;
0232:                }
0233:                return qualifiedName;
0234:            }
0235:
0236:            /**
0237:             * Return the value of the attribute specified by namespace and local name or an empty
0238:             * string.  If the result is an empty string then it will be either {@link #ATTRIBUTE_NOT_DEFINED}
0239:             * if the attribute wasn't specified or {@link #ATTRIBUTE_VALUE_EMPTY} if the
0240:             * attribute was specified but it was empty.
0241:             *
0242:             * @param namespaceURI the URI that identifies an XML namespace.
0243:             * @param localName The name within the namespace.
0244:             * @return The value of the attribute or {@link #ATTRIBUTE_NOT_DEFINED}
0245:             * or {@link #ATTRIBUTE_VALUE_EMPTY}
0246:             */
0247:            public final String getAttributeNS(final String namespaceURI,
0248:                    final String localName) {
0249:                return getAttributeValue(getQualifiedName(namespaceURI,
0250:                        localName));
0251:            }
0252:
0253:            /**
0254:             * {@inheritDoc}
0255:             */
0256:            public boolean hasAttributes() {
0257:                return attributes_.size() > 0;
0258:            }
0259:
0260:            /**
0261:             * Return whether the attribute specified by name has a value.
0262:             *
0263:             * @param attributeName the name of the attribute
0264:             * @return true if an attribute with the given name is specified on this element or has a
0265:             * default value, false otherwise.
0266:             */
0267:            public final boolean hasAttribute(final String attributeName) {
0268:                return attributes_.get(attributeName) != null;
0269:            }
0270:
0271:            /**
0272:             * Return whether the attribute specified by namespace and local name has a value.
0273:             *
0274:             * @param namespaceURI the URI that identifies an XML namespace.
0275:             * @param localName The name within the namespace.
0276:             * @return true if an attribute with the given name is specified on this element or has a
0277:             * default value, false otherwise.
0278:             */
0279:            public final boolean hasAttributeNS(final String namespaceURI,
0280:                    final String localName) {
0281:                return attributes_
0282:                        .get(getQualifiedName(namespaceURI, localName)) != null;
0283:            }
0284:
0285:            /**
0286:             * Return the value of the specified attribute or an empty string.  If the
0287:             * result is an empty string then it will be either {@link #ATTRIBUTE_NOT_DEFINED}
0288:             * if the attribute wasn't specified or {@link #ATTRIBUTE_VALUE_EMPTY} if the
0289:             * attribute was specified but it was empty.
0290:             *
0291:             * @param attributeName the name of the attribute
0292:             * @return The value of the attribute or {@link #ATTRIBUTE_NOT_DEFINED}
0293:             * or {@link #ATTRIBUTE_VALUE_EMPTY}
0294:             */
0295:            public final String getAttributeValue(final String attributeName) {
0296:                final HtmlAttr attr = (HtmlAttr) attributes_.get(attributeName
0297:                        .toLowerCase());
0298:
0299:                if (attr != null) {
0300:                    return attr.getNodeValue();
0301:                } else {
0302:                    return ATTRIBUTE_NOT_DEFINED;
0303:                }
0304:            }
0305:
0306:            /**
0307:             * Set the value of the attribute specified by name.
0308:             *
0309:             * @param attributeName the name of the attribute
0310:             * @param attributeValue The value of the attribute
0311:             */
0312:            public final void setAttribute(final String attributeName,
0313:                    final String attributeValue) {
0314:                setAttributeValue(null, attributeName, attributeValue);
0315:            }
0316:
0317:            /**
0318:             * Set the value of the attribute specified by namespace and qualified name.
0319:             *
0320:             * @param namespaceURI the URI that identifies an XML namespace.
0321:             * @param qualifiedName The qualified name (prefix:local) of the attribute.
0322:             * @param attributeValue The value of the attribute
0323:             */
0324:            public final void setAttributeNS(final String namespaceURI,
0325:                    final String qualifiedName, final String attributeValue) {
0326:                setAttributeValue(namespaceURI, qualifiedName, attributeValue);
0327:            }
0328:
0329:            /**
0330:             * Set the value of the specified attribute.
0331:             *
0332:             * @param attributeName the name of the attribute
0333:             * @param attributeValue The value of the attribute
0334:             */
0335:            public final void setAttributeValue(final String attributeName,
0336:                    final String attributeValue) {
0337:                setAttributeValue(null, attributeName, attributeValue);
0338:            }
0339:
0340:            /**
0341:             * Set the value of the specified attribute.
0342:             *
0343:             * @param namespaceURI the URI that identifies an XML namespace.
0344:             * @param qualifiedName The qualified name of the attribute
0345:             * @param attributeValue The value of the attribute
0346:             */
0347:            public void setAttributeValue(final String namespaceURI,
0348:                    final String qualifiedName, final String attributeValue) {
0349:                final String oldAttributeValue = getAttributeValue(qualifiedName);
0350:                String value = attributeValue;
0351:
0352:                if (attributes_ == Collections.EMPTY_MAP) {
0353:                    attributes_ = createAttributeMap(1);
0354:                }
0355:                if (value.length() == 0) {
0356:                    value = ATTRIBUTE_VALUE_EMPTY;
0357:                }
0358:
0359:                getPage().removeMappedElement(this );
0360:                final HtmlAttr newAttr = addAttributeToMap(getPage(),
0361:                        attributes_, namespaceURI, qualifiedName.toLowerCase(),
0362:                        value);
0363:                if (namespaceURI != null) {
0364:                    namespaces_.put(namespaceURI, newAttr.getPrefix());
0365:                }
0366:                getPage().addMappedElement(this );
0367:
0368:                final HtmlAttributeChangeEvent event;
0369:                if (oldAttributeValue == ATTRIBUTE_NOT_DEFINED) {
0370:                    event = new HtmlAttributeChangeEvent(this , qualifiedName,
0371:                            attributeValue);
0372:                } else {
0373:                    event = new HtmlAttributeChangeEvent(this , qualifiedName,
0374:                            oldAttributeValue);
0375:                }
0376:
0377:                if (oldAttributeValue == ATTRIBUTE_NOT_DEFINED) {
0378:                    fireHtmlAttributeAdded(event);
0379:                    getPage().fireHtmlAttributeAdded(event);
0380:                } else {
0381:                    fireHtmlAttributeReplaced(event);
0382:                    getPage().fireHtmlAttributeReplaced(event);
0383:                }
0384:            }
0385:
0386:            /**
0387:             * Removes an attribute specified by name from this element.
0388:             * @param attributeName the attribute attributeName
0389:             */
0390:            public final void removeAttribute(final String attributeName) {
0391:                final String value = getAttributeValue(attributeName);
0392:
0393:                getPage().removeMappedElement(this );
0394:                attributes_.remove(attributeName.toLowerCase());
0395:                getPage().addMappedElement(this );
0396:
0397:                final HtmlAttributeChangeEvent event = new HtmlAttributeChangeEvent(
0398:                        this , attributeName, value);
0399:                fireHtmlAttributeRemoved(event);
0400:                getPage().fireHtmlAttributeRemoved(event);
0401:            }
0402:
0403:            /**
0404:             * Removes an attribute specified by namespace and local name from this element.
0405:             * @param namespaceURI the URI that identifies an XML namespace.
0406:             * @param localName The name within the namespace.
0407:             */
0408:            public final void removeAttributeNS(final String namespaceURI,
0409:                    final String localName) {
0410:                removeAttribute(getQualifiedName(namespaceURI, localName));
0411:            }
0412:
0413:            /**
0414:             * Support for reporting HTML attribute changes.
0415:             * This method can be called when an attribute has been added and it will send the
0416:             * appropriate HtmlAttributeChangeEvent to any registered HtmlAttributeChangeListener.
0417:             *
0418:             * Note that this methods recursively calls this parent fireHtmlAttributeAdded.
0419:             *
0420:             * @param event The event.
0421:             */
0422:            protected void fireHtmlAttributeAdded(
0423:                    final HtmlAttributeChangeEvent event) {
0424:                if (attributeListeners_ != null) {
0425:                    synchronized (this ) {
0426:                        for (final Iterator iterator = attributeListeners_
0427:                                .iterator(); iterator.hasNext();) {
0428:                            ((HtmlAttributeChangeListener) iterator.next())
0429:                                    .attributeAdded(event);
0430:                        }
0431:                    }
0432:                }
0433:                final DomNode parentNode = getParentDomNode();
0434:                if (parentNode instanceof  HtmlElement) {
0435:                    ((HtmlElement) parentNode).fireHtmlAttributeAdded(event);
0436:                }
0437:            }
0438:
0439:            /**
0440:             * Support for reporting html attribute changes.
0441:             * This method can be called when an attribute has been replaced and it will send the
0442:             * appropriate HtmlAttributeChangeEvent to any registered HtmlAttributeChangeListener.
0443:             *
0444:             * Note that this methods recursively calls this parent fireHtmlAttributeReplaced.
0445:             *
0446:             * @param event The event.
0447:             */
0448:            protected void fireHtmlAttributeReplaced(
0449:                    final HtmlAttributeChangeEvent event) {
0450:                if (attributeListeners_ != null) {
0451:                    synchronized (this ) {
0452:                        for (final Iterator iterator = attributeListeners_
0453:                                .iterator(); iterator.hasNext();) {
0454:                            ((HtmlAttributeChangeListener) iterator.next())
0455:                                    .attributeReplaced(event);
0456:                        }
0457:                    }
0458:                }
0459:                final DomNode parentNode = getParentDomNode();
0460:                if (parentNode instanceof  HtmlElement) {
0461:                    ((HtmlElement) parentNode).fireHtmlAttributeReplaced(event);
0462:                }
0463:            }
0464:
0465:            /**
0466:             * Support for reporting html attribute changes.
0467:             * This method can be called when an attribute has been removed and it will send the
0468:             * appropriate HtmlAttributeChangeEvent to any registered HtmlAttributeChangeListener.
0469:             *
0470:             * Note that this methods recursively calls this parent fireHtmlAttributeRemoved.
0471:             *
0472:             * @param event The event.
0473:             */
0474:            protected void fireHtmlAttributeRemoved(
0475:                    final HtmlAttributeChangeEvent event) {
0476:                if (attributeListeners_ != null) {
0477:                    synchronized (this ) {
0478:                        for (final Iterator iterator = attributeListeners_
0479:                                .iterator(); iterator.hasNext();) {
0480:                            ((HtmlAttributeChangeListener) iterator.next())
0481:                                    .attributeRemoved(event);
0482:                        }
0483:                    }
0484:                }
0485:                final DomNode parentNode = getParentDomNode();
0486:                if (parentNode instanceof  HtmlElement) {
0487:                    ((HtmlElement) parentNode).fireHtmlAttributeRemoved(event);
0488:                }
0489:            }
0490:
0491:            /**
0492:             * Return true if the specified attribute has been defined.  This is neccessary
0493:             * in order to distinguish between an attribute that is set to an empty string
0494:             * and one that was not defined at all.
0495:             *
0496:             * @param attributeName The attribute to check
0497:             * @return true if the attribute is defined
0498:             */
0499:            public boolean isAttributeDefined(final String attributeName) {
0500:                return attributes_.get(attributeName.toLowerCase()) != null;
0501:            }
0502:
0503:            /**
0504:             * @return an iterator over the {@link HtmlAttr} objects representing the
0505:             * attributes of this element. Each entry holds a string key and a string value.
0506:             * The elements are ordered as found in the html source code.
0507:             */
0508:            public Iterator getAttributeEntriesIterator() {
0509:                return attributes_.values().iterator();
0510:            }
0511:
0512:            /**
0513:             * Return the tag name of this element.  The tag name is the actual html name.  For example
0514:             * the tag name for HtmlAnchor is "a" and the tag name for HtmlTable is "table".
0515:             * This tag name will always be in lowercase, no matter what case was used in the original
0516:             * document, when no namespace is defined.
0517:             *
0518:             * @return the tag name of this element.
0519:             */
0520:            public String getTagName() {
0521:                if (getNamespaceURI() == null) {
0522:                    return getLocalName().toLowerCase();
0523:                } else {
0524:                    return getQualifiedName();
0525:                }
0526:            }
0527:
0528:            /** @return the node type */
0529:            public short getNodeType() {
0530:                return org.w3c.dom.Node.ELEMENT_NODE;
0531:            }
0532:
0533:            /**
0534:             * @return The same value as returned by {@link #getTagName()},
0535:             */
0536:            public String getNodeName() {
0537:                return getTagName();
0538:            }
0539:
0540:            /**
0541:             * @return the identifier of this element.
0542:             */
0543:            public final String getId() {
0544:                return getAttributeValue("id");
0545:            }
0546:
0547:            /**
0548:             * Set the identifier this element.
0549:             *
0550:             * @param newId The new identifier of this element.
0551:             */
0552:            public final void setId(final String newId) {
0553:                setAttributeValue("id", newId);
0554:            }
0555:
0556:            /**
0557:             * Returns this element's tab index, if it has one. If the tab index is outside of the
0558:             * valid range (less than <tt>0</tt> or greater than <tt>32767</tt>), this method
0559:             * returns {@link #TAB_INDEX_OUT_OF_BOUNDS}. If this element does not have
0560:             * a tab index, or its tab index is otherwise invalid, this method returns <tt>null</tt>.
0561:             *
0562:             * @return this element's tab index
0563:             */
0564:            public Short getTabIndex() {
0565:                final String index = getAttributeValue("tabindex");
0566:                if (index == null || index.length() == 0) {
0567:                    return null;
0568:                }
0569:                try {
0570:                    final long l = Long.parseLong(index);
0571:                    if (l >= 0 && l <= Short.MAX_VALUE) {
0572:                        return new Short(new Long(l).shortValue());
0573:                    } else {
0574:                        return TAB_INDEX_OUT_OF_BOUNDS;
0575:                    }
0576:                } catch (final NumberFormatException e) {
0577:                    return null;
0578:                }
0579:            }
0580:
0581:            /**
0582:             * Return the element with the given name that enclosed this element or null if this element is
0583:             * no such element is found.
0584:             * @param tagName the name of the tag searched (case insensitive)
0585:             * @return See above
0586:             */
0587:            public HtmlElement getEnclosingElement(final String tagName) {
0588:                final String tagNameLC = tagName.toLowerCase();
0589:
0590:                DomNode currentNode = getParentDomNode();
0591:                while (currentNode != null) {
0592:                    if (currentNode instanceof  HtmlElement
0593:                            && currentNode.getNodeName().equals(tagNameLC)) {
0594:
0595:                        return (HtmlElement) currentNode;
0596:                    }
0597:                    currentNode = currentNode.getParentDomNode();
0598:                }
0599:                return null;
0600:            }
0601:
0602:            /**
0603:             * Return the form that enclosed this element or null if this element is not within a form.
0604:             *
0605:             * @return See above
0606:             */
0607:            public HtmlForm getEnclosingForm() {
0608:                return (HtmlForm) getEnclosingElement("form");
0609:            }
0610:
0611:            /**
0612:             * Return the form that enclosed this element or throw an exception if this element is not within a form.
0613:             *
0614:             * @return See above
0615:             * @throws IllegalStateException If the element is not within a form.
0616:             */
0617:            public HtmlForm getEnclosingFormOrDie()
0618:                    throws IllegalStateException {
0619:                final HtmlForm form = getEnclosingForm();
0620:                if (form == null) {
0621:                    throw new IllegalStateException(
0622:                            "Element is not contained within a form: " + this );
0623:                }
0624:
0625:                return form;
0626:            }
0627:
0628:            /**
0629:             * Simulate pressing a key on this element
0630:             *
0631:             * @param keyCode the key you wish to press
0632:             * @deprecated use {@link #type(char)} instead
0633:             */
0634:            public void keyDown(final int keyCode) {
0635:                keyDown(keyCode, false, false, false);
0636:            }
0637:
0638:            /**
0639:             * Simulate pressing a key on this element
0640:             *
0641:             * @param keyCode the key you wish to press
0642:             * @param shiftKey true if SHIFT is pressed
0643:             * @param ctrlKey true if CTRL is pressed
0644:             * @param altKey true if ALT is pressed
0645:             * @deprecated use {@link type(char, boolean, boolean, boolean)} instead
0646:             */
0647:            public void keyDown(final int keyCode, final boolean shiftKey,
0648:                    final boolean ctrlKey, final boolean altKey) {
0649:                if (this  instanceof  DisabledElement
0650:                        && ((DisabledElement) this ).isDisabled()) {
0651:                    return;
0652:                }
0653:                fireEvent(new Event(this , Event.TYPE_KEY_DOWN, keyCode,
0654:                        shiftKey, ctrlKey, altKey));
0655:            }
0656:
0657:            /**
0658:             * Simulates typing the specified text while this element has focus.
0659:             * @param text the text you with to simulate typing
0660:             * @exception IOException If an IO error occurs
0661:             */
0662:            public void type(final String text) throws IOException {
0663:                for (int i = 0; i < text.length(); i++) {
0664:                    type(text.charAt(i));
0665:                }
0666:            }
0667:
0668:            /**
0669:             * Simulates typing the specified text while this element has focus.
0670:             * @param text the text you with to simulate typing
0671:             * @param shiftKey true if SHIFT is pressed
0672:             * @param ctrlKey true if CTRL is pressed
0673:             * @param altKey true if ALT is pressed
0674:             * @exception IOException If an IO error occurs
0675:             */
0676:            public void type(final String text, final boolean shiftKey,
0677:                    final boolean ctrlKey, final boolean altKey)
0678:                    throws IOException {
0679:                for (int i = 0; i < text.length(); i++) {
0680:                    type(text.charAt(i), shiftKey, ctrlKey, altKey);
0681:                }
0682:            }
0683:
0684:            /**
0685:             * Simulates typing the specified character while this element has focus.
0686:             * @param c the character you with to simulate typing
0687:             * @return The page that occupies this window after typing.
0688:             * It may be the same window or it may be a freshly loaded one.
0689:             * @exception IOException If an IO error occurs
0690:             */
0691:            public Page type(final char c) throws IOException {
0692:                return type(c, false, false, false);
0693:            }
0694:
0695:            /**
0696:             * Simulates typing the specified character while this element has focus.
0697:             * Note that for some elements, typing '\n' submits the enclosed form.
0698:             * @param c the character you with to simulate typing
0699:             * @param shiftKey true if SHIFT is pressed
0700:             * @param ctrlKey true if CTRL is pressed
0701:             * @param altKey true if ALT is pressed
0702:             * @return The page that occupies this window after typing.
0703:             * It may be the same window or it may be a freshly loaded one.
0704:             * @exception IOException If an IO error occurs
0705:             */
0706:            public Page type(final char c, final boolean shiftKey,
0707:                    final boolean ctrlKey, final boolean altKey)
0708:                    throws IOException {
0709:                if (this  instanceof  DisabledElement
0710:                        && ((DisabledElement) this ).isDisabled()) {
0711:                    return getPage();
0712:                }
0713:                fireEvent(new Event(this , Event.TYPE_KEY_DOWN, c, shiftKey,
0714:                        ctrlKey, altKey));
0715:                fireEvent(new Event(this , Event.TYPE_KEY_PRESS, c, shiftKey,
0716:                        ctrlKey, altKey));
0717:                fireEvent(new Event(this , Event.TYPE_KEY_UP, c, shiftKey,
0718:                        ctrlKey, altKey));
0719:
0720:                final HtmlForm form = getEnclosingForm();
0721:                if (form != null && c == '\n' && isSubmittableByEnter()) {
0722:                    return form.submit((SubmittableElement) this );
0723:                } else {
0724:                    return getPage();
0725:                }
0726:            }
0727:
0728:            /**
0729:             * Returns true if clicking Enter (ASCII 10, or '\n') should submit the enclosed form (if any).
0730:             * Default implementation is false.
0731:             * @return true if clicking Enter should submit the enclosed form (if any).
0732:             */
0733:            protected boolean isSubmittableByEnter() {
0734:                return false;
0735:            }
0736:
0737:            /**
0738:             * recursively write the XML data for the node tree starting at <code>node</code>
0739:             *
0740:             * @param indent white space to indent child nodes
0741:             * @param printWriter writer where child nodes are written
0742:             */
0743:            protected void printXml(final String indent,
0744:                    final PrintWriter printWriter) {
0745:                final boolean hasChildren = (getFirstDomChild() != null);
0746:                printWriter.print(indent + "<");
0747:                printOpeningTagContentAsXml(printWriter);
0748:
0749:                if (!hasChildren && !isEmptyXmlTagExpanded()) {
0750:                    printWriter.println("/>");
0751:                } else {
0752:                    printWriter.println(">");
0753:                    printChildrenAsXml(indent, printWriter);
0754:                    printWriter.println(indent + "</" + getTagName() + ">");
0755:                }
0756:            }
0757:
0758:            /**
0759:             * Indicates if a node without children should be written in expanded form as xml
0760:             * (i.e. with closing tag rather than with "/&gt;")
0761:             * @return <code>false</code>
0762:             */
0763:            protected boolean isEmptyXmlTagExpanded() {
0764:                return false;
0765:            }
0766:
0767:            /**
0768:             * Prints the content between "&lt;" and "&gt;" (or "/&gt;") in the output of the tag name
0769:             * and its attributes in xml format.
0770:             * @param printWriter the writer to print in
0771:             */
0772:            protected void printOpeningTagContentAsXml(
0773:                    final PrintWriter printWriter) {
0774:                printWriter.print(getTagName());
0775:
0776:                for (final Iterator it = attributes_.keySet().iterator(); it
0777:                        .hasNext();) {
0778:                    printWriter.print(" ");
0779:                    final String name = (String) it.next();
0780:                    printWriter.print(name);
0781:                    printWriter.print("=\"");
0782:                    printWriter.print(StringEscapeUtils
0783:                            .escapeXml(((HtmlAttr) attributes_.get(name))
0784:                                    .getNodeValue()));
0785:                    printWriter.print("\"");
0786:                }
0787:            }
0788:
0789:            /**
0790:             * Return a string representation of this object
0791:             *
0792:             * @return See above
0793:             */
0794:            public String toString() {
0795:                final StringBuffer buffer = new StringBuffer();
0796:
0797:                buffer.append(ClassUtils.getShortClassName(getClass()));
0798:                buffer.append("[<");
0799:
0800:                final StringWriter writer = new StringWriter();
0801:                final PrintWriter printWriter = new PrintWriter(writer);
0802:                printOpeningTagContentAsXml(printWriter);
0803:                buffer.append(writer.toString());
0804:
0805:                buffer.append(">]");
0806:
0807:                return buffer.toString();
0808:            }
0809:
0810:            /**
0811:             * Throw an exception. This is a convenience during development only - it
0812:             * will likely be removed in the future.
0813:             */
0814:            protected final void notImplemented() {
0815:                throw new RuntimeException("Not implemented yet");
0816:            }
0817:
0818:            /**
0819:             * Assert that the specified string is not empty. Throw an exception if it is.
0820:             *
0821:             * @param description The description to pass into the exception if this string is empty
0822:             * @param string The string to check
0823:             * @throws IllegalArgumentException If the string is empty
0824:             */
0825:            protected final void assertNotEmpty(final String description,
0826:                    final String string) throws IllegalArgumentException {
0827:
0828:                if (string.length() == 0) {
0829:                    throw new IllegalArgumentException(
0830:                            "String may not be empty: " + description);
0831:                }
0832:            }
0833:
0834:            /**
0835:             * Search by the specified criteria and return the first HtmlElement that is found
0836:             *
0837:             * @param elementName The name of the element
0838:             * @param attributeName The name of the attribute
0839:             * @param attributeValue The value of the attribute
0840:             * @return The HtmlElement
0841:             * @exception ElementNotFoundException If a particular xml element could not be found in the dom model
0842:             */
0843:            public final HtmlElement getOneHtmlElementByAttribute(
0844:                    final String elementName, final String attributeName,
0845:                    final String attributeValue)
0846:                    throws ElementNotFoundException {
0847:
0848:                Assert.notNull("elementName", elementName);
0849:                Assert.notNull("attributeName", attributeName);
0850:                Assert.notNull("attributeValue", attributeValue);
0851:
0852:                final List list = getHtmlElementsByAttribute(elementName,
0853:                        attributeName, attributeValue);
0854:                final int listSize = list.size();
0855:                if (listSize == 0) {
0856:                    throw new ElementNotFoundException(elementName,
0857:                            attributeName, attributeValue);
0858:                }
0859:
0860:                return (HtmlElement) list.get(0);
0861:            }
0862:
0863:            /**
0864:             * Return the html element with the specified id. If more than one element
0865:             * has this id (not allowed by the html spec) then return the first one.
0866:             *
0867:             * @param id The id value to search by
0868:             * @return The html element found
0869:             * @exception ElementNotFoundException If no element was found that matches the id
0870:             */
0871:            public HtmlElement getHtmlElementById(final String id)
0872:                    throws ElementNotFoundException {
0873:
0874:                return getPage().getHtmlElementById(id);
0875:            }
0876:
0877:            /**
0878:             * Return true if there is a element with the specified id. This method
0879:             * is intended for situations where it is enough to know whether a specific
0880:             * element is present in the document.<p>
0881:             *
0882:             * Implementation note: This method calls getHtmlElementById() internally
0883:             * so writing code like the following would be extremely inefficient.
0884:             * <pre>
0885:             * if (hasHtmlElementWithId(id)) {
0886:             *     HtmlElement element = getHtmlElementWithId(id)
0887:             *     ...
0888:             * }
0889:             * </pre>
0890:             *
0891:             * @param id The id to search by
0892:             * @return true if an element was found with the specified id.
0893:             */
0894:            public boolean hasHtmlElementWithId(final String id) {
0895:                try {
0896:                    getHtmlElementById(id);
0897:                    return true;
0898:                } catch (final ElementNotFoundException e) {
0899:                    return false;
0900:                }
0901:            }
0902:
0903:            /**
0904:             * Search by the specified criteria and return all the HtmlElement that are found
0905:             *
0906:             * @param elementName The name of the element
0907:             * @param attributeName The name of the attribute
0908:             * @param attributeValue The value of the attribute
0909:             * @return A list of HtmlElements
0910:             */
0911:            public final List getHtmlElementsByAttribute(
0912:                    final String elementName, final String attributeName,
0913:                    final String attributeValue) {
0914:
0915:                final List list = new ArrayList();
0916:                final DescendantElementsIterator iterator = new DescendantElementsIterator();
0917:                final String lowerCaseTagName = elementName.toLowerCase();
0918:
0919:                while (iterator.hasNext()) {
0920:                    final HtmlElement next = iterator.nextElement();
0921:                    if (next.getTagName().equals(lowerCaseTagName)) {
0922:                        final String attValue = next
0923:                                .getAttributeValue(attributeName);
0924:                        if (attValue != null && attValue.equals(attributeValue)) {
0925:                            list.add(next);
0926:                        }
0927:                    }
0928:                }
0929:                return list;
0930:            }
0931:
0932:            /**
0933:             * Given a list of tag names, return the html elements that correspond to any matching element
0934:             *
0935:             * @param acceptableTagNames The list of tag names to search by.
0936:             * @return The list of tag names
0937:             */
0938:            public final List getHtmlElementsByTagNames(
0939:                    final List acceptableTagNames) {
0940:                final List list = new ArrayList();
0941:                final Iterator iterator = acceptableTagNames.iterator();
0942:
0943:                while (iterator.hasNext()) {
0944:                    final String next = iterator.next().toString()
0945:                            .toLowerCase();
0946:                    list.addAll(getHtmlElementsByTagName(next));
0947:                }
0948:                return list;
0949:            }
0950:
0951:            /**
0952:             * Given a list of tag names, return the html elements that correspond to any matching element
0953:             *
0954:             * @param tagName the tag name to match
0955:             * @return The list of tag names
0956:             */
0957:            public final List getHtmlElementsByTagName(final String tagName) {
0958:                final List list = new ArrayList();
0959:                final DescendantElementsIterator iterator = new DescendantElementsIterator();
0960:                final String lowerCaseTagName = tagName.toLowerCase();
0961:
0962:                while (iterator.hasNext()) {
0963:                    final HtmlElement next = iterator.nextElement();
0964:                    if (lowerCaseTagName.equals(next.getTagName())) {
0965:                        list.add(next);
0966:                    }
0967:                }
0968:                return list;
0969:            }
0970:
0971:            /**
0972:             * Appends a child element to this HTML element with the specified tag name
0973:             * if this HTML element does not already have a child with that tag name.
0974:             * Returns the appended child element, or the first existent child element
0975:             * with the specified tag name if none was appended.
0976:             * @param tagName the tag name of the child to append
0977:             * @return the added child, or the first existing child if none was added
0978:             */
0979:            public final HtmlElement appendChildIfNoneExists(
0980:                    final String tagName) {
0981:                final HtmlElement child;
0982:                final List children = getHtmlElementsByTagName(tagName);
0983:                if (children.isEmpty()) {
0984:                    // Add a new child and return it.
0985:                    child = getPage().createHtmlElement(tagName);
0986:                    appendDomChild(child);
0987:                } else {
0988:                    // Return the first existing child.
0989:                    child = (HtmlElement) children.get(0);
0990:                }
0991:                return child;
0992:            }
0993:
0994:            /**
0995:             * Removes the <tt>i</tt>th child element with the specified tag name
0996:             * from all relationships, if possible.
0997:             * @param tagName the tag name of the child to remove
0998:             * @param i the index of the child to remove
0999:             */
1000:            public final void removeChild(final String tagName, final int i) {
1001:                final List children = getHtmlElementsByTagName(tagName);
1002:                if (i >= 0 && i < children.size()) {
1003:                    final HtmlElement child = (HtmlElement) children.get(i);
1004:                    child.remove();
1005:                }
1006:            }
1007:
1008:            /**
1009:             * @return an iterator over the HtmlElement children of this object, i.e. excluding the
1010:             * non-element nodes
1011:             */
1012:            public final Iterator getChildElementsIterator() {
1013:                return new ChildElementsIterator();
1014:            }
1015:
1016:            /**
1017:             * an iterator over the HtmlElement children
1018:             */
1019:            protected class ChildElementsIterator implements  Iterator {
1020:
1021:                private HtmlElement nextElement_;
1022:
1023:                /** constructor */
1024:                public ChildElementsIterator() {
1025:                    if (getFirstDomChild() != null) {
1026:                        if (getFirstDomChild() instanceof  HtmlElement) {
1027:                            nextElement_ = (HtmlElement) getFirstDomChild();
1028:                        } else {
1029:                            setNextElement(getFirstDomChild());
1030:                        }
1031:                    }
1032:                }
1033:
1034:                /** @return is there a next one ? */
1035:                public boolean hasNext() {
1036:                    return nextElement_ != null;
1037:                }
1038:
1039:                /** @return the next one */
1040:                public Object next() {
1041:                    return nextElement();
1042:                }
1043:
1044:                /** remove the current one */
1045:                public void remove() {
1046:                    if (nextElement_ == null) {
1047:                        throw new IllegalStateException();
1048:                    }
1049:                    final DomNode sibling = nextElement_
1050:                            .getPreviousDomSibling();
1051:                    if (sibling != null) {
1052:                        sibling.remove();
1053:                    }
1054:                }
1055:
1056:                /** @return the next element */
1057:                public HtmlElement nextElement() {
1058:                    if (nextElement_ != null) {
1059:                        final HtmlElement result = nextElement_;
1060:                        setNextElement(nextElement_);
1061:                        return result;
1062:                    } else {
1063:                        throw new NoSuchElementException();
1064:                    }
1065:                }
1066:
1067:                private void setNextElement(final DomNode node) {
1068:                    DomNode next = node.getNextDomSibling();
1069:                    while (next != null && !(next instanceof  HtmlElement)) {
1070:                        next = next.getNextDomSibling();
1071:                    }
1072:                    nextElement_ = (HtmlElement) next;
1073:                }
1074:            }
1075:
1076:            /**
1077:             * Converts an iteration of plain {@link java.util.Map.Entry} into an iteration of {@link HtmlAttr}.
1078:             * @deprecated This class is no longer used since attributes are now represented by HtmlAttr.
1079:             * @author Denis N. Antonioli
1080:             */
1081:            public static class MapEntryWrappingIterator implements  Iterator {
1082:                /**
1083:                 * The original Iterator on the attribute map.
1084:                 */
1085:                private final Iterator baseIter_;
1086:
1087:                /**
1088:                 * Wraps a new iterator around an iterator of attributes.
1089:                 *
1090:                 * @param iterator An iterator of Map.Entry.
1091:                 * @param htmlElement the Parent of all the attributes.
1092:                 */
1093:                public MapEntryWrappingIterator(final Iterator iterator,
1094:                        final HtmlElement htmlElement) {
1095:                    baseIter_ = iterator;
1096:                }
1097:
1098:                /**
1099:                 * Delegates to wrapped Iterator.
1100:                 *
1101:                 * @return true if the iterator has more elements.
1102:                 */
1103:                public boolean hasNext() {
1104:                    return baseIter_.hasNext();
1105:                }
1106:
1107:                /**
1108:                 * Wraps the next entry into a new HtmlAttr.
1109:                 *
1110:                 * @return Next entry.
1111:                 */
1112:                public Object next() {
1113:                    return ((Map.Entry) baseIter_.next()).getValue();
1114:                }
1115:
1116:                /**
1117:                 * Delegates to wrapped Iterator.
1118:                 */
1119:                public void remove() {
1120:                    baseIter_.remove();
1121:                }
1122:            }
1123:
1124:            /**
1125:             * Create an attribute map as needed by HtmlElement.  This is just used by the element factories.
1126:             * @param attributeCount the initial number of attributes to be added to the map.
1127:             * @return the attribute map.
1128:             */
1129:            static Map createAttributeMap(final int attributeCount) {
1130:                return ListOrderedMap.decorate(new HashMap(attributeCount)); // preserve insertion order
1131:            }
1132:
1133:            /**
1134:             * Add an attribute to the attribute map.  This is just used by the element factories.
1135:             * @param attributeMap the attribute map where the attribute will be added.
1136:             * @param namespaceURI the URI that identifies an XML namespace.
1137:             * @param qualifiedName The qualified name of the attribute
1138:             * @param value The value of the attribute
1139:             */
1140:            static HtmlAttr addAttributeToMap(final HtmlPage page,
1141:                    final Map attributeMap, final String namespaceURI,
1142:                    final String qualifiedName, final String value) {
1143:                final HtmlAttr newAttr = new HtmlAttr(page, namespaceURI,
1144:                        qualifiedName, value);
1145:                attributeMap.put(qualifiedName, newAttr);
1146:                return newAttr;
1147:            }
1148:
1149:            /**
1150:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1151:             * Return a Function to be executed when a given event occurs.
1152:             * @param eventName Name of event such as "onclick" or "onblur", etc.
1153:             * @return A rhino javascript executable Function, or null if no event
1154:             * handler has been defined
1155:             */
1156:            public final Function getEventHandler(final String eventName) {
1157:                final HTMLElement jsObj = (HTMLElement) getScriptObject();
1158:                return jsObj.getEventHandler(eventName);
1159:            }
1160:
1161:            /**
1162:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1163:             * Register a Function as an event handler.
1164:             * @param eventName Name of event such as "onclick" or "onblur", etc.
1165:             * @param eventHandler A rhino javascript executable Function
1166:             */
1167:            public final void setEventHandler(final String eventName,
1168:                    final Function eventHandler) {
1169:                final HTMLElement jsObj = (HTMLElement) getScriptObject();
1170:                jsObj.setEventHandler(eventName, eventHandler);
1171:            }
1172:
1173:            /**
1174:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1175:             * Register a snippet of javascript code as an event handler.  The javascript code will
1176:             * be wrapped inside a unique function declaration which provides one argument named
1177:             * "event"
1178:             * @param eventName Name of event such as "onclick" or "onblur", etc.
1179:             * @param jsSnippet executable javascript code
1180:             */
1181:            public final void setEventHandler(final String eventName,
1182:                    final String jsSnippet) {
1183:                final BaseFunction function = new EventHandler(this , eventName,
1184:                        jsSnippet);
1185:                setEventHandler(eventName, function);
1186:                getLog().debug(
1187:                        "Created event handler " + function.getFunctionName()
1188:                                + " for " + eventName + " on " + this );
1189:            }
1190:
1191:            /**
1192:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1193:             * Removes the specified event handler.
1194:             * @param eventName Name of the event such as "onclick" or "onblur", etc.
1195:             */
1196:            public final void removeEventHandler(final String eventName) {
1197:                setEventHandler(eventName, (Function) null);
1198:            }
1199:
1200:            /**
1201:             * Adds an HtmlAttributeChangeListener to the listener list.
1202:             * The listener is registered for all attributes of this HtmlElement,
1203:             * as well as descendant elements.
1204:             *
1205:             * @param listener the attribute change listener to be added.
1206:             * @see #removeHtmlAttributeChangeListener(HtmlAttributeChangeListener)
1207:             */
1208:            public void addHtmlAttributeChangeListener(
1209:                    final HtmlAttributeChangeListener listener) {
1210:                Assert.notNull("listener", listener);
1211:                synchronized (this ) {
1212:                    if (attributeListeners_ == null) {
1213:                        attributeListeners_ = new ArrayList();
1214:                    }
1215:                    attributeListeners_.add(listener);
1216:                }
1217:            }
1218:
1219:            /**
1220:             * Removes an HtmlAttributeChangeListener from the listener list.
1221:             * This method should be used to remove HtmlAttributeChangeListener that were registered
1222:             * for all attributes of this HtmlElement, as well as descendant elements.
1223:             *
1224:             * @param listener the attribute change listener to be removed.
1225:             * @see #addHtmlAttributeChangeListener(HtmlAttributeChangeListener)
1226:             */
1227:            public void removeHtmlAttributeChangeListener(
1228:                    final HtmlAttributeChangeListener listener) {
1229:                Assert.notNull("listener", listener);
1230:                synchronized (this ) {
1231:                    if (attributeListeners_ != null) {
1232:                        attributeListeners_.remove(listener);
1233:                    }
1234:                }
1235:            }
1236:
1237:            /**
1238:             * Shortcut for {@link #fireEvent(Event)}.
1239:             * @param eventType the event type (like "load", "click")
1240:             * @return the execution result. <code>null</code> if nothing is executed.
1241:             */
1242:            public ScriptResult fireEvent(final String eventType) {
1243:                return fireEvent(new Event(this , eventType));
1244:            }
1245:
1246:            /**
1247:             * Fire the event on the element. Nothing is done if JavaScript is disabled
1248:             * @param event the event to fire.
1249:             * @return the execution result. <code>null</code> if nothing is executed.
1250:             */
1251:            public ScriptResult fireEvent(final Event event) {
1252:                if (!getPage().getWebClient().isJavaScriptEnabled()) {
1253:                    return null;
1254:                }
1255:
1256:                getLog().debug("Firing " + event);
1257:                final HTMLElement jsElt = (HTMLElement) getScriptObject();
1258:                final ContextAction action = new ContextAction() {
1259:                    public Object run(final Context cx) {
1260:                        return jsElt.fireEvent(event);
1261:                    }
1262:                };
1263:
1264:                final ScriptResult result = (ScriptResult) Context.call(action);
1265:                final boolean isIE = getPage().getWebClient()
1266:                        .getBrowserVersion().isIE();
1267:                if ((!isIE && event.isPreventDefault())
1268:                        || (isIE && ScriptResult.isFalse(result))) {
1269:                    preventDefault();
1270:                }
1271:                return result;
1272:            }
1273:
1274:            /**
1275:             * This method is called if the current fired event is canceled by <tt>preventDefault()</tt> in FireFox,
1276:             * or by returning <tt>false</tt> in Internet Explorer.
1277:             *
1278:             * The default implementation does nothing.
1279:             */
1280:            protected void preventDefault() {
1281:            }
1282:
1283:            /**
1284:             * Simulate moving the mouse over this element.
1285:             *
1286:             * @return The page that occupies this window after the mouse moves over this element.
1287:             * It may be the same window or it may be a freshly loaded one.
1288:             */
1289:            public Page mouseOver() {
1290:                return mouseOver(false, false, false, MouseEvent.BUTTON_LEFT);
1291:            }
1292:
1293:            /**
1294:             * Simulate moving the mouse over this element.
1295:             *
1296:             * @param shiftKey true if SHIFT is pressed
1297:             * @param ctrlKey true if CTRL is pressed
1298:             * @param altKey true if ALT is pressed
1299:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1300:             *        or {@link MouseEvent#BUTTON_RIGHT}
1301:             *
1302:             * @return The page that occupies this window after the mouse moves over this element.
1303:             * It may be the same window or it may be a freshly loaded one.
1304:             */
1305:            public Page mouseOver(final boolean shiftKey,
1306:                    final boolean ctrlKey, final boolean altKey,
1307:                    final int button) {
1308:                return doMouseEvent(MouseEvent.TYPE_MOUSE_OVER, shiftKey,
1309:                        ctrlKey, altKey, button);
1310:            }
1311:
1312:            /**
1313:             * Simulate moving the mouse inside this element.
1314:             *
1315:             * @return The page that occupies this window after the mouse moves inside this element.
1316:             * It may be the same window or it may be a freshly loaded one.
1317:             */
1318:            public Page mouseMove() {
1319:                return mouseMove(false, false, false, MouseEvent.BUTTON_LEFT);
1320:            }
1321:
1322:            /**
1323:             * Simulate moving the mouse inside this element.
1324:             *
1325:             * @param shiftKey true if SHIFT is pressed
1326:             * @param ctrlKey true if CTRL is pressed
1327:             * @param altKey true if ALT is pressed
1328:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1329:             *        or {@link MouseEvent#BUTTON_RIGHT}
1330:             *
1331:             * @return The page that occupies this window after the mouse moves inside this element.
1332:             * It may be the same window or it may be a freshly loaded one.
1333:             */
1334:            public Page mouseMove(final boolean shiftKey,
1335:                    final boolean ctrlKey, final boolean altKey,
1336:                    final int button) {
1337:                return doMouseEvent(MouseEvent.TYPE_MOUSE_MOVE, shiftKey,
1338:                        ctrlKey, altKey, button);
1339:            }
1340:
1341:            /**
1342:             * Simulate moving the mouse out of this element.
1343:             *
1344:             * @return The page that occupies this window after the mouse moves out of this element.
1345:             * It may be the same window or it may be a freshly loaded one.
1346:             */
1347:            public Page mouseOut() {
1348:                return mouseOut(false, false, false, MouseEvent.BUTTON_LEFT);
1349:            }
1350:
1351:            /**
1352:             * Simulate moving the mouse out of this element.
1353:             *
1354:             * @param shiftKey true if SHIFT is pressed
1355:             * @param ctrlKey true if CTRL is pressed
1356:             * @param altKey true if ALT is pressed
1357:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1358:             *        or {@link MouseEvent#BUTTON_RIGHT}
1359:             *
1360:             * @return The page that occupies this window after the mouse moves out of this element.
1361:             * It may be the same window or it may be a freshly loaded one.
1362:             */
1363:            public Page mouseOut(final boolean shiftKey, final boolean ctrlKey,
1364:                    final boolean altKey, final int button) {
1365:                return doMouseEvent(MouseEvent.TYPE_MOUSE_OUT, shiftKey,
1366:                        ctrlKey, altKey, button);
1367:            }
1368:
1369:            /**
1370:             * Simulate clicking the mouse in this element.
1371:             *
1372:             * @return The page that occupies this window after the mouse is clicked in this element.
1373:             * It may be the same window or it may be a freshly loaded one.
1374:             */
1375:            public Page mouseDown() {
1376:                return mouseDown(false, false, false, MouseEvent.BUTTON_LEFT);
1377:            }
1378:
1379:            /**
1380:             * Simulate clicking the mouse in this element.
1381:             *
1382:             * @param shiftKey true if SHIFT is pressed
1383:             * @param ctrlKey true if CTRL is pressed
1384:             * @param altKey true if ALT is pressed
1385:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1386:             *        or {@link MouseEvent#BUTTON_RIGHT}
1387:             *
1388:             * @return The page that occupies this window after the mouse is clicked in this element.
1389:             * It may be the same window or it may be a freshly loaded one.
1390:             */
1391:            public Page mouseDown(final boolean shiftKey,
1392:                    final boolean ctrlKey, final boolean altKey,
1393:                    final int button) {
1394:                return doMouseEvent(MouseEvent.TYPE_MOUSE_DOWN, shiftKey,
1395:                        ctrlKey, altKey, button);
1396:            }
1397:
1398:            /**
1399:             * Simulate releasing the mouse click in this element.
1400:             *
1401:             * @return The page that occupies this window after the mouse click is released in this element.
1402:             * It may be the same window or it may be a freshly loaded one.
1403:             */
1404:            public Page mouseUp() {
1405:                return mouseUp(false, false, false, MouseEvent.BUTTON_LEFT);
1406:            }
1407:
1408:            /**
1409:             * Simulate releasing the mouse click in this element.
1410:             *
1411:             * @param shiftKey true if SHIFT is pressed
1412:             * @param ctrlKey true if CTRL is pressed
1413:             * @param altKey true if ALT is pressed
1414:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1415:             *        or {@link MouseEvent#BUTTON_RIGHT}
1416:             *
1417:             * @return The page that occupies this window after the mouse click is released in this element.
1418:             * It may be the same window or it may be a freshly loaded one.
1419:             */
1420:            public Page mouseUp(final boolean shiftKey, final boolean ctrlKey,
1421:                    final boolean altKey, final int button) {
1422:                return doMouseEvent(MouseEvent.TYPE_MOUSE_UP, shiftKey,
1423:                        ctrlKey, altKey, button);
1424:            }
1425:
1426:            /**
1427:             * Simulate the given mouse event.
1428:             *
1429:             * @param shiftKey true if SHIFT is pressed
1430:             * @param ctrlKey true if CTRL is pressed
1431:             * @param altKey true if ALT is pressed
1432:             * @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
1433:             *        or {@link MouseEvent#BUTTON_RIGHT}
1434:             *
1435:             * @return The page that occupies this window after the mouse event occur on this element.
1436:             * It may be the same window or it may be a freshly loaded one.
1437:             */
1438:            private Page doMouseEvent(final String eventType,
1439:                    final boolean shiftKey, final boolean ctrlKey,
1440:                    final boolean altKey, final int button) {
1441:                if (this  instanceof  DisabledElement
1442:                        && ((DisabledElement) this ).isDisabled()) {
1443:                    return getPage();
1444:                }
1445:
1446:                final HtmlPage page = getPage();
1447:                final Event event = new MouseEvent(this , eventType, shiftKey,
1448:                        ctrlKey, altKey, button);
1449:                final ScriptResult scriptResult = fireEvent(event);
1450:                final Page currentPage;
1451:                if (scriptResult == null) {
1452:                    currentPage = page;
1453:                } else {
1454:                    currentPage = scriptResult.getNewPage();
1455:                }
1456:                return currentPage;
1457:            }
1458:
1459:            /**
1460:             * Simulate right clicking the mouse in this element.
1461:             *
1462:             * @return The page that occupies this window after the mouse is right clicked in this element.
1463:             * It may be the same window or it may be a freshly loaded one.
1464:             */
1465:            public Page rightClick() {
1466:                return rightClick(false, false, false);
1467:            }
1468:
1469:            /**
1470:             * Simulate right clicking the mouse in this element.
1471:             *
1472:             * <p>This is equivalent to calling {@link #mouseDown(boolean, boolean, boolean, int)},
1473:             * then {@link #mouseUp(boolean, boolean, boolean, int)}
1474:             *
1475:             * @param shiftKey true if SHIFT is pressed
1476:             * @param ctrlKey true if CTRL is pressed
1477:             * @param altKey true if ALT is pressed
1478:             *
1479:             * @return The page that occupies this window after the mouse is right clicked in this element.
1480:             * It may be the same window or it may be a freshly loaded one.
1481:             */
1482:            public Page rightClick(final boolean shiftKey,
1483:                    final boolean ctrlKey, final boolean altKey) {
1484:                final Page mouseDownPage = mouseDown(shiftKey, ctrlKey, altKey,
1485:                        MouseEvent.BUTTON_RIGHT);
1486:                if (mouseDownPage != getPage()) {
1487:                    getLog()
1488:                            .debug(
1489:                                    "rightClick() is incomplete, as mouseDown() loaded a different page.");
1490:                    return mouseDownPage;
1491:                }
1492:                final Page mouseUpPage = mouseUp(shiftKey, ctrlKey, altKey,
1493:                        MouseEvent.BUTTON_RIGHT);
1494:                if (mouseUpPage != getPage()) {
1495:                    getLog()
1496:                            .debug(
1497:                                    "rightClick() is incomplete, as mouseUp() loaded a different page.");
1498:                    return mouseUpPage;
1499:                }
1500:                return doMouseEvent(MouseEvent.TYPE_CONTEXT_MENU, shiftKey,
1501:                        ctrlKey, altKey, MouseEvent.BUTTON_RIGHT);
1502:            }
1503:
1504:            /**
1505:             * Remove focus from this element.
1506:             */
1507:            public void blur() {
1508:                getPage().moveFocusToElement(null);
1509:            }
1510:
1511:            /**
1512:             * Set the focus to this element.
1513:             */
1514:            public void focus() {
1515:                getPage().moveFocusToElement(this);
1516:            }
1517:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.