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;
021 
022 import js.ArrayLike;
023 import js.Function;
024 import js.Id;
025 import js.Initializer;
026 import js.Js;
027 import js.ObjectLike;
028 import js.RegExpLike;
029 import js.Vars;
030 import js.dom.EventTarget;
031 import js.user.JsEvent;
032 import jsx.Code;
033 import jsx.Configurable;
034 import jsx.Source;
035 import jsx.core.ArrayLikes;
036 import jsx.core.StringLikes;
037 import jsx.event.Handle;
038 import jsx.ui.css.Pseudo;
039 import jsx.ui.css.StyleSheets;
040 
041 /**
042  * <p>A base class for graphical widgets.</p>
043  * <p>A {@link Widget} is either a {@link Component} or a wrapper that wraps a {@link Component} 
044  * to encapsulate additional functionalities.</p>
045  * <p>Note that, widgets are {@link Configurable} and event sources which fire {@link Widget.Event}.</p>
046  * 
047  * @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>
048  */
049 public class Widget extends Source
050 {
051     /**
052      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
053      * <p>The identified configurable property of a {@link Widget} 
054      * is a reference to the component wrapped by this widget.</p>
055      * @since 1.0
056      */
057     protected final static Id<Component> COMPONENT = new Id<Component>();
058 
059     /**
060      * <p>A typical constructor forcing constructors of subclasses to pass an initializing 
061      * object as argument.</p>
062      * <p>This constructor simply invokes the typical constructor of the superclass 
063      * passing the specified initializing object as the argument.</p>
064      * <p>Note that, this constructor sets the configurable property {@link #COMPONENT} of 
065      * this widget to a newly created component which wraps a {@link js.user.JsHTMLDivElement} if 
066      * the property is still undefined.</p>
067      * @param ini The initializing object.
068      * @since 1.0
069      */
070     protected Widget(ObjectLike ini) {
071         super(ini);
072         if (this instanceof Component) {
073             ini(this).var(COMPONENT, (Component)this);
074         } else if (Js.not(unwrap())) {
075             ini(this).var(COMPONENT, Component.div());
076         }
077     }
078 
079     /**
080      * <p>A typical constructor forcing constructors of subclasses to pass 
081      * initializing data.</p>
082      * <p>This constructor simply invokes the typical constructor of the superclass 
083      * passing the specified initializing object as the argument.</p>
084      * <p>Note that, this constructor sets the configurable property {@link #COMPONENT} of 
085      * this widget to the argument component.</p>
086      * @param e The underlying component.
087      * @since 1.0
088      */
089     public Widget(Component e) {
090         this(new Initializer().set(COMPONENT, e).var());
091     }
092 
093     /**
094      * <p>Returns the string representation of the component.</p>
095      * @return The string representation of the component.
096      * @since 1.0
097      */
098     @Override
099     public String toString() {
100         return ArrayLikes.join(
101                 new Vars<String>()
102                     .add(getClass().getName())
103                     .add(unwrap().toString())
104                     .var(),
105                 ":"
106         );
107     }
108 
109     /**
110      * <p>Returns the wrapped component.</p>
111      * <p>The event queuing and dispatching mechanism in the superclass {@link jsx.Source.Event} 
112      * tries to dispatch an event fired from a widget also to its wrapped component by calling 
113      * this method, if this widget does wrap another valid source.</p>
114      * <p>This method simply returns the configurable property {@link #COMPONENT} of this 
115      * wrapper widget.</p>
116      * @return The wrapped component.
117      * @since 1.0
118      */
119     @Override
120     public final Component unwrap() {
121         return ini(this).var(COMPONENT);
122     }
123 
124     @SuppressWarnings("unchecked")
125     private static final ArrayLike<String> all(Class<? extends Widget> cls) {
126         if (cls == Widget.class) {
127             return new Vars<String>().add(css(Widget.class)).var();
128         }
129         return ArrayLikes.add(
130                 all((Class<? extends Widget>)cls.getSuperclass()),
131                 css(cls)
132         );
133     }
134 
135     /**
136      * <p>Creates a CSS sub-class selector for the current widget.</p>
137      * @param name A sub selector.
138      * @return The created CSS class selector.
139      * @since 1.0
140      */
141     protected final String sub(String name) {
142         return css(getClass(), name); 
143     }
144 
145     /**
146      * <p>Creates a CSS sub-class selector for the current widget.</p>
147      * @param name A sub selector.
148      * @return The created CSS class selector.
149      * @since 1.0
150      */
151     public final ArrayLike<String> subs(String name) {
152         ArrayLike<String> all = all(getClass());
153         for (int i = 0, len = ArrayLikes.length(all); i < len; i++) {
154             all.set(i, Code.join(all.get(i), "_", name));
155         }
156         return all; 
157     }
158 
159     /**
160      * <p>Creates a CSS class selector for a type of widget.</p>
161      * @param cls The class of widget.
162      * @return The created CSS class selector.
163      * @since 1.0
164      */
165     protected static final String css(Class<? extends Widget> cls) {
166         return StringLikes.replace(cls.getName(), DOTDOLLAR, "_"); 
167     }
168 
169     private static final RegExpLike DOTDOLLAR = Js.re("\\.|\\$", "g");
170 
171     /**
172      * <p>Creates a CSS class selector for a widget class with the name of a sub selector.</p>
173      * @param cls A CSS class of the widget.
174      * @param name A sub selector.
175      * @return The created CSS class selector.
176      * @since 1.0
177      */
178     protected static final String css(Class<? extends Widget> cls, String name) {
179         return Code.join(css(cls), "_", name);
180     }
181 
182     /**
183      * <p>A CSS class selector for no select.</p>
184      * @since 1.0
185      */
186     protected static final String NOSELECT = "jsx_noselect";
187 
188     private static final String ROUND_CORNER = Code.join(Code.join("jsx", "_", "round"), "_", "corner");
189 
190     /**
191      * <p>A CSS class selector for top-left round corner.</p>
192      * @since 1.0
193      */
194     protected static final String ROUND_CORNER_TL = Code.join(ROUND_CORNER, "_", "tl");
195     /**
196      * <p>A CSS class selector for top-right round corner.</p>
197      * @since 1.0
198      */
199     protected static final String ROUND_CORNER_TR = Code.join(ROUND_CORNER, "_", "tr");
200     /**
201      * <p>A CSS class selector for bottom-left round corner.</p>
202      * @since 1.0
203      */
204     protected static final String ROUND_CORNER_BL = Code.join(ROUND_CORNER, "_", "bl");
205     /**
206      * <p>A CSS class selector for bottom-right round corner.</p>
207      * @since 1.0
208      */
209     protected static final String ROUND_CORNER_BR = Code.join(ROUND_CORNER, "_", "br");
210     /**
211      * <p>A CSS class selector for top round corner.</p>
212      * @since 1.0
213      */
214     protected static final String ROUND_CORNER_TOP    = Code.join(ROUND_CORNER, "_", "top");
215     /**
216      * <p>A CSS class selector for bottom round corner.</p>
217      * @since 1.0
218      */
219     protected static final String ROUND_CORNER_BOTTOM = Code.join(ROUND_CORNER, "_", "bottom");
220     /**
221      * <p>A CSS class selector for right round corner.</p>
222      * @since 1.0
223      */
224     protected static final String ROUND_CORNER_RIGHT  = Code.join(ROUND_CORNER, "_", "right");
225     /**
226      * <p>A CSS class selector for left round corner.</p>
227      * @since 1.0
228      */
229     protected static final String ROUND_CORNER_LEFT   = Code.join(ROUND_CORNER, "_", "left");
230     /**
231      * <p>A CSS class selector for round corners.</p>
232      * @since 1.0
233      */
234     protected static final String ROUND_CORNER_ALL    = Code.join(ROUND_CORNER, "_", "all");
235 
236     /**
237      * <p>Adds CSS class names to the underlying HTML element.</p>
238      * @since 1.0
239      */
240     protected final void addClasses() {
241         StyleSheets.prelink();
242         Component.addClasses(unwrap(), all(getClass()));
243     }
244 
245     /**
246      * <p>Returns the container source object if this one is graphically contained by 
247      * another widget.</p>
248      * <p>The wrapped object of a widget must also be a widget.</p>
249      * <p>The event queuing and dispatching mechanism of the superclass tries to dispatch 
250      * an event fired from a widget also to its container by calling this method, if this 
251      * source is graphically contained by another valid source and the event wants to 
252      * bubble, that is, its {@link jsx.Source.Event#BUBBLE} configurable property is <tt>true</tt>.</p>
253      * @return The widget graphically containing this widget.
254      * @since 1.0
255      */
256     @Override
257     public Component getParent() {
258         return unwrap().getParent();
259     }
260 
261     /**
262      * <p>Determines if the widget is the parent of another widget.</p>
263      * <p>This method simply checks if the wrapped component contains that of  the other 
264      * widget.</p>
265      * @param w Another widget.
266      * @return <tt>true</tt> if the widget contains the other; <tt>false</tt>, otherwise.
267      * @since 1.0
268      */
269     public final boolean isParentOf(Widget w) {
270         return w.getParent() == unwrap();
271     }
272 
273     /**
274      * <p>Determines if the widget has been rendered.</p>
275      * <p>This method simply checks if the wrapped component has wrapped an HTML element.</p>
276      * @return <tt>true</tt> if the widget is rendered; <tt>false</tt>, otherwise.
277      * @since 1.0
278      */
279     public final boolean isRendered() {
280         return Js.be(Component.getHTMLElement(unwrap()));
281     }
282 
283     /**
284      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
285      * <p>The identified configurable property of a {@link Widget} 
286      * is a reference to the {@link Pseudo} object associated with the underlying
287      * component wrapped by this widget.</p>
288      * <p>The property stores a {@link Pseudo} object for "active" pseudo-selector.</p>
289      * @since 1.0
290      */
291     protected final static Id<Pseudo> ACTIVE   = new Id<Pseudo>();
292     /**
293      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
294      * <p>The identified configurable property of a {@link Widget} 
295      * is a reference to the {@link Pseudo} object associated with the underlying
296      * component wrapped by this widget.</p>
297      * <p>The property stores a {@link Pseudo} object for "focus" pseudo-selector.</p>
298      * @since 1.0
299      */
300     protected final static Id<Pseudo> FOCUS    = new Id<Pseudo>();
301     /**
302      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
303      * <p>The identified configurable property of a {@link Widget} 
304      * is a reference to the {@link Pseudo} object associated with the underlying
305      * component wrapped by this widget.</p>
306      * <p>The property stores a {@link Pseudo} object for "hidee" pseudo-selector.</p>
307      * @since 1.0
308      */
309     protected final static Id<Pseudo> HIDE     = new Id<Pseudo>();
310     /**
311      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
312      * <p>The identified configurable property of a {@link Widget} 
313      * is a reference to the {@link Pseudo} object associated with the underlying
314      * component wrapped by this widget.</p>
315      * <p>The property stores a {@link Pseudo} object for "hover" pseudo-selector.</p>
316      * @since 1.0
317      */
318     protected final static Id<Pseudo> HOVER    = new Id<Pseudo>();
319     /**
320      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
321      * <p>The identified configurable property of a {@link Widget} 
322      * is a reference to the {@link Pseudo} object associated with the underlying
323      * component wrapped by this widget.</p>
324      * <p>The property stores a {@link Pseudo} object for "check" pseudo-selector.</p>
325      * @since 1.0
326      */
327     protected final static Id<Pseudo> CHECK    = new Id<Pseudo>();
328     /**
329      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
330      * <p>The identified configurable property of a {@link Widget} 
331      * is a reference to the {@link Pseudo} object associated with the underlying
332      * component wrapped by this widget.</p>
333      * <p>The property stores a {@link Pseudo} object for "collapse" pseudo-selector.</p>
334      * @since 1.0
335      */
336     protected final static Id<Pseudo> COLLAPSE = new Id<Pseudo>();
337 
338     /**
339      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
340      * <p>The identified configurable property of a {@link Widget} 
341      * is a reference to a boolean value associated with the underlying
342      * component wrapped by this widget.</p>
343      * <p>The property indicates whether the widget prevents events.</p>
344      * @since 1.0
345      */
346     protected final static Id<Boolean> PREVENTEVENT = new Id<Boolean>();
347     /**
348      * <p>A global identifier for a configurable property of a {@link Widget}.</p>
349      * <p>The identified configurable property of a {@link Widget} 
350      * is a reference to a boolean value associated with the underlying
351      * component wrapped by this widget.</p>
352      * <p>The property indicates whether the widget prevents click events.</p>
353      * @since 1.0
354      */
355     protected final static Id<Boolean> PREVENTCLICK = new Id<Boolean>();
356 
357     /**
358      * <p>Associates a type of high level event to a browser event of the underly HTML element.</p>
359      * <p>This method creates, attaches and returns a {@link Handle}. To destroy the 
360      * association, simply detach the returned {@link Handle} by calling 
361      * {@link Handle#detach()}.</p>
362      * @param bevt The name of a browser event.
363      * @param evt A high level widget event.
364      * @param pid A {@link Id} of a property that tells whether prevents any events.
365      * @return The newly created {@link Handle}.
366      * @since 1.0
367      */
368     protected final Handle attachEvent(String bevt, final Event<?> evt, final Id<Boolean> pid) {
369         return attachEvent(unwrap(), bevt, evt, pid);
370     }
371 
372     /**
373      * <p>Associates a type of high level event to a browser event of the given component.</p>
374      * <p>This method creates, attaches and returns a {@link Handle}. To destroy the 
375      * association, simply detach the returned {@link Handle} by calling 
376      * {@link Handle#detach()}.</p>
377      * @param e A component that fires the browser event.
378      * @param bevt The name of a browser event.
379      * @param evt A high level widget event.
380      * @param pid A {@link Id} of a property that tells whether prevents any events.
381      * @return The newly created {@link Handle}.
382      * @since 1.0
383      */
384     protected final Handle attachEvent(Component e, String bevt, final Event<?> evt, final Id<Boolean> pid) {
385         return attachEvent(Component.getHTMLElement(e), bevt, new Function<Boolean>() {
386             @Override
387             protected Boolean function(Object jsthis, Call<Boolean> callee) {
388                 try {
389                     if (Js.not(ini(unwrap()).var(pid))) {
390                         fire(evt);
391                     }
392                 } catch (Exception e) {}
393                 return true;
394             }
395         });
396     }
397 
398     private static final Handle attachEvent(EventTarget et, String bevt, Function<?> f) {
399         Handle h = new Handle(
400                 et,
401                 bevt,
402                 f.var(),
403                 false
404         );
405         h.attach();
406         return h;
407     }
408 
409     /**
410      * <p>Associates a type of high level event to a browser event of the underly HTML element.</p>
411      * <p>This method creates, attaches and returns a {@link Handle}. To destroy the 
412      * association, simply detach the returned {@link Handle} by calling 
413      * {@link Handle#detach()}.</p>
414      * @param bevt The name of a browser event.
415      * @param evt A high level widget event.
416      * @return The newly created {@link Handle}.
417      * @since 1.0
418      */
419     protected final Handle attachEvent(String bevt, final Event<?> evt) {
420         return attachEvent(bevt, evt, PREVENTEVENT);
421     }
422 
423     /**
424      * <p>An abstract base class for high level events to be fired from widgets.</p>
425      * <p>This class is designed generic with the type parameter being the type of its 
426      * corresponding listener type. A subclass must be concrete and final if it is to be an 
427      * event type. As an event type, the subclass must also declare the type parameter of 
428      * this class as the type of its own listener.</p>
429      * <p>An event listener is typically of an interface with a declared method to react on 
430      * the event it handles so that the class of the event can implement the {@link Event#execute(Object)} 
431      * method by simply calling that declared method passing the event itself as the argument. 
432      * A subclass of this class defines events uniquely typed by the subclass itself and 
433      * designates a listener type corresponding to that type of events.</p>
434      * <p>For an event source to fire an event, the listener of the event must have been 
435      * registered onto the source object with a call to the {@link Source#addListener(Class, Object)} 
436      * method which asks both the type of the event and the listener object for arguments.</p>
437      * <p>Note that, high level events are {@link Configurable}.</p>
438      * 
439      * @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>
440      */
441     public static abstract class Event<T> extends Source.Event<T>
442     {
443         /**
444          * <p>A typical constructor forcing constructors of subclasses to pass 
445          * initializing data.</p>
446          * <p>This constructor simply invokes the typical constructor of the superclass 
447          * passing the specified initializing object as the argument.</p>
448          * <p>Note that, this constructor does not change the configurable properties 
449          * {@link Event#BUBBLE} and {@link Event#UPDATE} of this event object.</p>
450          * @since 1.0
451          */
452         protected Event(ObjectLike ini) {
453             super(ini);
454         }
455 
456         /**
457          * <p>A typical constructor passing the target component.</p>
458          * <p>This constructor creates an initializing object, sets its configurable 
459          * property {@link #TARGET} to the argument component and then invokes the default 
460          * constructor of this class passing the newly created initializing object as the 
461          * argument.</p>
462          * <p>Note that, this constructor does not change the configurable properties 
463          * {@link Event#BUBBLE} and {@link Event#UPDATE} of this event object.</p>
464          * @since 1.0
465          */
466         protected Event(Component target) {
467             this(new Initializer().set(TARGET, target).var());
468         }
469 
470         /**
471          * <p>The default constructor which simply invokes the default constructor of the 
472          * superclass.</p>
473          * <p>Note that, this constructor does not change the configurable properties 
474          * {@link Event#BUBBLE} and {@link Event#UPDATE} of this event object.</p>
475          * @since 1.0
476          */
477         protected Event() {
478         }
479 
480         /**
481          * <p>A global identifier for a configurable property of an {@link Event} object.</p>
482          * <p>The identified configurable property of an {@link Event} object 
483          * is a reference to the browser event held by the high level event.</p>
484          * @since 1.0
485          */
486         protected final static Id<JsEvent> BROWSEREVENT = new Id<JsEvent>();
487         /**
488          * <p>A global identifier for a configurable property of an {@link Event} object.</p>
489          * <p>The identified configurable property of an {@link Event} object 
490          * is a reference to the target component held by the event.</p>
491          * @since 1.0
492          */
493         protected final static Id<Component> TARGET = new Id<Component>();
494 
495         /**
496          * <p>Gets the browser event held by the specified high level event.</p>
497          * @param evt The high level event to get browser event from.
498          * @return The browser event held by the specified high level event.
499          * @since 1.0
500          */
501         public static final JsEvent getBrowserEvent(Event<?> evt) {
502             return ini(evt).var(BROWSEREVENT);
503         }
504         /**
505          * <p>Gets the component referenced by the configurable property {@link #TARGET} of 
506          * a specified event.</p>
507          * @param evt The event to get target component from.
508          * @return The target component held by the specified event.
509          * @since 1.0
510          */
511         public static final Component getTarget(Event<?> evt) {
512             return ini(evt).var(TARGET);
513         }
514     }
515 }