1 /********************************************** 2 * Copyright (C) 2010 Lukas Laag 3 * This file is part of lib-gwt-svg. 4 * 5 * libgwtsvg is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * libgwtsvg is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with libgwtsvg. If not, see http://www.gnu.org/licenses/ 17 **********************************************/ 18 /* 19 * Copyright (c) 2004 World Wide Web Consortium, 20 * 21 * (Massachusetts Institute of Technology, European Research Consortium for 22 * Informatics and Mathematics, Keio University). All Rights Reserved. This 23 * work is distributed under the W3C(r) Software License [1] in the hope that 24 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 25 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 26 * 27 * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 28 */ 29 package org.vectomatic.dom.svg.utils; 30 31 import java.util.Iterator; 32 import java.util.NoSuchElementException; 33 34 import org.vectomatic.dom.svg.OMElement; 35 import org.vectomatic.dom.svg.OMNode; 36 import org.vectomatic.dom.svg.OMSVGElement; 37 import org.vectomatic.dom.svg.impl.Attr; 38 import org.vectomatic.dom.svg.impl.DOMHelperImpl; 39 import org.vectomatic.dom.svg.impl.NamedNodeMap; 40 import org.w3c.dom.DOMException; 41 42 import com.google.gwt.core.client.GWT; 43 import com.google.gwt.core.client.JavaScriptException; 44 import com.google.gwt.core.client.JavaScriptObject; 45 import com.google.gwt.dom.client.Document; 46 import com.google.gwt.dom.client.Element; 47 import com.google.gwt.dom.client.Node; 48 import com.google.gwt.dom.client.NodeList; 49 import com.google.gwt.dom.client.Text; 50 import com.google.gwt.event.dom.client.LoseCaptureHandler; 51 import com.google.gwt.event.shared.HandlerRegistration; 52 53 /** 54 * Class to group miscellaneous DOM level 2 methods. These 55 * methods are missing from the GWT DOM overlay types (such as 56 * {@link com.google.gwt.dom.client.Element} or {@link com.google.gwt.dom.client.Node}) 57 * but exist in almost all browsers and are sometimes required 58 * to develop an SVG application. 59 * @author laaglu 60 */ 61 public class DOMHelper { 62 private static final DOMHelperImpl impl = GWT.create(DOMHelperImpl.class); 63 64 /** 65 * Creates an element of the given qualified name and namespace URI. 66 * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 67 * , applications must use the value <code>null</code> as the 68 * namespaceURI parameter for methods if they wish to have no namespace. 69 * @param document The document in which the element is to be created. 70 * @param namespaceURI The namespace URI of the element to create. 71 * @param qualifiedName The qualified name of the element type to 72 * instantiate. 73 * @return A new <code>Element</code> object with the following 74 * attributes: 75 * <table border='1' cellpadding='3'> 76 * <tr> 77 * <th>Attribute</th> 78 * <th>Value</th> 79 * </tr> 80 * <tr> 81 * <td valign='top' rowspan='1' colspan='1'><code>Node.nodeName</code></td> 82 * <td valign='top' rowspan='1' colspan='1'> 83 * <code>qualifiedName</code></td> 84 * </tr> 85 * <tr> 86 * <td valign='top' rowspan='1' colspan='1'><code>Node.namespaceURI</code></td> 87 * <td valign='top' rowspan='1' colspan='1'> 88 * <code>namespaceURI</code></td> 89 * </tr> 90 * <tr> 91 * <td valign='top' rowspan='1' colspan='1'><code>Node.prefix</code></td> 92 * <td valign='top' rowspan='1' colspan='1'>prefix, extracted 93 * from <code>qualifiedName</code>, or <code>null</code> if there is 94 * no prefix</td> 95 * </tr> 96 * <tr> 97 * <td valign='top' rowspan='1' colspan='1'><code>Node.localName</code></td> 98 * <td valign='top' rowspan='1' colspan='1'>local name, extracted from 99 * <code>qualifiedName</code></td> 100 * </tr> 101 * <tr> 102 * <td valign='top' rowspan='1' colspan='1'><code>Element.tagName</code></td> 103 * <td valign='top' rowspan='1' colspan='1'> 104 * <code>qualifiedName</code></td> 105 * </tr> 106 * </table> 107 * @exception DOMException 108 * INVALID_CHARACTER_ERR: Raised if the specified 109 * <code>qualifiedName</code> is not an XML name according to the XML 110 * version in use specified in the <code>Document.xmlVersion</code> 111 * attribute. 112 * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is a 113 * malformed qualified name, if the <code>qualifiedName</code> has a 114 * prefix and the <code>namespaceURI</code> is <code>null</code>, or 115 * if the <code>qualifiedName</code> has a prefix that is "xml" and 116 * the <code>namespaceURI</code> is different from "<a href='http://www.w3.org/XML/1998/namespace'> 117 * http://www.w3.org/XML/1998/namespace</a>" [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 118 * , or if the <code>qualifiedName</code> or its prefix is "xmlns" and 119 * the <code>namespaceURI</code> is different from "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>", or if the <code>namespaceURI</code> is "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>" and neither the <code>qualifiedName</code> nor its prefix is "xmlns". 120 * <br>NOT_SUPPORTED_ERR: Always thrown if the current document does not 121 * support the <code>"XML"</code> feature, since namespaces were 122 * defined by XML. 123 */ 124 public static final native Element createElementNS(Document document, String namespaceURI, String qualifiedName) throws JavaScriptException /*-{ 125 return document.createElementNS(namespaceURI, qualifiedName); 126 }-*/; 127 128 /** 129 * Creates a new empty SVG document 130 * @return 131 * a new empty SVG document 132 */ 133 public static final native Document createDocument(String uri, String qname) /*-{ 134 return $doc.implementation.createDocument(uri, qname, null); 135 }-*/; 136 137 /** 138 * Imports a node from another document to this document, without altering 139 * or removing the source node from the original document; this method 140 * creates a new copy of the source node. The returned node has no 141 * parent; (<code>parentNode</code> is <code>null</code>). 142 * <br>For all nodes, importing a node creates a node object owned by the 143 * importing document, with attribute values identical to the source 144 * node's <code>nodeName</code> and <code>nodeType</code>, plus the 145 * attributes related to namespaces (<code>prefix</code>, 146 * <code>localName</code>, and <code>namespaceURI</code>). As in the 147 * <code>cloneNode</code> operation, the source node is not altered. 148 * User data associated to the imported node is not carried over. 149 * However, if any <code>UserDataHandlers</code> has been specified 150 * along with the associated data these handlers will be called with the 151 * appropriate parameters before this method returns. 152 * <br>Additional information is copied as appropriate to the 153 * <code>nodeType</code>, attempting to mirror the behavior expected if 154 * a fragment of XML or HTML source was copied from one document to 155 * another, recognizing that the two documents may have different DTDs 156 * in the XML case. The following list describes the specifics for each 157 * type of node. 158 * <dl> 159 * <dt>ATTRIBUTE_NODE</dt> 160 * <dd>The <code>ownerElement</code> attribute 161 * is set to <code>null</code> and the <code>specified</code> flag is 162 * set to <code>true</code> on the generated <code>Attr</code>. The 163 * descendants of the source <code>Attr</code> are recursively imported 164 * and the resulting nodes reassembled to form the corresponding subtree. 165 * Note that the <code>deep</code> parameter has no effect on 166 * <code>Attr</code> nodes; they always carry their children with them 167 * when imported.</dd> 168 * <dt>DOCUMENT_FRAGMENT_NODE</dt> 169 * <dd>If the <code>deep</code> option 170 * was set to <code>true</code>, the descendants of the source 171 * <code>DocumentFragment</code> are recursively imported and the 172 * resulting nodes reassembled under the imported 173 * <code>DocumentFragment</code> to form the corresponding subtree. 174 * Otherwise, this simply generates an empty 175 * <code>DocumentFragment</code>.</dd> 176 * <dt>DOCUMENT_NODE</dt> 177 * <dd><code>Document</code> 178 * nodes cannot be imported.</dd> 179 * <dt>DOCUMENT_TYPE_NODE</dt> 180 * <dd><code>DocumentType</code> 181 * nodes cannot be imported.</dd> 182 * <dt>ELEMENT_NODE</dt> 183 * <dd><em>Specified</em> attribute nodes of the source element are imported, and the generated 184 * <code>Attr</code> nodes are attached to the generated 185 * <code>Element</code>. Default attributes are <em>not</em> copied, though if the document being imported into defines default 186 * attributes for this element name, those are assigned. If the 187 * <code>importNode</code> <code>deep</code> parameter was set to 188 * <code>true</code>, the descendants of the source element are 189 * recursively imported and the resulting nodes reassembled to form the 190 * corresponding subtree.</dd> 191 * <dt>ENTITY_NODE</dt> 192 * <dd><code>Entity</code> nodes can be 193 * imported, however in the current release of the DOM the 194 * <code>DocumentType</code> is readonly. Ability to add these imported 195 * nodes to a <code>DocumentType</code> will be considered for addition 196 * to a future release of the DOM.On import, the <code>publicId</code>, 197 * <code>systemId</code>, and <code>notationName</code> attributes are 198 * copied. If a <code>deep</code> import is requested, the descendants 199 * of the the source <code>Entity</code> are recursively imported and 200 * the resulting nodes reassembled to form the corresponding subtree.</dd> 201 * <dt> 202 * ENTITY_REFERENCE_NODE</dt> 203 * <dd>Only the <code>EntityReference</code> itself is 204 * copied, even if a <code>deep</code> import is requested, since the 205 * source and destination documents might have defined the entity 206 * differently. If the document being imported into provides a 207 * definition for this entity name, its value is assigned.</dd> 208 * <dt>NOTATION_NODE</dt> 209 * <dd> 210 * <code>Notation</code> nodes can be imported, however in the current 211 * release of the DOM the <code>DocumentType</code> is readonly. Ability 212 * to add these imported nodes to a <code>DocumentType</code> will be 213 * considered for addition to a future release of the DOM.On import, the 214 * <code>publicId</code> and <code>systemId</code> attributes are copied. 215 * Note that the <code>deep</code> parameter has no effect on this type 216 * of nodes since they cannot have any children.</dd> 217 * <dt> 218 * PROCESSING_INSTRUCTION_NODE</dt> 219 * <dd>The imported node copies its 220 * <code>target</code> and <code>data</code> values from those of the 221 * source node.Note that the <code>deep</code> parameter has no effect 222 * on this type of nodes since they cannot have any children.</dd> 223 * <dt>TEXT_NODE, 224 * CDATA_SECTION_NODE, COMMENT_NODE</dt> 225 * <dd>These three types of nodes inheriting 226 * from <code>CharacterData</code> copy their <code>data</code> and 227 * <code>length</code> attributes from those of the source node.Note 228 * that the <code>deep</code> parameter has no effect on these types of 229 * nodes since they cannot have any children.</dd> 230 * </dl> 231 * @param document The document in which to import the node. 232 * @param importedNode The node to import. 233 * @param deep If <code>true</code>, recursively import the subtree under 234 * the specified node; if <code>false</code>, import only the node 235 * itself, as explained above. This has no effect on nodes that cannot 236 * have any children, and on <code>Attr</code>, and 237 * <code>EntityReference</code> nodes. 238 * @return The imported node that belongs to this <code>Document</code>. 239 * @exception DOMException 240 * NOT_SUPPORTED_ERR: Raised if the type of node being imported is not 241 * supported. 242 * <br>INVALID_CHARACTER_ERR: Raised if one of the imported names is not 243 * an XML name according to the XML version in use specified in the 244 * <code>Document.xmlVersion</code> attribute. This may happen when 245 * importing an XML 1.1 [<a href='http://www.w3.org/TR/2004/REC-xml11-20040204/'>XML 1.1</a>] element 246 * into an XML 1.0 document, for instance. 247 */ 248 public static final native Node importNode(Document document, Node importedNode, boolean deep) /*-{ 249 return document.importNode(importedNode, deep); 250 }-*/; 251 252 /** 253 * Puts all <code>Text</code> nodes in the full depth of the sub-tree 254 * underneath this <code>Node</code>, including attribute nodes, into a 255 * "normal" form where only structure (e.g., elements, comments, 256 * processing instructions, CDATA sections, and entity references) 257 * separates <code>Text</code> nodes, i.e., there are neither adjacent 258 * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can 259 * be used to ensure that the DOM view of a document is the same as if 260 * it were saved and re-loaded, and is useful when operations (such as 261 * XPointer [<a href='http://www.w3.org/TR/2003/REC-xptr-framework-20030325/'>XPointer</a>] 262 * lookups) that depend on a particular document tree structure are to 263 * be used. If the parameter "normalize-characters" of the 264 * <code>DOMConfiguration</code> object attached to the 265 * <code>Node.ownerDocument</code> is <code>true</code>, this method 266 * will also fully normalize the characters of the <code>Text</code> 267 * nodes. 268 * <p ><b>Note:</b> In cases where the document contains 269 * <code>CDATASections</code>, the normalize operation alone may not be 270 * sufficient, since XPointers do not differentiate between 271 * <code>Text</code> nodes and <code>CDATASection</code> nodes. 272 * @param node the node to normalize 273 */ 274 public static final native void normalize(Node node) /*-{ 275 return node.normalize(); 276 }-*/; 277 278 /** 279 * Extracts a range of data from the node. 280 * @param text the text node 281 * @param offset Start offset of substring to extract. 282 * @param count The number of 16-bit units to extract. 283 * @return The specified substring. If the sum of <code>offset</code> and 284 * <code>count</code> exceeds the <code>length</code>, then all 16-bit 285 * units to the end of the data are returned. 286 * @exception DOMException 287 * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is 288 * negative or greater than the number of 16-bit units in 289 * <code>data</code>, or if the specified <code>count</code> is 290 * negative. 291 * <br>DOMSTRING_SIZE_ERR: Raised if the specified range of text does 292 * not fit into a <code>String</code>. 293 */ 294 public static final native String substringData(Text text, int offset, int count) throws JavaScriptException /*-{ 295 return text.substringData(offset, count); 296 }-*/; 297 298 /** 299 * Append the string to the end of the character data of the node. Upon 300 * success, <code>data</code> provides access to the concatenation of 301 * <code>data</code> and the <code>String</code> specified. 302 * @param text the text node 303 * @param arg The <code>String</code> to append. 304 * @exception DOMException 305 * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. 306 */ 307 public static final native void appendData(Text text, String arg) throws JavaScriptException /*-{ 308 text.appendData(offset, arg); 309 }-*/; 310 311 /** 312 * Retrieves an attribute value by local name and namespace URI on 313 * the specified element. 314 * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 315 * , applications must use the value <code>null</code> as the 316 * <code>namespaceURI</code> parameter for methods if they wish to have 317 * no namespace. 318 * @param elem the element 319 * @param namespaceURI The namespace URI of the attribute to retrieve. 320 * @param localName The local name of the attribute to retrieve. 321 * @return The <code>Attr</code> value as a string, or the empty string 322 * if that attribute does not have a specified or default value. 323 * @exception DOMException 324 * NOT_SUPPORTED_ERR: May be raised if the implementation does not 325 * support the feature <code>"XML"</code> and the language exposed 326 * through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 327 * @since DOM Level 2 328 */ 329 public static final native String getAttributeNS(Element elem, String namespaceURI, String localName) throws JavaScriptException /*-{ 330 return elem.getAttributeNS(namespaceURI, localName); 331 }-*/; 332 333 /** 334 * Returns a <code>NodeList</code> of all the descendant 335 * <code>Elements</code> of the specified element 336 * with a given local name and namespace URI in 337 * document order. 338 * @param elem The element 339 * @param namespaceURI The namespace URI of the elements to match on. The 340 * special value "*" matches all namespaces. 341 * @param localName The local name of the elements to match on. The 342 * special value "*" matches all local names. 343 * @return A new <code>NodeList</code> object containing all the matched 344 * <code>Elements</code>. 345 * @exception DOMException 346 * NOT_SUPPORTED_ERR: May be raised if the implementation does not 347 * support the feature <code>"XML"</code> and the language exposed 348 * through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 349 * @since DOM Level 2 350 */ 351 public static final native NodeList<? extends Node> getElementsByTagNameNS(Element elem, String namespaceURI, String localName) throws JavaScriptException /*-{ 352 return elem.getElementsByTagNameNS(namespaceURI, localName); 353 }-*/; 354 355 /** 356 * Returns a <code>NodeList</code> of all the <code>Elements</code> 357 * in the specified document with a 358 * given local name and namespace URI in document order. 359 * @param doc The document 360 * @param namespaceURI The namespace URI of the elements to match on. The 361 * special value <code>"*"</code> matches all namespaces. 362 * @param localName The local name of the elements to match on. The 363 * special value "*" matches all local names. 364 * @return A new <code>NodeList</code> object containing all the matched 365 * <code>Elements</code>. 366 * @since DOM Level 2 367 */ 368 public static final native NodeList<? extends Node> getElementsByTagNameNS(Document doc, String namespaceURI, String localName) throws JavaScriptException /*-{ 369 return doc.getElementsByTagNameNS(namespaceURI, localName); 370 }-*/; 371 372 /** 373 * Returns the current document 374 * @return the current document 375 */ 376 public static final native Document getCurrentDocument() /*-{ 377 return $doc; 378 }-*/; 379 380 /** 381 * Returns a <code>NamedNodeMap</code> containing the attributes of the 382 * specified element. 383 * @param elem The element 384 * @return a <code>NamedNodeMap</code> containing the attributes of the 385 * specified element 386 */ 387 public static final native NamedNodeMap<Attr> getAttributes(Element elem) /*-{ 388 return elem.attributes; 389 }-*/; 390 391 /** 392 * Retrieves an attribute node by name on the specified element. 393 * <br>To retrieve an attribute node by qualified name and namespace URI, 394 * use the <code>getAttributeNodeNS</code> method. 395 * @param elt The element 396 * @param attrName The name (<code>nodeName</code>) of the attribute to 397 * retrieve. 398 * @return The <code>Attr</code> node with the specified name ( 399 * <code>nodeName</code>) or <code>null</code> if there is no such 400 * attribute. 401 */ 402 public static final native Attr getAttributeNode(Element elt, String attrName) /*-{ 403 return elt.getAttributeNode(attrName); 404 }-*/; 405 406 /** 407 * Adds a new attribute node to the specified element. If an attribute with that name ( 408 * <code>nodeName</code>) is already present in the element, it is 409 * replaced by the new one. Replacing an attribute node by itself has no 410 * effect. 411 * <br>To add a new attribute node with a qualified name and namespace 412 * URI, use the <code>setAttributeNodeNS</code> method. 413 * @param elt The element 414 * @param attr The <code>Attr</code> node to add to the attribute list. 415 * @return If the <code>attr</code> attribute replaces an existing 416 * attribute, the replaced <code>Attr</code> node is returned, 417 * otherwise <code>null</code> is returned. 418 * @exception DOMException 419 * WRONG_DOCUMENT_ERR: Raised if <code>attr</code> was created from a 420 * different document than the one that created the element. 421 * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. 422 * <br>INUSE_ATTRIBUTE_ERR: Raised if <code>attr</code> is already an 423 * attribute of another <code>Element</code> object. The DOM user must 424 * explicitly clone <code>Attr</code> nodes to re-use them in other 425 * elements. 426 */ 427 public static final native Attr setAttributeNode(Element elt, Attr attr) throws JavaScriptException /*-{ 428 return elt.setAttributeNode(attr); 429 }-*/; 430 431 /** 432 * Returns <code>true</code> when an attribute with a given local name and 433 * namespace URI is specified on the specified element or has a default value, 434 * <code>false</code> otherwise. 435 * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 436 * , applications must use the value <code>null</code> as the 437 * <code>namespaceURI</code> parameter for methods if they wish to have 438 * no namespace. 439 * @param elem The element 440 * @param namespaceURI The namespace URI of the attribute to look for. 441 * @param localName The local name of the attribute to look for. 442 * @return <code>true</code> if an attribute with the given local name 443 * and namespace URI is specified or has a default value on this 444 * element, <code>false</code> otherwise. 445 * @exception DOMException 446 * NOT_SUPPORTED_ERR: May be raised if the implementation does not 447 * support the feature <code>"XML"</code> and the language exposed 448 * through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 449 */ 450 public static final native boolean hasAttributeNS(Element elem, String namespaceURI, String localName) throws JavaScriptException /*-{ 451 return elem.hasAttributeNS(namespaceURI, localName); 452 }-*/; 453 454 /** 455 * Adds a new attribute to the specified element. If an attribute with the same local name and 456 * namespace URI is already present on the element, its prefix is 457 * changed to be the prefix part of the <code>qualifiedName</code>, and 458 * its value is changed to be the <code>value</code> parameter. This 459 * value is a simple string; it is not parsed as it is being set. So any 460 * markup (such as syntax to be recognized as an entity reference) is 461 * treated as literal text, and needs to be appropriately escaped by the 462 * implementation when it is written out. In order to assign an 463 * attribute value that contains entity references, the user must create 464 * an <code>Attr</code> node plus any <code>Text</code> and 465 * <code>EntityReference</code> nodes, build the appropriate subtree, 466 * and use <code>setAttributeNodeNS</code> or 467 * <code>setAttributeNode</code> to assign it as the value of an 468 * attribute. 469 * <br>Per [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 470 * , applications must use the value <code>null</code> as the 471 * <code>namespaceURI</code> parameter for methods if they wish to have 472 * no namespace. 473 * @param elem The element 474 * @param namespaceURI The namespace URI of the attribute to create or 475 * alter. 476 * @param localName The local name of the attribute to create or 477 * alter. 478 * @param value The value to set in string form. 479 * @exception DOMException 480 * INVALID_CHARACTER_ERR: Raised if the specified qualified name is not 481 * an XML name according to the XML version in use specified in the 482 * <code>Document.xmlVersion</code> attribute. 483 * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. 484 * <br>NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is 485 * malformed per the Namespaces in XML specification, if the 486 * <code>qualifiedName</code> has a prefix and the 487 * <code>namespaceURI</code> is <code>null</code>, if the 488 * <code>qualifiedName</code> has a prefix that is "xml" and the 489 * <code>namespaceURI</code> is different from "<a href='http://www.w3.org/XML/1998/namespace'> 490 * http://www.w3.org/XML/1998/namespace</a>", if the <code>qualifiedName</code> or its prefix is "xmlns" and the 491 * <code>namespaceURI</code> is different from "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>", or if the <code>namespaceURI</code> is "<a href='http://www.w3.org/2000/xmlns/'>http://www.w3.org/2000/xmlns/</a>" and neither the <code>qualifiedName</code> nor its prefix is "xmlns". 492 * <br>NOT_SUPPORTED_ERR: May be raised if the implementation does not 493 * support the feature <code>"XML"</code> and the language exposed 494 * through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]). 495 */ 496 public static final native void setAttributeNS(Element elem, String namespaceURI, String localName, String value) throws JavaScriptException /*-{ 497 elem.setAttributeNS(namespaceURI, localName, value); 498 }-*/; 499 500 /** 501 * The namespace URI of the specified node, or <code>null</code> if it is 502 * unspecified (see ). 503 * <br>This is not a computed value that is the result of a namespace 504 * lookup based on an examination of the namespace declarations in 505 * scope. It is merely the namespace URI given at creation time. 506 * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and 507 * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 508 * method, such as <code>Document.createElement()</code>, this is always 509 * <code>null</code>. 510 * <p ><b>Note:</b> Per the <em>Namespaces in XML</em> Specification [<a href='http://www.w3.org/TR/1999/REC-xml-names-19990114/'>XML Namespaces</a>] 511 * an attribute does not inherit its namespace from the element it is 512 * attached to. If an attribute is not explicitly given a namespace, it 513 * simply has no namespace. 514 * @param node a DOM node 515 * @return The namespace URI of this node 516 */ 517 public static native String getNamespaceURI(Node node) /*-{ 518 return node.namespaceURI; 519 }-*/; 520 521 /** 522 * Returns the local part of the qualified name of this node. 523 * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and 524 * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1 525 * method, such as <code>Document.createElement()</code>, this is always 526 * <code>null</code>. 527 * @param node a DOM node 528 * @return The local part of the qualified name of this node 529 */ 530 public static native String getLocalName(Node node) /*-{ 531 return node.localName; 532 }-*/; 533 534 /** 535 * Makes a node sink the events emitted by the specified element 536 * @param elem The element emitting the events 537 * @param eventName The event name 538 */ 539 public static void bindEventListener(Element elem, String eventName) { 540 impl.bindEventListener(elem, eventName); 541 } 542 543 /** 544 * Stops making a node sink the events emitted by the specified element 545 * @param elem The element emitting the events 546 * @param eventName The event name 547 */ 548 public static void unbindEventListener(Element elem, String eventName) { 549 impl.unbindEventListener(elem, eventName); 550 } 551 552 /** 553 * Returns the element which currently captures all the 554 * events after a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)} 555 * or null if element is set to capture events 556 * @return The event capturing element 557 */ 558 public static OMSVGElement getCaptureElement() { 559 return impl.getCaptureElement(); 560 } 561 562 /** 563 * Makes the specified element capture all the events, until 564 * a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#releaseCaptureElement()} 565 * terminates the capture 566 * @param element The capturing element 567 * @param loseCaptureHandler A handler which will be invoked 568 * if the element loses capture 569 * @return {@link HandlerRegistration} used to remove this handler 570 */ 571 public static HandlerRegistration setCaptureElement(OMSVGElement element, LoseCaptureHandler loseCaptureHandler) { 572 return impl.setCaptureElement(element, loseCaptureHandler); 573 } 574 575 /** 576 * Stops the forwarding of all events to the capturing element 577 * specified by {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)} 578 */ 579 public static void releaseCaptureElement() { 580 impl.releaseCaptureElement(); 581 } 582 583 /** 584 * Returns the JavaScript type of an object. 585 * The function getType is borrowed from: 586 * JavaScript: The Definitive Guide, 5th Edition 587 * By David Flanagan 588 */ 589 public static final native String getType(JavaScriptObject x) /*-{ 590 // If x is null, return "null" 591 if (x == null) { 592 return "null"; 593 } 594 // Next try the typeof operator 595 var t = typeof x; 596 // If the result is not vague, return it 597 if (t != "object") { 598 return t; 599 } 600 // Hack from Chrome48+ 601 if ('__IsSVGPathSegList__' in x) { 602 return 'SVGPathSegList'; 603 } 604 // Otherwise, x is an object. Use the default toString( ) method to 605 // get the class value of the object. 606 var c = Object.prototype.toString.apply(x); // Returns "[object class]" 607 c = c.substring(8, c.length-1); // Strip off "[object" and "]" 608 609 // If the class is not a vague one, return it. 610 if (c != "Object") { 611 return c; 612 } 613 // If we get here, c is "Object". Check to see if 614 // the value x is really just a generic object. 615 if (x.constructor == Object) { 616 return c; // Okay the type really is "Object" 617 } 618 // For user-defined classes, look for a string-valued property named 619 // classname, that is inherited from the object's prototype 620 if ("classname" in x.constructor.prototype && // inherits classname 621 typeof x.constructor.prototype.classname == "string") // its a string 622 return x.constructor.prototype.classname; 623 624 // If we really can't figure it out, say so. 625 return "<unknown type>"; 626 }-*/; 627 628 /** 629 * Creates a value of the formed expected by SVG 630 * href attribtues. 631 * @param s the identifier of the data pointed by the href. 632 * @return a value of the formed expected by SVG 633 * href attribtues. 634 */ 635 public static final String toUrl(String s) { 636 return "url(#" + s + ")"; 637 } 638 639 /** 640 * Tests if the underlying DOM implementation supports the specified feature 641 * @param featureName 642 * The name of the feature to test 643 * @return 644 * true if the feature is supported, false otherwise 645 */ 646 public static boolean hasFeature(String featureName) { 647 if (SVGConstants.SVG_FEATURE_TOUCH_EVENTS.equals(featureName)) { 648 return supportsSvgTouchEvents(); 649 } if (SVGConstants.SVG_FEATURE_DND_EVENTS.equals(featureName)) { 650 return supportsSvgDndEvents(); 651 } else { 652 return hasFeature_(featureName); 653 } 654 } 655 private static native boolean hasFeature_(String featureName) /*-{ 656 return $doc.implementation.hasFeature(featureName, 1.1); 657 }-*/; 658 659 /** 660 * Evaluates the specified XPath expression and returns 661 * a iterator for the selected {@link org.vectomatic.dom.svg.OMNode} node list. 662 * The expression must evaluate to a node list. 663 * @param root 664 * The element the expression is rooted at 665 * @param expr 666 * The XPath expression 667 * @param resolver 668 * A prefix resolver if the expression has prefix 669 * @return 670 * A node iterator for the selected nodes. 671 */ 672 public static <T extends OMNode> Iterator<T> evaluateXPath(OMElement root, String expr, XPathPrefixResolver resolver) { 673 final JavaScriptObject result = impl.evaluateNodeListXPath_(root.getElement(), expr, resolver); 674 return new Iterator<T>() { 675 boolean fetched; 676 Node next; 677 678 @Override 679 public boolean hasNext() { 680 if (!fetched) { 681 next = iterateNext(result); 682 fetched = true; 683 } 684 return next != null; 685 } 686 687 @Override 688 public T next() { 689 if (!hasNext()) { 690 throw new NoSuchElementException(); 691 } 692 fetched = false; 693 return OMNode.<T>convert(next); 694 } 695 696 @Override 697 public void remove() { 698 throw new UnsupportedOperationException(); 699 } 700 701 private native Node iterateNext(JavaScriptObject result) /*-{ 702 return result.iterateNext(); 703 }-*/; 704 }; 705 706 } 707 708 /** 709 * Evaluates the specified XPath expression and returns 710 * a iterator for the selected {@link com.google.gwt.dom.client.Node} node list. 711 * The expression must evaluate to a node list. 712 * @param root 713 * The element the expression is rooted at 714 * @param expr 715 * The XPath expression 716 * @param resolver 717 * A prefix resolver if the expression has prefix 718 * @return 719 * A node iterator for the selected nodes. 720 */ 721 public static <T extends Node> Iterator<T> evaluateNodeListXPath(Element root, String expr, XPathPrefixResolver resolver) { 722 final JavaScriptObject result = impl.evaluateNodeListXPath_(root, expr, resolver); 723 return new Iterator<T>() { 724 boolean fetched; 725 T next; 726 727 @Override 728 public boolean hasNext() { 729 if (!fetched) { 730 next = iterateNext(result); 731 fetched = true; 732 } 733 return next != null; 734 } 735 736 @Override 737 public T next() { 738 if (!hasNext()) { 739 throw new NoSuchElementException(); 740 } 741 fetched = false; 742 return next; 743 } 744 745 @Override 746 public void remove() { 747 throw new UnsupportedOperationException(); 748 } 749 750 private native T iterateNext(JavaScriptObject result) /*-{ 751 return result.iterateNext(); 752 }-*/; 753 }; 754 } 755 756 /** 757 * Evaluates the specified XPath expression and returns 758 * the matching {@link com.google.gwt.dom.client.Node} node. 759 * The expression must evaluate to a single node. 760 * @param root 761 * The element the expression is rooted at 762 * @param expr 763 * The XPath expression 764 * @param resolver 765 * A prefix resolver if the expression has prefix 766 * @return 767 * The selected node, or null if no such node exists. 768 */ 769 public static <T extends Node> T evaluateNodeXPath(Element root, String expr, XPathPrefixResolver resolver) { 770 return (T)(impl.evaluateNodeXPath_(root, expr, resolver)); 771 } 772 773 /** 774 * Evaluates the specified XPath expression and returns 775 * the matching {@link java.lang.String}. 776 * The expression must evaluate to a string. 777 * @param root 778 * The element the expression is rooted at 779 * @param expr 780 * The XPath expression 781 * @param resolver 782 * A prefix resolver if the expression has prefix 783 * @return 784 * The matching string, or null if no such string exists. 785 */ 786 public static String evaluateStringXPath(Element root, String expr, XPathPrefixResolver resolver) { 787 return impl.evaluateStringXPath_(root, expr, resolver); 788 } 789 790 /** 791 * Evaluates the specified XPath expression and returns 792 * the matching float. 793 * The expression must evaluate to a number. 794 * @param root 795 * The element the expression is rooted at 796 * @param expr 797 * The XPath expression 798 * @param resolver 799 * A prefix resolver if the expression has prefix 800 * @return 801 * The matching float, or NaN if no such number exists. 802 */ 803 public static float evaluateNumberXPath(Element root, String expr, XPathPrefixResolver resolver) { 804 return impl.evaluateNumberXPath_(root, expr, resolver); 805 } 806 807 /** 808 * Evaluates the specified XPath expression and returns 809 * the matching boolean. 810 * The expression must evaluate to a boolean. 811 * @param root 812 * The element the expression is rooted at 813 * @param expr 814 * The XPath expression 815 * @param resolver 816 * A prefix resolver if the expression has prefix 817 * @return 818 * The matching boolean. 819 */ 820 public static boolean evaluateBooleanXPath(Element root, String expr, XPathPrefixResolver resolver) { 821 return impl.evaluateBooleanXPath_(root, expr, resolver); 822 } 823 824 /** 825 * Returns an XPath expression which enables reaching the specified node 826 * from the root node 827 * @param node 828 * The node to reach 829 * @param root 830 * The root node, or null to specify the document root 831 * @return 832 * An XPath expression which enables reaching the specified node 833 * from the root node 834 */ 835 public static String getXPath(Node node, Node root) { 836 StringBuilder builder = new StringBuilder(); 837 Node parentNode; 838 while ((node != root) && (parentNode = node.getParentNode()) != null) { 839 NodeList<Node> siblings = parentNode.getChildNodes(); 840 int index = 0; 841 for (int i = 0, length = siblings.getLength(); i < length; i++) { 842 Node sibling = siblings.getItem(i); 843 if (sibling.getNodeType() == Node.ELEMENT_NODE) { 844 index++; 845 if (node == sibling) { 846 builder.insert(0, "/*[" + index + "]"); 847 break; 848 } 849 } 850 } 851 node = parentNode; 852 } 853 return builder.toString(); 854 } 855 856 /** 857 * Returns whether touch events on SVG elements are supported 858 * on the current platform. 859 * @return true if touch events on SVG elements are supported 860 * on the current platform, false otherwise 861 * @deprecated 862 * Use the {@link #hasFeature(String)} method with the 863 * {@link SVGConstants#SVG_FEATURE_TOUCH_EVENTS} parameter instead. 864 */ 865 public static native boolean supportsSvgTouchEvents() /*-{ 866 var elem = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 867 elem.setAttribute('ontouchstart', 'return;'); 868 return (typeof elem.ontouchstart) == "function"; 869 }-*/; 870 871 /*** 872 * Returns whether drag and drop events on SVG elements are supported 873 * on the current platform. 874 * @return true if drag and drop events on SVG elements are supported 875 * on the current platform, false otherwise 876 */ 877 private static native boolean supportsSvgDndEvents() /*-{ 878 var elem = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 879 return 'ondragstart' in elem; 880 }-*/; 881 882 /** 883 * Returns a base64 encoding of the specified binary string 884 * @param str 885 * A binary string (obtained for instance by the FileReader API) 886 * @return a base64 encoded string. 887 */ 888 public static native String base64encode(String str) /*-{ 889 return $wnd.btoa(str); 890 }-*/; 891 892 }