View Javadoc

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 }