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.ui.css;
021 
022 import js.ArrayLike;
023 import js.Id;
024 import js.Js;
025 import js.Vars;
026 import js.core.JsFunction;
027 import jsx.Code;
028 import jsx.event.Handle;
029 import jsx.ui.Component;
030 import jsx.ui.Widget;
031 
032 /**
033  * <p>A class of wrapper widgets that facilitates to efficiently manage CSS pseudo class 
034  * selectors over the underlying HTML elements of the wrapped components.</p>
035  * 
036  * @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>
037  */
038 public final class Pseudo extends Widget
039 {
040     private final String pseudo;
041 
042     /**
043      * <p>Typically constructs a {@link Pseudo} widget.</p>
044      * <p>This constructor invokes the typical constructor of the super class with the given 
045      * component as argument.</p>
046      * @param c The underlying component for the {@link Pseudo} widget being constructed.
047      * @param pseudo The name of a CSS pseudo class selector for the {@link Pseudo} widget 
048      * to manage.
049      * @since 1.0
050      */
051     public Pseudo(Component c, String pseudo) {
052         super(c);
053         this.pseudo = pseudo;
054     }
055 
056     /**
057      * <p>Sets up events of the underly HTML element to activate or deactivate the pseudo selector.</p>
058      * @param c The underlying component to handle the browser events.
059      * @param activator A browser event to add the pseudo selector to the component.
060      * @param deactivator A browser event to remove the pseudo selector from the component.
061      * @return An array of the two {@link Handle}s being set up.
062      * @since 1.0
063      */
064     public final ArrayLike<Handle> attach(Component c, String activator, String deactivator) {
065         return new Vars<Handle>().add(
066                 c.handle(activator  , getAdder  ())
067         ).add(
068                 c.handle(deactivator, getRemover())
069         ).var();
070     }
071 
072     /**
073      * <p>Sets up a browser event of the underly HTML element toggling to activate and deactivate 
074      * the pseudo selector.</p>
075      * @param c The underlying component to handle the browser events.
076      * @param bevt A browser event toggling to add and remove the pseudo selector of the component.
077      * @return The {@link Handle} being set up.
078      * @since 1.0
079      */
080     public final Handle toggle(Component c, String bevt) {
081         return c.handle(bevt, getToggler());
082     }
083 
084     private final static Id<JsFunction<Boolean>> ADDER   = new Id<JsFunction<Boolean>>();
085     private final static Id<JsFunction<Boolean>> REMOVER = new Id<JsFunction<Boolean>>();
086     private final static Id<JsFunction<Boolean>> TOGGLER = new Id<JsFunction<Boolean>>();
087 
088     /**
089      * <p>Gets a function that toggles to add and remove the pseudo selector against the 
090      * underlying component.</p>
091      * @return The toggling function.
092      * @since 1.0
093      */
094     public final JsFunction<Boolean> getToggler() {
095         JsFunction<Boolean> f = ini(this).var(TOGGLER);
096         if (Js.not(f)) {
097             f = new JsFunction<Boolean>(
098                     Handle.HANDLER.var().invoke(
099                             new Vars<Object>().add(Component.getHTMLElement(unwrap())).add(
100                                     Code.handler(
101                                             Code.cond(
102                                                     Code.test(
103                                                             Code.regi(Code.hyphen(ui(Code.NONWS), pseudo)),
104                                                             Code.THIS_CLASSNAME
105                                                     ),
106                                                     Code.replaceThisClassName(
107                                                             Code.hyphen(ui(Code.NONWS), pseudo),
108                                                             Code.EMPTY
109                                                     ),
110                                                     Code.replaceThisClassName(
111                                                             ui(Code.NONWS),
112                                                             lambda(pseudo)
113                                                     )
114                                             )
115                                     )
116                             )
117                     )
118             );
119             ini(this).var(TOGGLER, f);
120         }
121         return f;
122     }
123 
124     /**
125      * <p>Gets a function that adds the pseudo selector to the underlying component.</p>
126      * @return The pseudo selector adding function.
127      * @since 1.0
128      */
129     public final JsFunction<Boolean> getAdder() {
130         JsFunction<Boolean> f = ini(this).var(ADDER);
131         if (Js.not(f)) {
132             f = new JsFunction<Boolean>(
133                     Handle.HANDLER.var().invoke(
134                             new Vars<Object>().add(Component.getHTMLElement(unwrap())).add(
135                                     Code.handler(
136                                             Code.replaceThisClassName(
137                                                     ui(Code.NONWS),
138                                                     lambda(pseudo)
139                                             )
140                                     )
141                             )
142                     )
143             );
144             ini(this).var(ADDER, f);
145         }
146         return f;
147     }
148 
149     private final static JsFunction<String> lambda(String s) {
150         return Js.function(
151                 "w",
152                 Code.ret(
153                         Code.cond(
154                                 Code.test(Code.re(Code.ASCII),"w"),
155                                 Code.qlist("w", ps("w", Code.qt(s))),
156                                 "w"
157                         )
158                 )
159         );
160     }
161 
162     /**
163      * <p>Gets a function that removes the pseudo selector from the underlying component.</p>
164      * @return The pseudo selector removing function.
165      * @since 1.0
166      */
167     public final JsFunction<Boolean> getRemover() {
168         JsFunction<Boolean> f = ini(this).var(REMOVER);
169         if (Js.not(f)) {
170             f = new JsFunction<Boolean>(
171                     Handle.HANDLER.var().invoke(
172                             new Vars<Object>().add(Component.getHTMLElement(unwrap())).add(
173                                     Code.handler(
174                                             Code.replaceThisClassName(
175                                                     Code.hyphen(ui(Code.NONWS), pseudo),
176                                                     Code.EMPTY
177                                             )
178                                     )
179                             )
180                     )
181             );
182             ini(this).var(REMOVER, f);
183         }
184         return f;
185     }
186 
187     private final static String JSX_UI = Code.underline("jsx", "ui");
188 
189     private static final String ui(String s) {
190         return Code.underline(JSX_UI, s);
191     }
192 
193     private static final String ps(String css, String ps) {
194         return Code.add(Code.add(css, "'-'"), ps);
195     }
196 }