001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.dom;
021 
022 import js.*;
023 import js.core.*;
024 import js.user.*;
025 import jsx.Code;
026 import jsx.client.*;
027 import jsx.core.*;
028 import jsx.event.Handle;
029 
030 /**
031  * <p>A utility class manipulating DOM elements with its static methods.</p>
032  * 
033  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
034  */
035 public final class Elements extends Disposable
036 {
037     private Elements() {}
038 
039     /**
040      * <p>Creates a new {@link JsElement} node with the specified tag name.</p>
041      * @param tagName The tag name of the {@link JsElement} node being created. Since 
042      * HTML tags are case-insensitive, you may use any capitalization for HTML tag names. 
043      * XML tag names are case-sensitive. 
044      * @return A newly created {@link JsElement} node with the specified tag name. 
045      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
046      * the {@link JsDOMException#code} property of the value {@link JsDOMException#INVALID_CHARACTER_ERR} 
047      * if <tt>tagName</tt> contains an illegal character. See {@link Js#err(Object)} 
048      * for JS Simulation.
049      * @since 1.0
050      * @see jsx.client.Document#createElement(String)
051      * @see JsDocument#createElement(String)
052      */
053     public final static JsElement createElement(String tagName) {
054         return Win.document.var().createElement(tagName);
055     }
056 
057     /**
058      * <p>Creates a new {@link JsElement} node with the specified tag name and name-space.</p>
059      * <p>This method is just like {@link #createElement(String)}, except that the 
060      * created {@link JsElement} node has a name and name space instead of just a name. 
061      * This method is useful only with XML documents that use name spaces.</p>
062      * @param namespaceURI The unique identifier for the name space of the new {@link JsElement} 
063      * node or <tt>null</tt> for no name space. 
064      * @param qualifiedName The qualified name of the new {@link JsElement} node. This 
065      * should include a name space prefix, a colon, and a local name. 
066      * @return A newly created {@link JsElement} node with the specified tag name and 
067      * name space. 
068      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
069      * the {@link JsDOMException#code} property of the value {@link JsDOMException#INVALID_CHARACTER_ERR} 
070      * if <tt>qualifiedName</tt> contains an illegal character, the value {@link JsDOMException#NAMESPACE_ERR} 
071      * if <tt>qualifiedName</tt> is malformed or there is a mismatch between <tt>qualifiedName</tt> 
072      * and <tt>namespaceURI</tt>, or the value {@link JsDOMException#NOT_SUPPORTED_ERR} 
073      * if the implementation does not support XML documents and therefore does not implement 
074      * this method. See {@link js.Js#err(Object)} for JS Simulation.
075      * @since 1.0
076      * @see JsDocument#createElementNS(String, String)
077      */
078     public final static JsElement createElementNS(String namespaceURI, String qualifiedName) {
079         return Win.document.var().createElementNS(namespaceURI, qualifiedName);
080     }
081 
082     /**
083      * <p>Returns the value of a named attribute as a string.</p>
084      * <p>This method returns the value of a named attribute of an element. Note that 
085      * the {@link JsHTMLElement} objects have JavaScript properties that match each of 
086      * the standard HTML attributes, so you need to use this method with HTML documents 
087      * only if you are querying the value of nonstandard attributes.</p>
088      * <p>In XML documents, attribute values are not available directly as element 
089      * properties and must be looked up by calling this method. For XML documents that 
090      * use name spaces, use {@link #getAttributeNS(JsElement, String, String)}.</p>
091      * @param e The current element.
092      * @param name The name of the attribute whose value is to be returned.
093      * @return The value of the named attribute as a string. If the attribute is not 
094      * defined, this method is supposed to return an empty string. Some implementations 
095      * return <tt>null</tt> in this case, however.
096      * @since 1.0
097      * @see JsElement#getAttribute(String)
098      */
099     public final static String getAttribute(JsElement e, String name) {
100         return e.getAttribute(name);
101     }
102 
103     /**
104      * <p>Returns the string value of an attribute specified by local name and name space URI.</p> 
105      * <p>This method is useful only with XML documents that use name spaces and works 
106      * just like the {@link #getAttribute(JsElement, String)} method, except that the attribute is 
107      * specified by a combination of name space URI and local name within that name 
108      * space.</p>
109      * @param e The current element.
110      * @param namespaceURI The URI that uniquely identifies the name space of this 
111      * attribute or <tt>null</tt> for no name space.
112      * @param localName The identifier that specifies the name of the attribute within 
113      * its name space.
114      * @return The value of the named attribute, as a string. If the attribute is not 
115      * defined, this method is supposed to return an empty string, but some implementations 
116      * return <tt>null</tt> instead.
117      * @since 1.0
118      * @see JsElement#getAttributeNodeNS(String, String)
119      */
120     public final static String getAttributeNS(JsElement e, String namespaceURI, String localName) {
121         return e.getAttributeNS(namespaceURI, localName);
122     }
123 
124     /**
125      * <p>Sets or adds the named attribute to the specified string value.</p>
126      * <p>This method sets the specified attribute to the specified value. If no 
127      * attribute by that name already exists, a new one is created.</p>
128      * <p>Note that {@link JsHTMLElement} objects of an HTML document define JavaScript 
129      * properties that correspond to all standard HTML attributes. Thus, you need to use 
130      * this method only if you want to set a nonstandard attribute.</p>
131      * @param e The current element.
132      * @param name The name of the attribute to be created or modified.
133      * @param value The string value of the attribute.
134      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
135      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
136      * if this element is read-only and does not allow its attributes to be removed or 
137      * the value {@link JsDOMException#INVALID_CHARACTER_ERR} if the <tt>name</tt> argument 
138      * contains a character that is not allowed in HTML or XML attribute names. See 
139      * {@link Js#err(Object)} for JS Simulation.
140      * @since 1.0
141      * @see JsElement#setAttribute(String, Object)
142      */
143     public final static void setAttribute(JsElement e, String name, Object value) {
144         e.setAttribute(name, value);
145     }
146 
147     /**
148      * <p>Sets or adds, to the list of attributes for this element, an attribute with 
149      * the specified a combination of local name and name space URI.</p>
150      * <p>This method is useful only with XML documents that use name spaces and works 
151      * just like {@link #setAttribute(JsElement, String, Object)} method, except that the attribute 
152      * is specified by the combination of a name space URI and a local name defined 
153      * within that name space.</p>
154      * @param e The current element.
155      * @param namespaceURI The URI that uniquely identifies the name space of the 
156      * attribute to be set or created, or <tt>null</tt> for no name space.
157      * @param localName The name of the attribute, specified as an optional name 
158      * space prefix and colon followed by the local name within the name space.
159      * @param value The new value of the attribute.
160      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
161      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
162      * if this element is read-only and does not allow its attributes to be removed, 
163      * the value {@link JsDOMException#INVALID_CHARACTER_ERR} if <tt>qualifiedName</tt> 
164      * argument contains a character that is not allowed in HTML or XML attribute names, 
165      * the value {@link JsDOMException#NAMESPACE_ERR} if <tt>qualifiedName</tt> is 
166      * malformed, or there is a mismatch between the name space prefix of <tt>qualifiedName</tt> 
167      * and the <tt>namespaceURI</tt> argument, or the value {@link JsDOMException#NOT_SUPPORTED_ERR} 
168      * if the DOM implementation of the browser does not support XML documents. See 
169      * {@link Js#err(Object)} for JS Simulation.
170      * @since 1.0
171      * @see JsElement#setAttributeNS(String, String, String)
172      */
173     public final static void setAttributeNS(JsElement e, String namespaceURI, String localName, String value) {
174         e.setAttributeNS(namespaceURI, localName, value);
175     }
176 
177     /**
178      * <p>Removes the named attribute from this element.</p>
179      * <p>This method deletes a named attribute from this element. If the named attribute 
180      * has a default value specified by the document type, subsequent calls to this method 
181      * return that default value. Attempts to remove nonexistent attributes or attributes 
182      * that are not specified but have a default value are silently ignored.</p>
183      * @param e The current element.
184      * @param name The name of the desired attribute.
185      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
186      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
187      * if this element is read-only and does not allow its attributes to be removed. See 
188      * {@link Js#err(Object)} for JS Simulation.
189      * @since 1.0
190      * @see JsElement#removeAttribute(String)
191      */
192     public final static void removeAttribute(JsElement e, String name) {
193         e.removeAttribute(name);
194     }
195 
196     /**
197      * <p>Removes, from this element, an attribute with the specified a combination of 
198      * local name and name space URI.</p>
199      * <p>This method is useful only with XML documents that use name spaces and works 
200      * just like {@link #removeAttribute(JsElement, String)} method, except that the attribute to 
201      * be removed is specified by the combination of a name space URI and a local name 
202      * defined within that name space.</p>
203      * @param e The current element.
204      * @param namespaceURI The URI that uniquely identifies the name space of this 
205      * attribute or <tt>null</tt> for no name space.
206      * @param localName The identifier that specifies the name of the attribute within 
207      * its name space.
208      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
209      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
210      * if this element is read-only and does not allow its attributes to be removed. See 
211      * {@link Js#err(Object)} for JS Simulation.
212      * @since 1.0
213      * @see JsElement#removeAttributeNS(String, String)
214      */
215     public final static void removeAttributeNS(JsElement e, String namespaceURI, String localName) {
216         e.removeAttributeNS(namespaceURI, localName);
217     }
218 
219     /**
220      * <p>Creates an HTML button element.</p>
221      * @param value The string value for the button being created.
222      * @return The newly created HTML element.
223      * @since 1.0
224      */
225     public final static JsHTMLButtonElement createButton(String value) {
226         JsHTMLButtonElement ret = new JsHTMLButtonElement(
227                 JsWindow.document.with(Js.core()).createElement("Button")
228         );
229         JsHTMLButtonElement.value.with(ret, value);
230         return ret;
231     }
232 
233     /**
234      * <p>Creates an HTML div element.</p>
235      * @return The newly created HTML element.
236      * @since 1.0
237      */
238     public final static JsHTMLDivElement createDiv() {
239         return new JsHTMLDivElement(
240                 JsWindow.document.with(Js.core()).createElement("div")
241         );
242     }
243 
244     /**
245      * <p>Applies a style object to an HTML element.</p>
246      * @param e The current HTML element.
247      * @param p A {@link JsCSS2Properties} object.
248      * @since 1.0
249      */
250     public static final void applyStyle(JsHTMLElement e, ObjectLike p) {
251         Js.apply(style(e), p);
252     }
253 
254     /**
255      * <p>Gets the id value of an HTML element.</p>
256      * @param e The current HTML element.
257      * @return The string representation of the id value.
258      * @since 1.0
259      */
260     public final static String id(JsElement e) {
261         return JsHTMLElement.id.with(e);
262     }
263 
264     /**
265      * <p>Sets the id value of an HTML element.</p>
266      * @param e The current HTML element.
267      * @param id The string representation of the id value.
268      * @since 1.0
269      */
270     public final static String id(JsElement e, String id) {
271         return JsHTMLElement.id.with(e, id);
272     }
273 
274     /**
275      * <p>Gets the style object of an HTML element.</p>
276      * @param e The current HTML element.
277      * @return The {@link JsCSS2Properties} object.
278      * @since 1.0
279      */
280     public final static JsObject style(JsHTMLElement e) {
281         return JsHTMLElement.style.with(e);
282     }
283 
284     /**
285      * <p>Determines if a given element is an HTML element.</p>
286      * <p>This method does the job by only testing the existence of {@link JsHTMLElement#style} 
287      * property in the element object.</p>
288      * @param e The given element.
289      * @return <tt>true</tt> if it is an HTML element; <tt>false</tt>, otherwise.
290      * @since 1.0
291      */
292     public final static boolean isHTMLElement(JsElement e) {
293         return Js.be(JsHTMLElement.style.with(e));
294     }
295 
296     /**
297      * <p>Gets or computes the {@link JsHTMLElement#currentStyle} of an HTML element.</p>
298      * <p>This method eliminates the browser dependencies.</p>
299      * @param e The given HTML element.
300      * @return The current {@link JsCSS2Properties} object in the element.
301      * @since 1.0
302      */
303     public final static JsObject currentStyle(JsHTMLElement e) {
304         return Browser.isIE ? JsHTMLElement.currentStyle.with(e) :
305                 Win.getComputedStyle(e);
306     }
307 
308     /**
309      * <p>Gets the {@link JsHTMLElement#offsetParent} property of an HTML element.</p>
310      * @param e The given HTML element.
311      * @return An HTML element.
312      * @since 1.0
313      */
314     public final static JsHTMLElement offsetParent(JsHTMLElement e) {
315         return JsHTMLElement.offsetParent.with(e);
316     }
317 
318     /**
319      * <p>Gets the parent element of an element.</p>
320      * @param e The given element.
321      * @return An element or <tt>null</tt> for none.
322      * @since 1.0
323      */
324     public final static JsElement parentNode(JsElement e) {
325         JsNode p = Nodes.parentNode(e);
326         return Js.be(p) && Js.be(Nodes.tagName(p)) ? new JsElement(p) : null;
327     }
328 
329     /**
330      * <p>Sets the "position" style of an HTML element to "absolute".</p>
331      * @param e The given HTML element.
332      * @since 1.0
333      */
334     public static final void absolute(JsHTMLElement e) {
335         Styles.position(style(e), "absolute");
336     }
337 
338     /**
339      * <p>Computes the transparency value for an HTML element.</p>
340      * @param e The given HTML element.
341      * @return A number value of transparency.
342      * @since 1.0
343      */
344     public static final double tranparency(JsHTMLElement e) {
345         return Styles.transparency(currentStyle(e));
346     }
347 
348     /**
349      * <p>Sets the transparency value for an HTML element.</p>
350      * @param e The given HTML element.
351      * @param v A number value of transparency.
352      * @since 1.0
353      */
354     public static final void transparency(JsHTMLElement e, double v) {
355         Styles.transparency(style(e), v);
356     }
357 
358     /**
359      * <p>Gets the {@link JsHTMLElement#offsetTop} value of an HTML element.</p>
360      * @param e The given HTML element.
361      * @return A number in pixel.
362      * @since 1.0
363      */
364     public final static Number offsetTop(JsHTMLElement e) {
365         return JsHTMLElement.offsetTop.with(e);
366     }
367 
368     /**
369      * <p>Gets the {@link JsHTMLElement#offsetLeft} value of an HTML element.</p>
370      * @param e The given HTML element.
371      * @return A number in pixel.
372      * @since 1.0
373      */
374     public final static Number offsetLeft(JsHTMLElement e) {
375         return JsHTMLElement.offsetLeft.with(e);
376     }
377 
378     /**
379      * <p>Gets the {@link JsHTMLElement#offsetWidth} value of an HTML element.</p>
380      * @param e The given HTML element.
381      * @return A number in pixel.
382      * @since 1.0
383      */
384     public final static Number offsetWidth(JsHTMLElement e) {
385         return JsHTMLElement.offsetWidth.with(e);
386     }
387 
388     /**
389      * <p>Gets the {@link JsHTMLElement#offsetHeight} value of an HTML element.</p>
390      * @param e The given HTML element.
391      * @return A number in pixel.
392      * @since 1.0
393      */
394     public final static Number offsetHeight(JsHTMLElement e) {
395         return JsHTMLElement.offsetHeight.with(e);
396     }
397 
398     /**
399      * <p>Gets the {@link JsHTMLElement#scrollTop} value of an HTML element.</p>
400      * @param e The given HTML element.
401      * @return A number in pixel.
402      * @since 1.0
403      */
404     public final static Number scrollTop(JsHTMLElement e) {
405         return JsHTMLElement.scrollTop.with(e);
406     }
407 
408     /**
409      * <p>Gets the {@link JsHTMLElement#scrollLeft} value of an HTML element.</p>
410      * @param e The given HTML element.
411      * @return A number in pixel.
412      * @since 1.0
413      */
414     public final static Number scrollLeft(JsHTMLElement e) {
415         return JsHTMLElement.scrollLeft.with(e);
416     }
417 
418     /**
419      * <p>Gets the {@link JsHTMLElement#scrollWidth} value of an HTML element.</p>
420      * @param e The given HTML element.
421      * @return A number in pixel.
422      * @since 1.0
423      */
424     public final static Number scrollWidth(JsHTMLElement e) {
425         return JsHTMLElement.scrollWidth.with(e);
426     }
427 
428     /**
429      * <p>Gets the {@link JsHTMLElement#scrollHeight} value of an HTML element.</p>
430      * @param e The given HTML element.
431      * @return A number in pixel.
432      * @since 1.0
433      */
434     public final static Number scrollHeight(JsHTMLElement e) {
435         return JsHTMLElement.scrollHeight.with(e);
436     }
437 
438     /**
439      * <p>Returns a function for adding the given CSS to an HTML element.</p>
440      * @param e The given HTML element.
441      * @param s The CSS class name to add.
442      * @return A function for adding the given CSS to the given HTML element.
443      * @since 1.0
444      */
445     public final static JsFunction<Void> cssAdder(JsHTMLElement e, String s) {
446         return new JsFunction<Void>(
447                 Handle.HANDLER.var().invoke(
448                         new Vars<Object>().add(e).add(
449                                 Js.function(
450                                         Code.stmt(
451                                                 Code.aasg(
452                                                         Code.THIS_CLASSNAME,
453                                                         Code.qt(s)
454                                                 )
455                                         )
456                                 )
457                         )
458                 )
459         );
460     }
461 
462     /**
463      * <p>Returns a function for removing the given CSS from an HTML element.</p>
464      * @param e The given HTML element.
465      * @param s The CSS class name to remove.
466      * @return A function for removing the given CSS from the given HTML element.
467      * @since 1.0
468      */
469     public final static JsFunction<Void> cssRemover(JsHTMLElement e, String s) {
470         return new JsFunction<Void>(
471                 Handle.HANDLER.var().invoke(
472                         new Vars<Object>().add(e).add(
473                                 Js.function(
474                                         Code.replaceThisClassName(
475                                                 Code.join(r1, s, r2),
476                                                 Code.EMPTY
477                                         )
478                                 )
479                         )
480                 )
481         );
482     }
483 
484     /**
485      * <p>Returns the {@link JsHTMLElement#className} value of an HTML element.</p>
486      * @param e The given HTML element.
487      * @return A string of CSS class names.
488      * @since 1.0
489      */
490     public final static String className(JsHTMLElement e) {
491         return JsHTMLElement.className.with(e);
492     }
493 
494     /**
495      * <p>Sets the {@link JsHTMLElement#className} property of an HTML element.</p>
496      * @param e The given HTML element.
497      * @param css A string of CSS class names.
498      * @since 1.0
499      */
500     public final static String className(JsHTMLElement e, String css) {
501         return JsHTMLElement.className.with(e, css);
502     }
503 
504     /**
505      * <p>Appends a CSS class name to an HTML element.</p>
506      * @param e The given HTML element.
507      * @param cls A string value of CSS class name.
508      * @since 1.0
509      */
510     public final static void addClass(JsHTMLElement e, String cls) {
511         if (Js.be(cls)) {
512             String className = className(e);
513             if (!hasClass(className, cls)) {
514                 className(e, Code.join(className, " ", cls));
515             }
516         }
517     }
518 
519     /**
520      * <p>Appends a CSS class name to an HTML element.</p>
521      * @param e The given HTML element.
522      * @param classes An array of CSS class names.
523      * @since 1.0
524      */
525     public final static void addClasses(JsHTMLElement e, ArrayLike<String> classes) {
526         String className = className(e);
527         int n = 0;
528         for (int i = 0, len = ArrayLikes.length(classes); i < len; i++) {
529             String cls = classes.get(i);
530             if (!hasClass(className, cls)) {
531                 className = Code.join(className, " ", cls);
532                 n++;
533             }
534         }
535         if (n > 0) {
536             className(e, className);
537         }
538     }
539 
540     /**
541      * <p>Determines if an HTML element has a CSS class name.</p>
542      * @param e The given HTML element.
543      * @param cls A string value of CSS class name.
544      * @return <tt>true</tt> if the HTML element has the CSS class name; <tt>false</tt>, otherwise.
545      * @since 1.0
546      */
547     public final static boolean hasClass(JsHTMLElement e, String cls) {
548         return Js.be(e) && hasClass(className(e), cls);
549     }
550 
551     /**
552      * <p>Checks if a name string has a CSS class name.</p>
553      * @param className A string of CSS class names.
554      * @param cls A string value of CSS class name.
555      * @return <tt>true</tt> if the string has the CSS class name; <tt>false</tt>, otherwise.
556      * @since 1.0
557      */
558     public final static boolean hasClass(String className, String cls) {
559         return Js.neq(
560                 Code.join(" ", className, " ").indexOf(
561                         Code.join(" ", cls, " ")
562                 ),
563                 -1
564         );
565     }
566 
567     private final static String r1 = "(?:^|\\s+)";
568     private final static String r2 = "(?:\\s+|$)";
569 
570     /**
571      * <p>Removes a CSS class name from an HTML element.</p>
572      * @param e The given HTML element.
573      * @param cls A string value of CSS class name.
574      * @since 1.0
575      */
576     public final static void removeClass(JsHTMLElement e, String cls) {
577         String className = className(e);
578         if (hasClass(className, cls)) {
579             className(
580                     e,
581                     StringLikes.trim(StringLikes.replace(
582                             className,
583                             Js.re(Code.join(r1, cls, r2), "g"),
584                             " "
585                     ))
586             );
587         }
588     }
589 
590     /**
591      * <p>Removes a CSS class name from an HTML element.</p>
592      * @param e The given HTML element.
593      * @param classes An array of CSS class names.
594      * @since 1.0
595      */
596     public final static void removeClasses(JsHTMLElement e, ArrayLike<String> classes) {
597         String className = className(e);
598         int n = 0;
599         for (int i = 0, len = ArrayLikes.length(classes); i < len; i++) {
600             String cls = classes.get(i);
601             if (hasClass(className, cls)) {
602                 className = StringLikes.replace(
603                         className,
604                         Js.re(Code.join(r1, cls, r2), "g"),
605                         " "
606                 );
607                 n++;
608             }
609         }
610         if (n > 0) {
611             className(
612                     e,
613                     StringLikes.trim(className)
614             );
615         }
616     }
617 }