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 }