0001 
0002 /*
0003  *  JScripter Standard 1.0 - To Script In Java
0004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
0005  *  
0006  *  This program is free software: you can redistribute it and/or modify
0007  *  it under the terms of the GNU Affero General Public License as published by
0008  *  the Free Software Foundation, either version 3 of the License, or
0009  *  (at your option) any later version.
0010  *  
0011  *  This program is distributed in the hope that it will be useful,
0012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  *  GNU Affero General Public License for more details.
0015  *  
0016  *  You should have received a copy of the GNU Affero General Public License
0017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 package jsx.ui;
0021 
0022 import js.ArrayLike;
0023 import js.Id;
0024 import js.Initializer;
0025 import js.Js;
0026 import js.ObjectLike;
0027 import js.Vars;
0028 import js.core.JsFunction;
0029 import js.user.JsDOMException;
0030 import js.user.JsElement;
0031 import js.user.JsHTMLElement;
0032 import js.user.JsNode;
0033 import jsx.Configurable;
0034 import jsx.client.Document;
0035 import jsx.client.Browser;
0036 import jsx.client.Win;
0037 import jsx.core.ArrayLikes;
0038 import jsx.core.ObjectLikes;
0039 import jsx.core.StringLikes;
0040 import jsx.core.Variables;
0041 import jsx.dom.Elements;
0042 import jsx.dom.Markups;
0043 import jsx.dom.Nodes;
0044 import jsx.dom.Styles;
0045 import jsx.event.Handle;
0046 import jsx.ui.css.Pseudo;
0047 import jsx.ui.event.OnRender;
0048 import jsx.ui.event.Render;
0049 import jsx.ui.event.Style;
0050 import jsx.ui.fx.event.Animation;
0051 
0052 /**
0053  * <p>Defines components wrapping HTML or XML elements.</p>
0054  * <p>A component of this class uniquely wraps an underlying HTML or XML element. It is a 
0055  * {@link Widget} and can be wrapped by {@link Widget} to make more complex widgets.</p>
0056  * <p>Note that, components are {@link Configurable} and event sources which may fire 
0057  * {@link Widget.Event} high level events.</p>
0058  * 
0059  * @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>
0060  */
0061 public final class Component extends Widget implements OnRender
0062 {
0063     private final static Id<JsElement> ELEMENT = new Id<JsElement>();
0064     private final static Id<String> HTML = new Id<String>();
0065     private final static Id<Boolean> DIV = new Id<Boolean>();
0066     private final static Id<Boolean> SPAN = new Id<Boolean>();
0067     private final static Id<Boolean> FIXED = new Id<Boolean>();
0068 
0069     /**
0070      * <p>Returns the underlying opaque element.</p>
0071      * @param e The current component.
0072      * @return The wrapped XML or HTML element.
0073      * @since 1.0
0074      */
0075     public static final JsElement getElement(Component e) {
0076         return ini(e).var(ELEMENT);
0077     }
0078 
0079     /**
0080      * <p>Returns the underlying opaque element as an HTML element.</p>
0081      * @param e The current component.
0082      * @return The wrapped HTML element.
0083      * @since 1.0
0084      */
0085     public static final JsHTMLElement getHTMLElement(Component e) {
0086         return new JsHTMLElement(getElement(e));
0087     }
0088 
0089     /**
0090      * <p>Returns the HTML text of the underlying element.</p>
0091      * @param e The current component.
0092      * @return The HTML text of the underlying element.
0093      * @since 1.0
0094      */
0095     public static final String getHTML(Component e) {
0096         return ini(e).var(HTML);
0097     }
0098 
0099     /**
0100      * <p>Determines if the underlying element is an HTML <tt>&lt;div&gt;</tt> element.</p>
0101      * @param e The current component.
0102      * @return <tt>true</tt> if the underlying element is an HTML <tt>&lt;div&gt;</tt> 
0103      * element; <tt>false</tt>, otherwise.
0104      * @since 1.0
0105      */
0106     public static final boolean div(Component e) {
0107         if (Variables.undefined(ini(e).var(DIV))) {
0108             ini(e).var(DIV, Nodes.tagName(Component.getHTMLElement(e)) == "DIV");
0109         }
0110         return ini(e).var(DIV);
0111     }
0112 
0113     /**
0114      * <p>Determines if the underlying element is an HTML <tt>&lt;span&gt;</tt> element.</p>
0115      * @param e The current component.
0116      * @return <tt>true</tt> if the underlying element is an HTML <tt>&lt;span&gt;</tt> 
0117      * element; <tt>false</tt>, otherwise.
0118      * @since 1.0
0119      */
0120     public static final boolean span(Component e) {
0121         if (Variables.undefined(ini(e).var(SPAN))) {
0122             ini(e).var(SPAN, Nodes.tagName(Component.getHTMLElement(e)) == "SPAN" ||
0123                     Nodes.tagName(Component.getHTMLElement(e)) == "NOBR");
0124         }
0125         return ini(e).var(SPAN);
0126     }
0127 
0128     /**
0129      * <p>Determines if the underlying element is fixed.</p>
0130      * @param e The current component.
0131      * @return <tt>true</tt> if the underlying element is fixed; <tt>false</tt>, otherwise.
0132      * @since 1.0
0133      * @see #fix(Component, boolean)
0134      */
0135     public static final boolean fixed(Component e) {
0136         return Js.be(ini(e).var(FIXED));
0137     }
0138 
0139     /**
0140      * <p>Fixes the underlying element.</p>
0141      * @param e The current component.
0142      * @since 1.0
0143      * @see #fixed(Component)
0144      */
0145     public static final void fix(Component e, boolean fixed) {
0146         ini(e).var(FIXED, fixed);
0147     }
0148 
0149     private static int untitled = 1;
0150 
0151     /**
0152      * <p>Creates a component for untitled.</p>
0153      * @return The component for untitled.
0154      * @since 1.0
0155      * @see #title(String)
0156      */
0157     public static final Component untitled() {
0158         return new Component(Markups.span(Js.add("Untitled", untitled++)));
0159     }
0160 
0161     /**
0162      * <p>Creates a component for a title.</p>
0163      * @param html The HTML text for the title.
0164      * @return The component for the title.
0165      * @since 1.0
0166      * @see #untitled()
0167      */
0168     public static final Component title(String html) {
0169         return new Component(Markups.span(html));
0170     }
0171 
0172     /**
0173      * <p>Determines whether a component contains another.</p>
0174      * @param p A component.
0175      * @param c A component.
0176      * @return <tt>true</tt> if the underlying HTML element of the component <tt>p</tt> 
0177      * contains the underlying HTML element of the component <tt>c</tt>; <tt>false</tt>, 
0178      * otherwise.
0179      * @since 1.0
0180      */
0181     public static final boolean contains(Component p, Component c) {
0182         return Nodes.contains(getHTMLElement(p), getHTMLElement(c));
0183     }
0184 
0185     private final static Id<ObjectLike> ATTRIBUTE = new Id<ObjectLike>();
0186     private final static Id<ObjectLike> CACHE = new Id<ObjectLike>();
0187     private final static Id<ObjectLike> STYLE = new Id<ObjectLike>();
0188     private final static Id<ArrayLike<String>> CLASS = new Id<ArrayLike<String>>();
0189     private final static Id<Component> PARENT = new Id<Component>();
0190     private final static Id<ArrayLike<Component>> CHILDREN = new Id<ArrayLike<Component>>();
0191 
0192     /**
0193      * <p>Returns the id of the underlying element.</p>
0194      * <p>No two elements within the same document should have the same value for 
0195      * {@link JsHTMLElement#id}.</p>
0196      * @param e The current component.
0197      * @return The {@link JsHTMLElement#id} property of the underlying element.
0198      * @since 1.0
0199      */
0200     public static final String id(Component e) {
0201         return JsHTMLElement.id.with(getElement(e));
0202     }
0203 
0204     /**
0205      * <p>Returns the title of the underlying element.</p>
0206      * <p>Web browsers display the value of the {@link JsHTMLElement#title} attribute in 
0207      * a tool tip when the mouse hovers over the element.</p>
0208      * @param e The current component.
0209      * @return The {@link JsHTMLElement#title} attribute of the underlying element.
0210      * @since 1.0
0211      */
0212     public static final String title(Component e) {
0213         return JsHTMLElement.title.with(getElement(e));
0214     }
0215 
0216     /**
0217      * <p>Sets new id of the underlying element.</p>
0218      * <p>No two elements within the same document should have the same value for 
0219      * {@link JsHTMLElement#id}.</p>
0220      * @param e The current component.
0221      * @param id The new id to be set to the {@link JsHTMLElement#id} property of the 
0222      * underlying element.
0223      * @return The new id.
0224      * @since 1.0
0225      */
0226     public static final String id(Component e, String id) {
0227         JsHTMLElement he = getHTMLElement(e);
0228         return Js.be(he) ? JsHTMLElement.id.with(he, id) : 
0229             JsHTMLElement.id.with(ini(e).var(ATTRIBUTE), id);
0230     }
0231 
0232     /**
0233      * <p>Sets new title of the underlying element.</p>
0234      * <p>Web browsers display the value of the {@link JsHTMLElement#title} attribute in 
0235      * a tool tip when the mouse hovers over the element.</p>
0236      * @param e The current component.
0237      * @param title The new title to be set to the {@link JsHTMLElement#title} property of 
0238      * the underlying element.
0239      * @return The new title.
0240      * @since 1.0
0241      */
0242     public static final String title(Component e, String title) {
0243         JsHTMLElement he = getHTMLElement(e);
0244         return Js.be(he) ? JsHTMLElement.title.with(he, title) : 
0245             JsHTMLElement.title.with(ini(e).var(ATTRIBUTE), title);
0246     }
0247 
0248     /**
0249      * <p>Gets the style object being applied to the underlying element.</p>
0250      * @param e The current component.
0251      * @return The style object being applied to the underlying element.
0252      * @since 1.0
0253      */
0254     public static final ObjectLike style(Component e) {
0255         return ini(e).var(STYLE);
0256     }
0257 
0258     /**
0259      * <p>Adds a CSS class name to the underlying HTML element.</p>
0260      * @param e The current component.
0261      * @param cls The class name to add.
0262      * @since 1.0
0263      */
0264     public static final void addClass(Component e, String cls) {
0265         JsHTMLElement he = getHTMLElement(e);
0266         if (Js.be(he)) {
0267             Elements.addClass(he, cls);
0268             calculate(e);
0269         } else {
0270             ArrayLikes.push(ini(e).var(CLASS), cls);
0271         }
0272     }
0273 
0274     /**
0275      * <p>Removes a CSS class name from the underlying HTML element.</p>
0276      * @param e The current component.
0277      * @param cls The class name to add.
0278      * @since 1.0
0279      */
0280     public static final void removeClass(Component e, String cls) {
0281         JsHTMLElement he = getHTMLElement(e);
0282         if (Js.be(he)) {
0283             Elements.removeClass(he, cls);
0284             calculate(e);
0285         } else {
0286             ArrayLikes.remove(ini(e).var(CLASS), cls);
0287         }
0288     }
0289 
0290     /**
0291      * <p>Adds CSS class names to the underlying HTML element.</p>
0292      * @param e The current component.
0293      * @param classes An array of CSS class names to add.
0294      * @since 1.0
0295      */
0296     public static final void addClasses(Component e, ArrayLike<String> classes) {
0297         JsHTMLElement he = getHTMLElement(e);
0298         if (Js.be(he)) {
0299             Elements.addClasses(he, classes);
0300             calculate(e);
0301         } else {
0302             for (int i = 0, len = ArrayLikes.length(classes); i < len; i++) {
0303                 ArrayLikes.push(ini(e).var(CLASS), classes.get(i));
0304             }
0305         }
0306     }
0307 
0308     /**
0309      * <p>Removes CSS class names from the underlying HTML element.</p>
0310      * @param e The current component.
0311      * @param classes An array of CSS class names to remove.
0312      * @since 1.0
0313      */
0314     public static final void removeClasses(Component e, ArrayLike<String> classes) {
0315         JsHTMLElement he = getHTMLElement(e);
0316         if (Js.be(he)) {
0317             Elements.removeClasses(he, classes);
0318             calculate(e);
0319         } else {
0320             for (int i = 0, len = ArrayLikes.length(classes); i < len; i++) {
0321                 ArrayLikes.remove(ini(e).var(CLASS), classes.get(i));
0322             }
0323         }
0324     }
0325 
0326     /**
0327      * <p>Applies a style object to the underlying HTML element.</p>
0328      * @param e The current component.
0329      * @param p The style object to apply.
0330      * @param animated Whether fires an {@link Animation} command from the component.
0331      * @since 1.0
0332      */
0333     public static final void applyStyle(Component e, ObjectLike p, boolean animated) {
0334         ObjectLike style = style(e);
0335         if (Js.be(style)) {
0336             if (animated) {
0337                 e.exec(new Animation(p));
0338             }
0339             Js.apply(style, p);
0340             e.fire(new Style(Js.apply(new Initializer().var(), p)));
0341             if (Styles.relevant(p, Styles.BMP)) {
0342                 calculate(e);
0343             }
0344         } else {
0345             Js.apply(ini(e).var(CACHE), p);
0346         }
0347     }
0348 
0349     /**
0350      * <p>Applies a style object to the underlying HTML element.</p>
0351      * @param e The current component.
0352      * @param p The style object to apply.
0353      * @since 1.0
0354      */
0355     public static final void applyStyle(Component e, ObjectLike p) {
0356         applyStyle(e, p, false);
0357     }
0358 
0359     /**
0360      * <p>Gets or creates a {@link Pseudo} widget associated with this component.</p>
0361      * @param id Identifies where to store the {@link Pseudo} widget.
0362      * @param bevt A browser event type.
0363      * @return The required {@link Pseudo} widget.
0364      * @since 1.0
0365      */
0366     public final Pseudo pseudo(Id<Pseudo> id, String bevt) {
0367         ObjectLike ini = ini(this);
0368         Pseudo ps = ini.var(id);
0369         if (Js.not(ps)) {
0370             ps = new Pseudo(this, bevt);
0371             ini.var(id, ps);
0372         }
0373         return ps;
0374     }
0375 
0376     /**
0377      * <p>Handles a browser event from the underly HTML element.</p>
0378      * @param bevt A browser event.
0379      * @param lstnr A browser event listener.
0380      * @return The newly created {@link Handle}.
0381      * @since 1.0
0382      */
0383     public final Handle handle(String bevt, JsFunction<?> lstnr) {
0384         Handle h = new Handle(
0385                 Component.getHTMLElement(this),
0386                 bevt, lstnr, false
0387         );
0388         h.attach();
0389         return h;
0390     }
0391 
0392     private Component(JsElement e) {
0393         super(new Initializer().set(
0394                 ELEMENT, e
0395         ).set(
0396                 CACHE, new Initializer().var()
0397         ).var());
0398     }
0399 
0400     /**
0401      * <p>Constructs a component wrapping an HTML element if rendered.</p>
0402      * @param html The HTML text to create underlying HTML element when rendered.
0403      * @since 1.0
0404      */
0405     public Component(String html) {
0406         super(new Initializer().set(
0407                 HTML, html
0408         ).set(
0409                 ATTRIBUTE, new Initializer().var()
0410         ).set(
0411                 CACHE, new Initializer().var()
0412         ).set(
0413                 CLASS, new Vars<String>().var()
0414         ).set(
0415                 CHILDREN, new Vars<Component>().var()
0416         ).var());
0417         addListener(Render.class, this);
0418     }
0419 
0420     /**
0421      * <p>Returns the string representation of the component.</p>
0422      * @return The string representation of the component.
0423      * @since 1.0
0424      */
0425     @Override
0426     public final String toString() {
0427         return ArrayLikes.join(
0428                 new Vars<String>()
0429                     .add(getClass().getName())
0430                     .add(getHTMLElement(this).toString())
0431                     .var(),
0432                 ":"
0433         );
0434     }
0435 
0436     /**
0437      * <p>Gets the rendered element or adds rendering listener to the current component.</p>
0438      * <p>This method simply returns the underlying HTML element if the current 
0439      * component has been rendered.</p>
0440      * @param e The current component.
0441      * @param on A rendering listener.
0442      * @return The underlying HTML element or <tt>null</tt> if <tt>e</tt> is not rendered.
0443      * @since 1.0
0444      */
0445     public static final JsHTMLElement getRendered(Component e, OnRender on) {
0446         JsHTMLElement he = getHTMLElement(e);
0447         if (Js.not(he)) {
0448             e.addListener(Render.class, on);
0449         }
0450         return he;
0451     }
0452 
0453     /**
0454      * <p>Performs an action on the dispatched event.</p>
0455      * <p>This method renders all the child components it has by calling the {@link #appendChild(Component, Component)} 
0456      * method, which fires {@link Render} events from the child components and forces them 
0457      * to render their children accordingly.</p>
0458      * @param evt The event dispatched to this listener.
0459      * @since 1.0
0460      */
0461     public void onEvent(Render evt) {
0462         ObjectLike ini = ini(this);
0463         ArrayLike<Component> children = ini.var(CHILDREN);
0464         for (int i = 0, len = ArrayLikes.length(children); i < len; i++) {
0465             appendChild(this, children.get(i));
0466         }
0467         ObjectLikes.delete(ini, CHILDREN);
0468     }
0469 
0470     private final static String REF = ":xel";
0471     private static int NXT = 0;
0472 
0473     private static final void syncAttribute(Component e) {
0474         JsHTMLElement he = getHTMLElement(e);
0475         if (Js.be(he)) {
0476             Js.apply(he, ini(e).var(ATTRIBUTE));
0477             ini(e).var(ATTRIBUTE, new Initializer().var());
0478         }
0479     }
0480 
0481     private static final void syncStyle(Component e) {
0482         if (Js.be(style(e))) {
0483             Js.apply(style(e), ini(e).var(CACHE));
0484             ini(e).var(CACHE, new Initializer().var());
0485         }
0486     }
0487 
0488     /**
0489      * <p>Appends the child component to the parent component.</p>
0490      * <p>This method simply adds the child component to the children list of the parent 
0491      * component if the parent component has not been rendered. Otherwise, if the child 
0492      * component has been rendered, the method simply appends the child element to the 
0493      * parent element; if not, the method creates a new HTML element by inserting the 
0494      * {@link #HTML} value to the parent element, updates cached styles of the child 
0495      * component and forces all of its children to render by firing a {@link Render} event 
0496      * from the newly rendered component.</p>
0497      * @param p The parent component.
0498      * @param c The child component.
0499      * @return <tt>true</tt> if the operation is successful; <tt>false</tt>, otherwise.
0500      * @since 1.0
0501      */
0502     public static final boolean appendChild(Component p, Component c) {
0503         if (Js.eq(c, body())) {
0504             return false;
0505         }
0506         JsHTMLElement pe = getHTMLElement(p);
0507         if (Js.not(pe)) {
0508             ArrayLikes.push(ini(p).var(CHILDREN), c);
0509             ini(c).var(PARENT, p);
0510         } else {
0511             JsHTMLElement ce = getHTMLElement(c);
0512             if (Js.be(ce)) {
0513                 if (Js.be(c.getParent())) {
0514                     return false;
0515                 }
0516                 pe.appendChild(ce);
0517                 ini(c).var(PARENT, p);
0518             } else {
0519                 accept(c, Markups.insertBeforeEnd(
0520                         pe,
0521                         getHTML(c)
0522                 ));
0523                 ini(c).var(PARENT, p);
0524                 c.exec(new Render(p));
0525             }
0526         }
0527         return true;
0528     }
0529 
0530     private static final void accept(Component c, JsHTMLElement e) {
0531         e.var(REF, c);
0532         ini(c).var(ELEMENT, e);
0533         ini(c).var(STYLE, Elements.style(e));
0534         genId(e);
0535         syncAttribute(c);
0536         syncStyle(c);
0537         Elements.addClasses(e, ini(c).var(CLASS));
0538         calculate(c);
0539     }
0540 
0541     /**
0542      * <p>Appends the child component to the parent component.</p>
0543      * <p>This method simply adds the child component to the children list of the parent 
0544      * component if the parent component has not been rendered. Otherwise, if the child 
0545      * component has been rendered, the method simply appends the child element to the 
0546      * parent element; if not, the method creates a new HTML element by inserting the 
0547      * {@link #HTML} value to the parent element, updates cached styles of the child 
0548      * component and forces all of its children to render by firing a {@link Render} event 
0549      * from the newly rendered component.</p>
0550      * @param p The parent component.
0551      * @param c The child component.
0552      * @param r The reference component.
0553      * @return <tt>true</tt> if the operation is successful; <tt>false</tt>, otherwise.
0554      * @since 1.0
0555      */
0556     public static final boolean insertBefore(Component p, Component c, Component r) {
0557         if (Js.eq(c, body())) {
0558             return false;
0559         }
0560         JsHTMLElement pe = getHTMLElement(p);
0561         if (Js.not(pe)) {
0562             ArrayLikes.insert(ini(p).var(CHILDREN), c, r);
0563             ini(c).var(PARENT, p);
0564         } else {
0565             JsHTMLElement re = getHTMLElement(r);
0566             if (Js.not(re)) {
0567                 ini(c).var(PARENT, p);
0568                 return appendChild(p, c);
0569             }
0570             JsHTMLElement ce = getHTMLElement(c);
0571             if (Js.be(ce)) {
0572                 if (Js.be(c.getParent())) {
0573                     return false;
0574                 }
0575                 pe.insertBefore(ce, re);
0576                 ini(c).var(PARENT, p);
0577             } else {
0578                 accept(c, Markups.insertBeforeBegin(
0579                         re,
0580                         getHTML(c)
0581                 ));
0582                 ini(c).var(PARENT, p);
0583                 c.exec(new Render(p));
0584             }
0585         }
0586         return true;
0587     }
0588 
0589     /**
0590      * <p>Gets the first child component of the specified one.</p>
0591      * <p>The method returns the component wrapping the element that is the first child 
0592      * of the underlying element of the argument component.</p>
0593      * @param p A parent component.
0594      * @return The first child component of the specified one.
0595      * @since 1.0
0596      */
0597     public static final Component firstChild(Component p) {
0598         JsNode e = getElement(p);
0599         return Js.be(e) ? get(JsElement.firstChild.with(e)) :
0600             ini(p).var(CHILDREN).get(0);
0601     }
0602 
0603     /**
0604      * <p>Gets the component that wraps the HTML <tt>&lt;body&gt;</tt> element of the 
0605      * current HTML document.</p>
0606      * @return The newly created component.
0607      * @since 1.0
0608      */
0609     public static final Component body() {
0610         return get(Document.body.var());
0611     }
0612 
0613     /**
0614      * <p>Gets the component that wraps an HTML <tt>&lt;div&gt;</tt> element.</p>
0615      * @return The newly created component.
0616      * @since 1.0
0617      */
0618     public static final Component div() {
0619         return new Component(Markups.DIV);
0620     }
0621 
0622     /**
0623      * <p>Gets the component that wraps an HTML <tt>&lt;span&gt;</tt> element.</p>
0624      * @return The newly created component.
0625      * @since 1.0
0626      */
0627     public static final Component span() {
0628         return new Component(Markups.SPAN);
0629     }
0630 
0631     /**
0632      * <p>Gets a component wrapping an HTML element that has the specified id.</p>
0633      * <p>This method simply calls the {@link Nodes#getElementById(String)} method to find 
0634      * the element and then calls the {@link #get(JsElement)} method to return a component 
0635      * for it.</p>
0636      * @param id The id of an HTML element.
0637      * @return The component wrapping an HTML element that has the specified id.
0638      * @since 1.0
0639      */
0640     public static final Component getById(String id) {
0641         return get(Nodes.getElementById(id));
0642     }
0643 
0644     private static final synchronized String genId(JsElement e) {
0645         String id = Elements.id(e);
0646         if (Js.not(id)) {
0647             id = Js.add(Component.class.getName(), NXT++);
0648             Elements.id(e, id);
0649         }
0650         return id;
0651     }
0652 
0653     /**
0654      * <p>Gets the component for an element.</p>
0655      * <p>The method simply returns the component that wraps the specified element if 
0656      * there is one. Otherwise, it creates one to wrap the element and returns the newly 
0657      * created component.</p>
0658      * @param e An underlying element.
0659      * @return A component that wraps the specified element.
0660      * @since 1.0
0661      */
0662     public static final Component get(JsElement e) {
0663         if (Js.not(e)) {
0664             return null;
0665         }
0666         Object o = e.var(REF);
0667         if (Js.be(o)) {
0668             return (Component)o;
0669         }
0670         Component we = new Component(e);
0671         e.var(REF, we);
0672         genId(e);
0673         if (Elements.isHTMLElement(e)) {
0674             ini(we).var(STYLE, Elements.style(new JsHTMLElement(e)));
0675             calculate(we);
0676         }
0677         return we;
0678     }
0679 
0680     /**
0681      * <p>Gets the parent component of this one.</p>
0682      * <p>The method returns the component wrapping the element that is the parent of the 
0683      * underlying element of the argument component.</p>
0684      * @return The parent component of the current one.
0685      * @since 1.0
0686      */
0687     @Override
0688     public final Component getParent() {
0689         Component p = ini(this).var(PARENT);
0690         if (Js.not(p)) {
0691             p = getParent(getElement(this));
0692             ini(this).var(PARENT, p);
0693         }
0694         return p;
0695     }
0696 
0697     private static final Component getParent(JsElement e) {
0698         if (Js.not(e)) return null;
0699         JsElement p = Elements.parentNode(e);
0700         if (Browser.isIE && Js.be(p) && Elements.isHTMLElement(e)) {
0701             String pid = JsHTMLElement.id.with(e);
0702             if (Js.not(getById(pid))) {
0703                 p = null;
0704             }
0705         }
0706         return get(p);
0707     }
0708 
0709     /**
0710      * <p>Removes a child component from its parent.</p>
0711      * <p>The method removes the underlying element of the argument component from its 
0712      * parent node by calling the {@link Nodes#detach(JsNode)} method.</p>
0713      * @param c A child component.
0714      * @since 1.0
0715      */
0716     public static final void detach(Component c) {
0717         JsElement ce = getElement(c);
0718         if (Js.be(ce)) {
0719             Nodes.detach(ce);
0720         }
0721         ObjectLikes.delete(ini(c), PARENT);
0722     }
0723 
0724     /**
0725      * <p>Gets the position style of the underlying HTML element of a component.</p>
0726      * @param e The current component.
0727      * @return The position style of the underlying HTML element of the component.
0728      * @since 1.0
0729      * @see #isAbsolute(Component)
0730      * @see #isRelative(Component)
0731      */
0732     public static final String position(Component e) {
0733         return Styles.position(Elements.currentStyle(getHTMLElement(e)));
0734     }
0735 
0736     /**
0737      * <p>Is the underlying HTML element of a component being absolutely positioned?</p>
0738      * @param e The current component.
0739      * @return <tt>true</tt> if the <tt>position</tt> style of the underlying HTML element 
0740      * is "absolute"; <tt>false</tt>, otherwise.
0741      * @since 1.0
0742      * @see #position(Component)
0743      * @see #isRelative(Component)
0744      */
0745     public static final boolean isAbsolute(Component e) {
0746         return Js.eq(position(e), "absolute");
0747     }
0748 
0749     /**
0750      * <p>Is the underlying HTML element of a component being relatively positioned?</p>
0751      * @param e The current component.
0752      * @return <tt>true</tt> if the <tt>position</tt> style of the underlying HTML element 
0753      * is "relative"; <tt>false</tt>, otherwise.
0754      * @since 1.0
0755      * @see #position(Component)
0756      * @see #isAbsolute(Component)
0757      */
0758     public static final boolean isRelative(Component e) {
0759         return Js.eq(position(e), "relative");
0760     }
0761 
0762     /**
0763      * <p>Makes the underlying element to absolute positioning style.</p>
0764      * @param e The current component.
0765      * @since 1.0
0766      * @see #relative(Component)
0767      */
0768     public static final void absolute(Component e) {
0769         ObjectLike p = new Initializer().var();
0770         Styles.position(p, "absolute");
0771         applyStyle(e, p);
0772     }
0773 
0774     /**
0775      * <p>Makes the underlying element to relative positioning style.</p>
0776      * @param e The current component.
0777      * @since 1.0
0778      * @see #absolute(Component)
0779      */
0780     public static final void relative(Component e) {
0781         ObjectLike p = new Initializer().var();
0782         Styles.position(p, "relative");
0783         applyStyle(e, p);
0784     }
0785 
0786     /**
0787      * <p>Sets visibility of the underlying HTML element.</p>
0788      * @param e The current component.
0789      * @param s The value of visibility.
0790      * @since 1.0
0791      */
0792     public static final void visibility(Component e, String s) {
0793         ObjectLike p = new Initializer().var();
0794         Styles.visibility(p, s);
0795         applyStyle(e, p);
0796     }
0797 
0798     /**
0799      * <p>Gets the transparency style value of the underlying HTML element of a component.</p>
0800      * @param e The current component.
0801      * @return The transparency value of the underlying HTML element of the component.
0802      * @since 1.0
0803      * @see #transparency(Component, double)
0804      */
0805     public static final double transparency(Component e) {
0806         return Styles.transparency(Elements.currentStyle(getHTMLElement(e)));
0807     }
0808     /**
0809      * <p>Sets the transparency style value of the underlying HTML element of a component.</p>
0810      * @param e The current component.
0811      * @param v The new transparency value.
0812      * @since 1.0
0813      * @see #transparency(Component)
0814      */
0815     public static final void transparency(Component e, double v) {
0816         Elements.transparency(getHTMLElement(e), v);
0817     }
0818 
0819     /**
0820      * <p>Computes and returns the X coordinate of the upper-left corner of the underlying 
0821      * element relative to the whole page.</p>
0822      * <p>This method has eliminated browser dependencies and is useful for components of 
0823      * absolutely positioned element move or resize according to mouse events that can only 
0824      * provide position data of page context.</p>
0825      * @return The X coordinate of the upper-left corner of the element wrapped by this 
0826      * component.
0827      * @see #pageTop()
0828      */
0829     public final double pageLeft() {
0830         double x = offsetLeft(this);
0831         JsHTMLElement p = Elements.offsetParent(getHTMLElement(this));
0832         if (Js.be(p) && Js.neq(p, Win.document.var())) {
0833             Component pe = get(p);
0834             x = x + border(pe, BORDERLEFT) + pe.pageLeft();
0835         }
0836         return x;
0837     }
0838 
0839     /**
0840      * <p>Computes and returns the Y coordinate of the upper-left corner of the underlying 
0841      * element relative to the whole page.</p>
0842      * <p>This method has eliminated browser dependencies and is useful for components of 
0843      * absolutely positioned element move or resize according to mouse events that can only 
0844      * provide position data of page context.</p>
0845      * @return The Y coordinate of the upper-left corner of the element wrapped by this 
0846      * component.
0847      * @see #pageTop()
0848      */
0849     public final double pageTop() {
0850         double y = offsetTop(this);
0851         JsHTMLElement p = Elements.offsetParent(getHTMLElement(this));
0852         if (Js.be(p) && Js.neq(p, Win.document.var())) {
0853             Component pe = get(p);
0854             y = y + border(pe, BORDERTOP) + pe.pageTop();
0855         }
0856         return y;
0857     }
0858 
0859     /**
0860      * <p>A constant used with the {@link #border(Component, int)} method to return 
0861      * the left border width of a component.</p>
0862      * @since 1.0
0863      */
0864     public final static int BORDERLEFT    = 0;
0865     /**
0866      * <p>A constant used with the {@link #border(Component, int)} method to return 
0867      * the right border width of a component.</p>
0868      * @since 1.0
0869      */
0870     public final static int BORDERRIGHT   = 1;
0871     /**
0872      * <p>A constant used with the {@link #border(Component, int)} method to return 
0873      * the top border width of a component.</p>
0874      * @since 1.0
0875      */
0876     public final static int BORDERTOP     = 2;
0877     /**
0878      * <p>A constant used with the {@link #border(Component, int)} method to return 
0879      * the bottom border width of a component.</p>
0880      * @since 1.0
0881      */
0882     public final static int BORDERBOTTOM  = 3;
0883     /**
0884      * <p>A constant used with the {@link #margin(Component, int)} method to return 
0885      * the left margin of a component.</p>
0886      * @since 1.0
0887      */
0888     public final static int MARGINLEFT    = 0;
0889     /**
0890      * <p>A constant used with the {@link #margin(Component, int)} method to return 
0891      * the right margin of a component.</p>
0892      * @since 1.0
0893      */
0894     public final static int MARGINRIGHT   = 1;
0895     /**
0896      * <p>A constant used with the {@link #margin(Component, int)} method to return 
0897      * the top margin of a component.</p>
0898      * @since 1.0
0899      */
0900     public final static int MARGINTOP     = 2;
0901     /**
0902      * <p>A constant used with the {@link #margin(Component, int)} method to return 
0903      * the bottom margin of a component.</p>
0904      * @since 1.0
0905      */
0906     public final static int MARGINBOTTOM  = 3;
0907     /**
0908      * <p>A constant used with the {@link #padding(Component, int)} method to return 
0909      * the left padding of a component.</p>
0910      * @since 1.0
0911      */
0912     public final static int PADDINGLEFT   = 0;
0913     /**
0914      * <p>A constant used with the {@link #padding(Component, int)} method to return 
0915      * the right padding of a component.</p>
0916      * @since 1.0
0917      */
0918     public final static int PADDINGRIGHT  = 1;
0919     /**
0920      * <p>A constant used with the {@link #padding(Component, int)} method to return 
0921      * the top padding of a component.</p>
0922      * @since 1.0
0923      */
0924     public final static int PADDINGTOP    = 2;
0925     /**
0926      * <p>A constant used with the {@link #padding(Component, int)} method to return 
0927      * the bottom padding of a component.</p>
0928      * @since 1.0
0929      */
0930     public final static int PADDINGBOTTOM = 3;
0931 
0932     private final static Id<ArrayLike<Double>> BORDERS  = new Id<ArrayLike<Double>>();
0933     private final static Id<ArrayLike<Double>> MARGINS  = new Id<ArrayLike<Double>>();
0934     private final static Id<ArrayLike<Double>> PADDINGS = new Id<ArrayLike<Double>>();
0935     private final static ArrayLike<String> DIRS = new Vars<String>()
0936         .add("Left").add("Right").add("Top").add("Bottom").var();
0937 
0938     /**
0939      * <p>Forces a component to recalculate its cached data of borders, margins, paddings, 
0940      * etc.</p>
0941      * @param e A component is to update its cache.
0942      * @since 1.0
0943      */
0944     public static final void calculate(Component e) {
0945         ArrayLike<Double> borders  = new Vars<Double>().var();
0946         ArrayLike<Double> margins  = new Vars<Double>().var();
0947         ArrayLike<Double> paddings = new Vars<Double>().var();
0948         ObjectLike ini = ini(e);
0949         ObjectLike cs = Elements.currentStyle(getHTMLElement(e));
0950         if (Js.be(cs)) {
0951             for (int i = 0; i < 4; i++) {
0952                 String dir = DIRS.get(i);
0953                 borders .set(i, Styles.px((String)cs.var(Js.add("border" , Js.add(dir, "Width")))));
0954                 margins .set(i, Styles.px((String)cs.var(Js.add("margin" , dir))));
0955                 paddings.set(i, Styles.px((String)cs.var(Js.add("padding", dir))));
0956             }
0957             if (margins.get(MARGINTOP) > 0 && Browser.userAgent("Firefox/3.6")) {
0958                 margins.set(MARGINTOP, margins.get(MARGINTOP) - 1);
0959             }
0960         }
0961         ini.var(BORDERS , borders );
0962         ini.var(MARGINS , margins );
0963         ini.var(PADDINGS, paddings);
0964     }
0965 
0966     /**
0967      * <p>Returns the widths of the left, right, top and bottom borders of a component 
0968      * as an array.</p>
0969      * <p>This method only returns the cached data. To update the cache of a component, 
0970      * use the {@link #calculate(Component)} method.</p>
0971      * @return A list of border widths of the specified component.
0972      * @since 1.0
0973      */
0974     public static final ArrayLike<Double> borders(Component e) {
0975         return ArrayLikes.slice(ini(e).var(BORDERS), 0);
0976     }
0977     /**
0978      * <p>Returns the left, right, top and bottom margins of a component as an array.</p>
0979      * <p>This method only returns the cached data. To update the cache of a component, 
0980      * use the {@link #calculate(Component)} method.</p>
0981      * @return A list of margins of the specified component.
0982      * @since 1.0
0983      */
0984     public static final ArrayLike<Double> margins(Component e) {
0985         return ArrayLikes.slice(ini(e).var(MARGINS), 0);
0986     }
0987     /**
0988      * <p>Returns the left, right, top and bottom paddings of a component as an array.</p>
0989      * <p>This method only returns the cached data. To update the cache of a component, 
0990      * use the {@link #calculate(Component)} method.</p>
0991      * @return A list of paddings of the specified component.
0992      * @since 1.0
0993      */
0994     public static final ArrayLike<Double> paddings(Component e) {
0995         return ArrayLikes.slice(ini(e).var(PADDINGS), 0);
0996     }
0997 
0998     /**
0999      * <p>Returns the width of a border of the specified component.</p>
1000      * <p>This method only returns the cached data. To update the cache of a component, 
1001      * use the {@link #calculate(Component)} method.</p>
1002      * @param i The index of the border to get width.
1003      * @return The width of the <tt>i</tt>th border of the specified component.
1004      * @since 1.0
1005      * @see #BORDERLEFT
1006      * @see #BORDERRIGHT
1007      * @see #BORDERTOP
1008      * @see #BORDERBOTTOM
1009      */
1010     public static final Double border(Component e, int i) {
1011         return ini(e).var(BORDERS).get(i);
1012     }
1013     /**
1014      * <p>Returns a margin of the specified component.</p>
1015      * <p>This method only returns the cached data. To update the cache of a component, 
1016      * use the {@link #calculate(Component)} method.</p>
1017      * @param i The index of the margin.
1018      * @return The <tt>i</tt>th margin of the specified component.
1019      * @since 1.0
1020      * @see #MARGINLEFT
1021      * @see #MARGINRIGHT
1022      * @see #MARGINTOP
1023      * @see #MARGINBOTTOM
1024      */
1025     public static final Double margin(Component e, int i) {
1026         return ini(e).var(MARGINS).get(i);
1027     }
1028     /**
1029      * <p>Returns a padding of the specified component.</p>
1030      * <p>This method only returns the cached data. To update the cache of a component, 
1031      * use the {@link #calculate(Component)} method.</p>
1032      * @param i The index of the padding.
1033      * @return The <tt>i</tt>th padding of the specified component.
1034      * @since 1.0
1035      * @see #PADDINGLEFT
1036      * @see #PADDINGRIGHT
1037      * @see #PADDINGTOP
1038      * @see #PADDINGBOTTOM
1039      */
1040     public static final Double padding(Component e, int i) {
1041         return ini(e).var(PADDINGS).get(i);
1042     }
1043 
1044     /**
1045      * <p>Gets the X coordinate of the upper-left corner of the CSS border of the 
1046      * underlying HTML element relative to its parent container element.</p>
1047      * <p>This method has eliminated browser dependencies, simply calling the 
1048      * {@link Elements#offsetLeft(JsHTMLElement)} method to get the 
1049      * {@link JsHTMLElement#offsetLeft} property of the underlying HTML element.</p>
1050      * @param e The current component.
1051      * @return The X coordinate of the upper-left corner of the CSS border of the 
1052      * underlying HTML element relative to its parent container element.
1053      * @since 1.0
1054      * @see #left(Component)
1055      * @see #offsetTop(Component)
1056      * @see #offsetWidth(Component)
1057      * @see #offsetHeight(Component)
1058      */
1059     public static final double offsetLeft(Component e) {
1060         double x = Elements.offsetLeft(Component.getHTMLElement(e)).doubleValue();
1061         if (Browser.isOpera) {
1062             Component p = e.getParent();
1063             if (Js.be(p)) {
1064                 x = x - border(p, Component.BORDERLEFT);
1065             }
1066         }
1067         return x;
1068     }
1069     /**
1070      * <p>Gets the Y coordinate of the upper-left corner of the CSS border of the 
1071      * underlying HTML element relative to its parent container element.</p>
1072      * <p>This method has eliminated browser dependencies, simply calling the 
1073      * {@link Elements#offsetTop(JsHTMLElement)} method to get the 
1074      * {@link JsHTMLElement#offsetTop} property of the underlying HTML element.</p>
1075      * @param e The current component.
1076      * @return The Y coordinate of the upper-left corner of the CSS border of the 
1077      * underlying HTML element relative to its parent container element.
1078      * @since 1.0
1079      * @see #top(Component)
1080      * @see #offsetLeft(Component)
1081      * @see #offsetWidth(Component)
1082      * @see #offsetHeight(Component)
1083      */
1084     public static final double offsetTop(Component e) {
1085         double y = Elements.offsetTop(Component.getHTMLElement(e)).doubleValue();
1086         if (Browser.isOpera) {
1087             Component p = e.getParent();
1088             if (Js.be(p)) {
1089                 y = y - border(p, Component.BORDERTOP);
1090             }
1091         }
1092         return y;
1093     }
1094     /**
1095      * <p>Gets the X coordinate of the upper-left corner of the underlying HTML element 
1096      * relative to its parent container element.</p>
1097      * @param e The current component.
1098      * @return The X coordinate of the upper-left corner of the underlying HTML element 
1099      * relative to its parent container element. It is the difference between the values 
1100      * returned from calling the {@link #offsetLeft(Component)} and {@link #margin(Component, int)} 
1101      * methods.</p> 
1102      * @since 1.0
1103      * @see #top(Component)
1104      * @see #offsetLeft(Component)
1105      */
1106     public static final double left(Component e) {
1107         return left(e, offsetLeft(e));
1108     }
1109     /**
1110      * <p>Gets the Y coordinate of the upper-left corner of the underlying HTML element 
1111      * relative to its parent container element.</p>
1112      * @param e The current component.
1113      * @return The T coordinate of the upper-left corner of the underlying HTML element 
1114      * relative to its parent container element. It is the difference between the values 
1115      * returned from calling the {@link #offsetTop(Component)} and {@link #margin(Component, int)} 
1116      * methods.</p> 
1117      * @since 1.0
1118      * @see #left(Component)
1119      * @see #offsetLeft(Component)
1120      */
1121     public static final double top(Component e) {
1122         return top(e, offsetTop(e));
1123     }
1124     /**
1125      * <p>Corrects a position value of X dimension in consideration of the left margin of 
1126      * the underlying HTML element.</p>
1127      * @param e The current component.
1128      * @param x A position value.
1129      * @return The difference between the specified position value and the left margin of 
1130      * the underlying HTML element.</p> 
1131      * @since 1.0
1132      * @see #top(Component, double)
1133      */
1134     public static final double left(Component e, double x) {
1135         return x - margin(e, Component.MARGINLEFT);
1136     }
1137     /**
1138      * <p>Corrects a position value of Y dimension in consideration of the top margin of 
1139      * the underlying HTML element.</p>
1140      * @param e The current component.
1141      * @param y A position value.
1142      * @return The difference between the specified position value and the top margin of 
1143      * the underlying HTML element.</p> 
1144      * @since 1.0
1145      * @see #left(Component, double)
1146      */
1147     public static final double top(Component e, double y) {
1148         return y - margin(e, Component.MARGINTOP);
1149     }
1150     /**
1151      * <p>Gets the width, in pixels, of the underlying element and all its content, 
1152      * including the element's CSS padding and border, but not its margin.</p>
1153      * <p>This method has eliminated browser dependencies, simply calling the 
1154      * {@link Elements#offsetWidth(JsHTMLElement)} method to get the 
1155      * {@link JsHTMLElement#offsetWidth} property of the underlying HTML element.</p>
1156      * @param e The current component.
1157      * @return The width, in pixels, of the underlying element and all its content, 
1158      * including the element's CSS padding and border, but not its margin.
1159      * @since 1.0
1160      * @see #offsetLeft(Component)
1161      * @see #offsetTop(Component)
1162      * @see #offsetHeight(Component)
1163      */
1164     public static final int offsetWidth(Component e) {
1165         return Elements.offsetWidth(Component.getHTMLElement(e)).intValue();
1166     }
1167     /**
1168      * <p>Gets the height, in pixels, of the underlying element and all its content, 
1169      * including the element's CSS padding and border, but not its margin.</p>
1170      * <p>This method has eliminated browser dependencies, simply calling the 
1171      * {@link Elements#offsetHeight(JsHTMLElement)} method to get the 
1172      * {@link JsHTMLElement#offsetHeight} property of the underlying HTML element.</p>
1173      * @param e The current component.
1174      * @return The height, in pixels, of the underlying element and all its content, 
1175      * including the element's CSS padding and border, but not its margin.
1176      * @since 1.0
1177      * @see #offsetLeft(Component)
1178      * @see #offsetTop(Component)
1179      * @see #offsetWidth(Component)
1180      */
1181     public static final int offsetHeight(Component e) {
1182         return Elements.offsetHeight(Component.getHTMLElement(e)).intValue();
1183     }
1184     /**
1185      * <p>Computes the content width of the underlying HTML element.</p>
1186      * <p>In IE, use {@link #offsetWidth(Component)} for content width.</p>
1187      * @param e The current component.
1188      * @return {@link #offsetWidth(Component)} minus {@link #contentLeft(Component)} and 
1189      * {@link #contentRight(Component)}.
1190      * @since 1.0
1191      * @see #contentHeight(Component)
1192      * @see #offsetWidth(Component)
1193      */
1194     public static final double contentWidth(Component e) {
1195         return contentWidth(e, offsetWidth(e));
1196     }
1197     /**
1198      * <p>Computes the content height of the underlying HTML element.</p>
1199      * <p>In IE, use {@link #offsetHeight(Component)} for content height.</p>
1200      * @param e The current component.
1201      * @return {@link #offsetHeight(Component)} minus {@link #contentTop(Component)} and 
1202      * {@link #contentBottom(Component)}.
1203      * @since 1.0
1204      * @see #contentWidth(Component)
1205      * @see #offsetHeight(Component)
1206      */
1207     public static final double contentHeight(Component e) {
1208         return contentHeight(e, offsetHeight(e));
1209     }
1210     /**
1211      * <p>Corrects a length value of X dimension in consideration of the borders and 
1212      * paddings of the underlying HTML element.</p>
1213      * @param e The current component.
1214      * @param w A length value.
1215      * @return The specified length value minus {@link #contentLeft(Component)} and 
1216      * {@link #contentRight(Component)}.</p> 
1217      * @since 1.0
1218      * @see #contentHeight(Component, double)
1219      */
1220     public static final double contentWidth(Component e, double w) {
1221         return w - contentLeft(e) - contentRight(e);
1222     }
1223     /**
1224      * <p>Corrects a length value of Y dimension in consideration of the borders and 
1225      * paddings of the underlying HTML element.</p>
1226      * @param e The current component.
1227      * @param h A length value.
1228      * @return The specified length value minus {@link #contentTop(Component)} and 
1229      * {@link #contentBottom(Component)}.</p> 
1230      * @since 1.0
1231      * @see #contentWidth(Component, double)
1232      */
1233     public static final double contentHeight(Component e, double h) {
1234         return h - contentTop(e) - contentBottom(e);
1235     }
1236     /**
1237      * <p>Gets the absolute X coordinate of the upper-left corner of the content area of 
1238      * the underlying HTML element of a component.</p>
1239      * @param e The current component.
1240      * @return The sum of the width of left border and the left padding of the underlying 
1241      * element.</p> 
1242      * @since 1.0
1243      * @see #contentRight(Component)
1244      * @see #contentTop(Component)
1245      * @see #contentBottom(Component)
1246      */
1247     public static final double contentLeft(Component e) {
1248         return border(e, Component.BORDERLEFT) + padding(e, Component.PADDINGLEFT);
1249     }
1250     /**
1251      * <p>Gets the distance from the right boundary of the content area of the underlying 
1252      * HTML element of a component to its CSS left border.</p>
1253      * @param e The current component.
1254      * @return The sum of the width of right border and the right padding of the underlying 
1255      * element.</p> 
1256      * @since 1.0
1257      * @see #contentLeft(Component)
1258      * @see #contentTop(Component)
1259      * @see #contentBottom(Component)
1260      */
1261     public static final double contentRight(Component e) {
1262         return border(e, Component.BORDERRIGHT) + padding(e, Component.PADDINGRIGHT);
1263     }
1264     /**
1265      * <p>Gets the absolute Y coordinate of the upper-left corner of the content area of 
1266      * the underlying HTML element of a component.</p>
1267      * @param e The current component.
1268      * @return The sum of the width of top border and the top padding of the underlying 
1269      * element.</p> 
1270      * @since 1.0
1271      * @see #contentLeft(Component)
1272      * @see #contentRight(Component)
1273      * @see #contentBottom(Component)
1274      */
1275     public static final double contentTop(Component e) {
1276         return border(e, Component.BORDERTOP) + padding(e, Component.PADDINGTOP);
1277     }
1278     /**
1279      * <p>Gets the distance from the bottom boundary of the content area of the underlying 
1280      * HTML element of a component to its CSS bottom border.</p>
1281      * @param e The current component.
1282      * @return The sum of the width of bottom border and the bottom padding of the underlying 
1283      * element.</p> 
1284      * @since 1.0
1285      * @see #contentLeft(Component)
1286      * @see #contentRight(Component)
1287      * @see #contentTop(Component)
1288      */
1289     public static final double contentBottom(Component e) {
1290         return border(e, Component.BORDERBOTTOM) + padding(e, Component.PADDINGBOTTOM);
1291     }
1292 
1293     /**
1294      * <p>Creates a new element in the current document with the specified tag name and 
1295      * returns a component that wraps the element.</p>
1296      * <p>This method simply calls the {@link Elements#createElement(String)} method 
1297      * to create an element and then calls the {@link #get(JsElement)} method to return a 
1298      * component for it.</p>
1299      * @param tagName The tag name of the underlying opaque element node being created. 
1300      * Since HTML tags are case-insensitive, you may use any capitalization for HTML tag 
1301      * names. XML tag names are case-sensitive. 
1302      * @return The component that wraps the newly created opaque element.
1303      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1304      * the {@link JsDOMException#code} property of the value {@link JsDOMException#INVALID_CHARACTER_ERR} 
1305      * if <tt>tagName</tt> contains an illegal character. See {@link js.Js#err(Object)} 
1306      * for JS Simulation.
1307      * @since 1.0
1308      * @see #createElementNS(String, String)
1309      * @see js.user.JsDocument#createElement(String)
1310      * @see jsx.dom.Elements#createElement(String)
1311      */
1312     public final static Component createElement(String tagName) {
1313         return get(Elements.createElement(tagName));
1314     }
1315 
1316     /**
1317      * <p>Creates a new element with the specified tag name and name-space in the current 
1318      * document and returns a component that wraps the element.</p>
1319      * <p>This method simply calls the {@link Elements#createElement(String)} method 
1320      * to create an element and then calls the {@link #get(JsElement)} method to return a 
1321      * component for it. It is just like {@link #createElement(String)}, except that the 
1322      * created element has a name and name space instead of just a name. This method is 
1323      * useful only with XML documents that use name spaces.</p>
1324      * @param namespaceURI The unique identifier for the name space of the new element 
1325      * or <tt>null</tt> for no name space. 
1326      * @param qualifiedName The qualified name of the new element. This should include a 
1327      * name space prefix, a colon, and a local name. 
1328      * @return A component that wraps the newly created element node with the specified tag 
1329      * name and name space. 
1330      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1331      * the {@link JsDOMException#code} property of the value {@link JsDOMException#INVALID_CHARACTER_ERR} 
1332      * if <tt>qualifiedName</tt> contains an illegal character, the value {@link JsDOMException#NAMESPACE_ERR} 
1333      * if <tt>qualifiedName</tt> is malformed or there is a mismatch between <tt>qualifiedName</tt> 
1334      * and <tt>namespaceURI</tt>, or the value {@link JsDOMException#NOT_SUPPORTED_ERR} 
1335      * if the implementation does not support XML documents and therefore does not implement 
1336      * this method. See {@link js.Js#err(Object)} for JS Simulation.
1337      * @since 1.0
1338      * @see #createElement(String)
1339      * @see js.user.JsDocument#createElementNS(String, String)
1340      * @see jsx.dom.Elements#createElementNS(String, String)
1341      */
1342     public final static Component createElementNS(String namespaceURI, String qualifiedName) {
1343         return get(Elements.createElementNS(namespaceURI, qualifiedName));
1344     }
1345 
1346     /**
1347      * <p>Returns the tag name of the underlying element of the current component.</p>
1348      * @param e The current component.
1349      * @return The tag name of the underlying element of the current component.
1350      * @since 1.0
1351      */
1352     public final static String tagName(Component e) {
1353         return Nodes.tagName(getElement(e));
1354     }
1355 
1356     /**
1357      * <p>Checks if the tag name of the underlying element of the current component matches 
1358      * the specified name.</p>
1359      * @param e The current component.
1360      * @param tag A tag name.
1361      * @return <tt>true</tt> if they match case-insensitively; <tt>false</tt>, otherwise.
1362      * @since 1.0
1363      */
1364     public final static boolean tagName(Component e, String tag) {
1365         return Js.eq(StringLikes.toLowerCase(tagName(e)), StringLikes.toLowerCase(tag));
1366     }
1367 
1368     /**
1369      * <p>Returns the value of a named attribute of the underlying element as a string.</p>
1370      * <p>This method simply calls the {@link Elements#getAttribute(JsElement, String)} 
1371      * method with the underlying element and returns the value of a named attribute it has. 
1372      * Note that the {@link JsHTMLElement} objects have JavaScript properties that match 
1373      * each of the standard HTML attributes, so you need to use this method with HTML documents 
1374      * only if you are querying the value of nonstandard attributes.</p>
1375      * <p>In XML documents, attribute values are not available directly as element 
1376      * properties and must be looked up by calling this method. For XML documents that 
1377      * use name spaces, use {@link #getAttributeNS(Component, String, String)}.</p>
1378      * @param e The current component.
1379      * @param name The name of the attribute whose value is to be returned.
1380      * @return The value of the named attribute as a string. If the attribute is not 
1381      * defined, this method is supposed to return an empty string. Some implementations 
1382      * return <tt>null</tt> in this case, however.
1383      * @since 1.0
1384      * @see #getAttributeNS(Component, String, String)
1385      * @see JsElement#getAttribute(String)
1386      * @see Elements#getAttribute(JsElement, String)
1387      */
1388     public final static String getAttribute(Component e, String name) {
1389         return Elements.getAttribute(getElement(e), name);
1390     }
1391 
1392     /**
1393      * <p>Returns the string value of an attribute of the underlying element specified by 
1394      * local name and name space URI.</p> 
1395      * <p>This method simply calls the {@link Elements#getAttributeNS(JsElement, String, String)} 
1396      * method with the underlying element. It is useful only with XML documents that use 
1397      * name spaces and works just like the {@link #getAttribute(Component, String)} method, 
1398      * except that the attribute is specified by a combination of name space URI and local 
1399      * name within that name space.</p>
1400      * @param e The current component.
1401      * @param namespaceURI The URI that uniquely identifies the name space of this 
1402      * attribute or <tt>null</tt> for no name space.
1403      * @param localName The identifier that specifies the name of the attribute within 
1404      * its name space.
1405      * @return The value of the named attribute, as a string. If the attribute is not 
1406      * defined, this method is supposed to return an empty string, but some implementations 
1407      * return <tt>null</tt> instead.
1408      * @since 1.0
1409      * @see #getAttribute(Component, String)
1410      * @see JsElement#getAttributeNS(String, String)
1411      * @see Elements#getAttributeNS(JsElement, String, String)
1412      */
1413     public final static String getAttributeNS(Component e, String namespaceURI, String localName) {
1414         return Elements.getAttributeNS(getElement(e), namespaceURI, localName);
1415     }
1416 
1417     /**
1418      * <p>Sets or adds the named attribute of the underlying element to the specified 
1419      * string value.</p>
1420      * <p>This method simply calls the {@link Elements#setAttribute(JsElement, String, Object)} 
1421      * method with the underlying element and sets its specified attribute to the specified 
1422      * value. If no attribute by that name already exists, a new one is created.</p>
1423      * <p>Note that {@link JsHTMLElement} objects of an HTML document define JavaScript 
1424      * properties that correspond to all standard HTML attributes. Thus, you need to use 
1425      * this method only if you want to set a nonstandard attribute.</p>
1426      * @param e The current component.
1427      * @param name The name of the attribute to be created or modified.
1428      * @param value The string value of the attribute.
1429      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1430      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
1431      * if the element is read-only and does not allow its attributes to be removed or 
1432      * the value {@link JsDOMException#INVALID_CHARACTER_ERR} if the <tt>name</tt> argument 
1433      * contains a character that is not allowed in HTML or XML attribute names. See 
1434      * {@link Js#err(Object)} for JS Simulation.
1435      * @since 1.0
1436      * @see #setAttributeNS(Component, String, String, String)
1437      * @see Elements#setAttribute(JsElement, String, Object)
1438      * @see JsElement#setAttribute(String, Object)
1439      */
1440     public final static void setAttribute(Component e, String name, Object value) {
1441         Elements.setAttribute(getElement(e), name, value);
1442     }
1443 
1444     /**
1445      * <p>Sets or adds, to the list of attributes for the underlying element, an attribute 
1446      * with the specified a combination of local name and name space URI.</p>
1447      * <p>This method simply calls the {@link Elements#setAttributeNS(JsElement, String, String, String)} 
1448      * method with the underlying element. It is useful only with XML documents that use 
1449      * name spaces and works just like {@link #setAttribute(Component, String, Object)} 
1450      * method, except that the attribute is specified by the combination of a name space 
1451      * URI and a local name defined within that name space.</p>
1452      * @param e The current component.
1453      * @param namespaceURI The URI that uniquely identifies the name space of the 
1454      * attribute to be set or created, or <tt>null</tt> for no name space.
1455      * @param qualifiedName The name of the attribute, specified as an optional name 
1456      * space prefix and colon followed by the local name within the name space.
1457      * @param value The new value of the attribute.
1458      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1459      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
1460      * if the element is read-only and does not allow its attributes to be removed, 
1461      * the value {@link JsDOMException#INVALID_CHARACTER_ERR} if <tt>qualifiedName</tt> 
1462      * argument contains a character that is not allowed in HTML or XML attribute names, 
1463      * the value {@link JsDOMException#NAMESPACE_ERR} if <tt>qualifiedName</tt> is 
1464      * malformed, or there is a mismatch between the name space prefix of <tt>qualifiedName</tt> 
1465      * and the <tt>namespaceURI</tt> argument, or the value {@link JsDOMException#NOT_SUPPORTED_ERR} 
1466      * if the DOM implementation of the browser does not support XML documents. See 
1467      * {@link Js#err(Object)} for JS Simulation.
1468      * @since 1.0
1469      * @see #setAttribute(Component, String, Object)
1470      * @see Elements#setAttributeNS(JsElement, String, String, String)
1471      * @see JsElement#setAttributeNS(String, String, String)
1472      */
1473     public final static void setAttributeNS(Component e, String namespaceURI, String qualifiedName, String value) {
1474         Elements.setAttributeNS(getElement(e), namespaceURI, qualifiedName, value);
1475     }
1476 
1477     /**
1478      * <p>Removes the named attribute from the underlying element.</p>
1479      * <p>This method simply calls the {@link Elements#removeAttribute(JsElement, String)} 
1480      * method with the underlying element and deletes a named attribute from the element. 
1481      * If the named attribute has a default value specified by the document type, subsequent 
1482      * calls to this method return that default value. Attempts to remove nonexistent 
1483      * attributes or attributes that are not specified but have a default value are 
1484      * silently ignored.</p>
1485      * @param e The current component.
1486      * @param name The name of the desired attribute.
1487      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1488      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
1489      * if the element is read-only and does not allow its attributes to be removed. See 
1490      * {@link Js#err(Object)} for JS Simulation.
1491      * @since 1.0
1492      * @see #removeAttributeNS(Component, String, String)
1493      * @see Elements#removeAttribute(JsElement, String)
1494      * @see JsElement#removeAttribute(String)
1495      */
1496     public final static void removeAttribute(Component e, String name) {
1497         Elements.removeAttribute(getElement(e), name);
1498     }
1499 
1500     /**
1501      * <p>Removes, from the underlying element, an attribute with the specified a combination of 
1502      * local name and name space URI.</p>
1503      * <p>This method simply calls the {@link Elements#removeAttributeNS(JsElement, String, String)} 
1504      * method with the underlying element. It is useful only with XML documents that use 
1505      * name spaces and works just like {@link #removeAttribute(Component, String)} method, 
1506      * except that the attribute to be removed is specified by the combination of a name 
1507      * space URI and a local name defined within that name space.</p>
1508      * @param e The current component.
1509      * @param namespaceURI The URI that uniquely identifies the name space of this 
1510      * attribute or <tt>null</tt> for no name space.
1511      * @param localName The identifier that specifies the name of the attribute within 
1512      * its name space.
1513      * @throws RuntimeException JavaScript throws a {@link JsDOMException} object with 
1514      * the {@link JsDOMException#code} property of the value {@link JsDOMException#NO_MODIFICATION_ALLOWED_ERR} 
1515      * if the element is read-only and does not allow its attributes to be removed. See 
1516      * {@link Js#err(Object)} for JS Simulation.
1517      * @since 1.0
1518      * @see #removeAttribute(Component, String)
1519      * @see Elements#removeAttributeNS(JsElement, String, String)
1520      * @see JsElement#removeAttributeNS(String, String)
1521      */
1522     public final static void removeAttributeNS(Component e, String namespaceURI, String localName) {
1523         Elements.removeAttributeNS(getElement(e), namespaceURI, localName);
1524     }
1525 }