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 * Returns the element which currently captures all the
545 * events after a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)}
546 * or null if element is set to capture events
547 * @return The event capturing element
548 */
549 public static OMSVGElement getCaptureElement() {
550 return impl.getCaptureElement();
551 }
552
553 /**
554 * Makes the specified element capture all the events, until
555 * a call to {@link org.vectomatic.dom.svg.utils.DOMHelper#releaseCaptureElement()}
556 * terminates the capture
557 * @param element The capturing element
558 * @param loseCaptureHandler A handler which will be invoked
559 * if the element loses capture
560 * @return {@link HandlerRegistration} used to remove this handler
561 */
562 public static HandlerRegistration setCaptureElement(OMSVGElement element, LoseCaptureHandler loseCaptureHandler) {
563 return impl.setCaptureElement(element, loseCaptureHandler);
564 }
565
566 /**
567 * Stops the forwarding of all events to the capturing element
568 * specified by {@link org.vectomatic.dom.svg.utils.DOMHelper#setCaptureElement(OMSVGElement, LoseCaptureHandler)}
569 */
570 public static void releaseCaptureElement() {
571 impl.releaseCaptureElement();
572 }
573
574 /**
575 * Returns the JavaScript type of an object.
576 * The function getType is borrowed from:
577 * JavaScript: The Definitive Guide, 5th Edition
578 * By David Flanagan
579 */
580 public static final native String getType(JavaScriptObject x) /*-{
581 // If x is null, return "null"
582 if (x == null) {
583 return "null";
584 }
585 // Next try the typeof operator
586 var t = typeof x;
587 // If the result is not vague, return it
588 if (t != "object") {
589 return t;
590 }
591 // Otherwise, x is an object. Use the default toString( ) method to
592 // get the class value of the object.
593 var c = Object.prototype.toString.apply(x); // Returns "[object class]"
594 c = c.substring(8, c.length-1); // Strip off "[object" and "]"
595
596 // If the class is not a vague one, return it.
597 if (c != "Object") {
598 return c;
599 }
600 // If we get here, c is "Object". Check to see if
601 // the value x is really just a generic object.
602 if (x.constructor == Object) {
603 return c; // Okay the type really is "Object"
604 }
605 // For user-defined classes, look for a string-valued property named
606 // classname, that is inherited from the object's prototype
607 if ("classname" in x.constructor.prototype && // inherits classname
608 typeof x.constructor.prototype.classname == "string") // its a string
609 return x.constructor.prototype.classname;
610
611 // If we really can't figure it out, say so.
612 return "<unknown type>";
613 }-*/;
614
615 /**
616 * Creates a value of the formed expected by SVG
617 * href attribtues.
618 * @param s the identifier of the data pointed by the href.
619 * @return a value of the formed expected by SVG
620 * href attribtues.
621 */
622 public static final String toUrl(String s) {
623 return "url(#" + s + ")";
624 }
625
626 /**
627 * Tests if the underlying DOM implementation supports the specified feature
628 * @param featureName
629 * The name of the feature to test
630 * @return
631 * true if the feature is supported, false otherwise
632 */
633 public static native boolean hasFeature(String featureName) /*-{
634 return $doc.implementation.hasFeature(featureName, 1.1);
635 }-*/;
636
637 /**
638 * Evaluates the specified XPath expression and returns
639 * a iterator for the selected {@link org.vectomatic.dom.svg.OMNode} node list.
640 * The expression must evaluate to a node list.
641 * @param root
642 * The element the expression is rooted at
643 * @param expr
644 * The XPath expression
645 * @param resolver
646 * A prefix resolver if the expression has prefix
647 * @return
648 * A node iterator for the selected nodes.
649 */
650 public static <T extends OMNode> Iterator<T> evaluateXPath(OMElement root, String expr, XPathPrefixResolver resolver) {
651 final JavaScriptObject result = impl.evaluateNodeListXPath_(root.getElement(), expr, resolver);
652 return new Iterator<T>() {
653 boolean fetched;
654 Node next;
655
656 @Override
657 public boolean hasNext() {
658 if (!fetched) {
659 next = iterateNext(result);
660 fetched = true;
661 }
662 return next != null;
663 }
664
665 @Override
666 public T next() {
667 if (!hasNext()) {
668 throw new NoSuchElementException();
669 }
670 fetched = false;
671 return OMNode.<T>convert(next);
672 }
673
674 @Override
675 public void remove() {
676 throw new UnsupportedOperationException();
677 }
678
679 private native Node iterateNext(JavaScriptObject result) /*-{
680 return result.iterateNext();
681 }-*/;
682 };
683
684 }
685
686 /**
687 * Evaluates the specified XPath expression and returns
688 * a iterator for the selected {@link com.google.gwt.dom.client.Node} node list.
689 * The expression must evaluate to a node list.
690 * @param root
691 * The element the expression is rooted at
692 * @param expr
693 * The XPath expression
694 * @param resolver
695 * A prefix resolver if the expression has prefix
696 * @return
697 * A node iterator for the selected nodes.
698 */
699 public static <T extends Node> Iterator<T> evaluateNodeListXPath(Element root, String expr, XPathPrefixResolver resolver) {
700 final JavaScriptObject result = impl.evaluateNodeListXPath_(root, expr, resolver);
701 return new Iterator<T>() {
702 boolean fetched;
703 T next;
704
705 @Override
706 public boolean hasNext() {
707 if (!fetched) {
708 next = iterateNext(result);
709 fetched = true;
710 }
711 return next != null;
712 }
713
714 @Override
715 public T next() {
716 if (!hasNext()) {
717 throw new NoSuchElementException();
718 }
719 fetched = false;
720 return next;
721 }
722
723 @Override
724 public void remove() {
725 throw new UnsupportedOperationException();
726 }
727
728 private native T iterateNext(JavaScriptObject result) /*-{
729 return result.iterateNext();
730 }-*/;
731 };
732 }
733
734 /**
735 * Evaluates the specified XPath expression and returns
736 * the matching {@link com.google.gwt.dom.client.Node} node.
737 * The expression must evaluate to a single node.
738 * @param root
739 * The element the expression is rooted at
740 * @param expr
741 * The XPath expression
742 * @param resolver
743 * A prefix resolver if the expression has prefix
744 * @return
745 * The selected node, or null if no such node exists.
746 */
747 public static <T extends Node> T evaluateNodeXPath(Element root, String expr, XPathPrefixResolver resolver) {
748 return (T)(impl.evaluateNodeXPath_(root, expr, resolver));
749 }
750
751 /**
752 * Evaluates the specified XPath expression and returns
753 * the matching {@link java.lang.String}.
754 * The expression must evaluate to a string.
755 * @param root
756 * The element the expression is rooted at
757 * @param expr
758 * The XPath expression
759 * @param resolver
760 * A prefix resolver if the expression has prefix
761 * @return
762 * The matching string, or null if no such string exists.
763 */
764 public static String evaluateStringXPath(Element root, String expr, XPathPrefixResolver resolver) {
765 return impl.evaluateStringXPath_(root, expr, resolver);
766 }
767
768 /**
769 * Evaluates the specified XPath expression and returns
770 * the matching float.
771 * The expression must evaluate to a number.
772 * @param root
773 * The element the expression is rooted at
774 * @param expr
775 * The XPath expression
776 * @param resolver
777 * A prefix resolver if the expression has prefix
778 * @return
779 * The matching float, or NaN if no such number exists.
780 */
781 public static float evaluateNumberXPath(Element root, String expr, XPathPrefixResolver resolver) {
782 return impl.evaluateNumberXPath_(root, expr, resolver);
783 }
784
785 /**
786 * Evaluates the specified XPath expression and returns
787 * the matching boolean.
788 * The expression must evaluate to a boolean.
789 * @param root
790 * The element the expression is rooted at
791 * @param expr
792 * The XPath expression
793 * @param resolver
794 * A prefix resolver if the expression has prefix
795 * @return
796 * The matching boolean.
797 */
798 public static boolean evaluateBooleanXPath(Element root, String expr, XPathPrefixResolver resolver) {
799 return impl.evaluateBooleanXPath_(root, expr, resolver);
800 }
801
802 /**
803 * Returns an XPath expression which enables reaching the specified node
804 * from the root node
805 * @param node
806 * The node to reach
807 * @param root
808 * The root node, or null to specify the document root
809 * @return
810 * An XPath expression which enables reaching the specified node
811 * from the root node
812 */
813 public static String getXPath(Node node, Node root) {
814 StringBuilder builder = new StringBuilder();
815 Node parentNode;
816 while ((node != root) && (parentNode = node.getParentNode()) != null) {
817 NodeList<Node> siblings = parentNode.getChildNodes();
818 int index = 0;
819 for (int i = 0, length = siblings.getLength(); i < length; i++) {
820 Node sibling = siblings.getItem(i);
821 if (sibling.getNodeType() == Node.ELEMENT_NODE) {
822 index++;
823 if (node == sibling) {
824 builder.insert(0, "/*[" + index + "]");
825 break;
826 }
827 }
828 }
829 node = parentNode;
830 }
831 return builder.toString();
832 }
833
834 /***
835 * Returns whether touch events on SVG elements are supported
836 * on the current platform.
837 * @return true if touch events on SVG elements are supported
838 * on the current platform, false otherwise
839 */
840 public static native boolean supportsSvgTouchEvents() /*-{
841 var elem = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
842 elem.setAttribute('ontouchstart', 'return;');
843 return (typeof elem.ontouchstart) == "function";
844 }-*/;
845 }