001/*
002 *  Copyright 2012 GWT-Bootstrap
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.github.gwtbootstrap.client.ui.base;
017
018import com.github.gwtbootstrap.client.ui.constants.Placement;
019import com.github.gwtbootstrap.client.ui.constants.Trigger;
020import com.github.gwtbootstrap.client.ui.constants.VisibilityChange;
021import com.google.gwt.core.client.Scheduler;
022import com.google.gwt.core.client.Scheduler.ScheduledCommand;
023import com.google.gwt.dom.client.Element;
024import com.google.gwt.event.logical.shared.AttachEvent;
025import com.google.gwt.user.client.ui.HasOneWidget;
026import com.google.gwt.user.client.ui.HasText;
027import com.google.gwt.user.client.ui.HasWidgets;
028import com.google.gwt.user.client.ui.IsWidget;
029import com.google.gwt.user.client.ui.Widget;
030
031//@formatter:off
032/**
033* Base class for widgets that hover above other widgets.
034* 
035* @since 2.0.4.0
036* 
037* @author Dominik Mayer
038* 
039* @see <a href="http://twitter.github.com/bootstrap/javascript.html#popovers">Bootstrap documentation</a>
040*/
041//@formatter:on
042public abstract class HoverBase extends MarkupWidget  implements IsWidget, HasWidgets, HasOneWidget, IsAnimated, HasTrigger, HasPlacement, HasText, HasShowDelay, HasVisibility {
043
044        /**
045         * Whether the widget is animated or not.
046         */
047        protected boolean animated = true;
048
049        /**
050         * The placement of the widget relative to its trigger element.
051         */
052        protected Placement placement = Placement.TOP;
053
054        /**
055         * The action that triggers the widget.
056         */
057        protected Trigger trigger = Trigger.HOVER;
058
059        /**
060         * The delay until the widget is shown.
061         */
062        protected int showDelayInMilliseconds = 0;
063
064        /**
065         * The delay until the widget is hidden.
066         */
067        protected int hideDelayInMilliseconds = 0;
068
069        /**
070         * Creates a new widget based on the provided HTML tag.
071         */
072        public HoverBase() {
073        }
074
075        /**
076         * {@inheritDoc}
077         */
078        @Override
079        public Widget asWidget() {
080                
081            if(getWidget() != null) {
082                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
083                
084                @Override
085                public void execute() {
086                    removeDataIfExists();
087                    
088                    reconfigure();
089                    
090                    getWidget().addAttachHandler(new AttachEvent.Handler() {
091                        
092                        @Override
093                        public void onAttachOrDetach(AttachEvent event) {
094                            if (!event.isAttached()) {
095                                changeVisibility(VisibilityChange.HIDE);
096                            }
097                        }
098                    });
099                }
100            });
101                }
102                
103                return getWidget();
104
105        }
106        
107        protected void removeDataIfExists() {
108                removeDataIfExists(getWidget().getElement(), getDataName());
109        }
110        
111        protected native void removeDataIfExists(Element e, String dataName) /*-{
112                if($wnd.jQuery(e).data(dataName)) {
113                        var data = $wnd.jQuery(e).data(dataName);
114                        var eventIn, eventOut;
115                        if (data.options.trigger != 'manual') {
116                                eventIn  = data.options.trigger == 'hover' ? 'mouseenter' : 'focus'
117                                eventOut = data.options.trigger == 'hover' ? 'mouseleave' : 'blur'
118                                data.$element.off(eventIn);
119                                data.$element.off(eventOut);
120                        }
121                        $wnd.jQuery(e).removeData(dataName);
122                }
123        }-*/;
124
125        /**
126         * Adds an HTML data attribute to the widget's tag.
127         * 
128         * @param e target element
129         * 
130         * @param attribute
131         *            the name of the attribute without leading <code>"data-"</code>
132         * @param value
133         *            the value to be stored
134         */
135        protected void setDataAttribute(Element e , String attribute, String value) {
136                e.setAttribute("data-" + attribute, value);
137        }
138
139        /**
140         * {@inheritDoc}
141         */
142        public void setAnimation(boolean animated) {
143                this.animated = animated;
144        }
145
146        /**
147         * Redraws the widget with the currently set options. This must <b>not</b>
148         * be called when a parameter is updated because it would deactivate all
149         * other parameters. No idea why...
150         */
151        public abstract void reconfigure();
152
153        /**
154         * {@inheritDoc}
155         */
156        public boolean getAnimation() {
157                return animated;
158        }
159
160        /**
161         * {@inheritDoc} Relative to its trigger element.
162         */
163        public void setPlacement(Placement placement) {
164                this.placement = placement;
165        }
166
167        /**
168         * {@inheritDoc}
169         */
170        public Placement getPlacement() {
171                return placement;
172        }
173
174        /**
175         * {@inheritDoc}
176         */
177        public void setTrigger(Trigger trigger) {
178                this.trigger = trigger;
179        }
180
181        /**
182         * {@inheritDoc}
183         */
184        public Trigger getTrigger() {
185                return trigger;
186        }
187
188        /**
189         * {@inheritDoc}
190         */
191        public void setShowDelay(int delayInMilliseconds) {
192                showDelayInMilliseconds = delayInMilliseconds;
193        }
194
195        /**
196         * {@inheritDoc}
197         */
198        public int getShowDelay() {
199                return showDelayInMilliseconds;
200        }
201
202        /**
203         * {@inheritDoc}
204         */
205        public void setHideDelay(int delayInMilliseconds) {
206                hideDelayInMilliseconds = delayInMilliseconds;
207        }
208
209        /**
210         * {@inheritDoc}
211         */
212        public int getHideDelay() {
213                return hideDelayInMilliseconds;
214        }
215
216        /**
217         * {@inheritDoc}
218         */
219        public void show() {
220                changeVisibility(VisibilityChange.SHOW);
221        }
222
223        /**
224         * {@inheritDoc}
225         */
226        public void hide() {
227                changeVisibility(VisibilityChange.HIDE);
228        }
229
230        /**
231         * {@inheritDoc}
232         */
233        public void toggle() {
234                changeVisibility(VisibilityChange.TOGGLE);
235        }
236
237        /**
238         * Changes the visibility of the widget.
239         * 
240         * @param visibilityChange
241         *            the action to be performed
242         */
243        protected abstract void changeVisibility(VisibilityChange visibilityChange);
244
245        /**
246         * Get data name of JS Data API.
247         * @return data name
248         */
249        protected abstract String getDataName();
250}