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            hide();
109                removeDataIfExists(getWidget().getElement(), getDataName());
110        }
111        
112        protected native void removeDataIfExists(Element e, String dataName) /*-{
113            var element = $wnd.jQuery(e);
114                if(element.data(dataName)) {
115                        var data = element.data(dataName);
116                        var eventIn, eventOut;
117                        if (data.options.trigger != 'manual') {
118                                eventIn  = data.options.trigger == 'hover' ? 'mouseenter' : 'focus'
119                                eventOut = data.options.trigger == 'hover' ? 'mouseleave' : 'blur'
120                                data.$element.off(eventIn);
121                                data.$element.off(eventOut);
122                        }
123                        element.removeData(dataName);
124                }
125        }-*/;
126
127        /**
128         * Adds an HTML data attribute to the widget's tag.
129         * 
130         * @param e target element
131         * 
132         * @param attribute
133         *            the name of the attribute without leading <code>"data-"</code>
134         * @param value
135         *            the value to be stored
136         */
137        protected void setDataAttribute(Element e , String attribute, String value) {
138                e.setAttribute("data-" + attribute, value);
139        }
140
141        /**
142         * {@inheritDoc}
143         */
144        public void setAnimation(boolean animated) {
145                this.animated = animated;
146        }
147
148        /**
149         * Redraws the widget with the currently set options. This must <b>not</b>
150         * be called when a parameter is updated because it would deactivate all
151         * other parameters. No idea why...
152         */
153        public abstract void reconfigure();
154
155        /**
156         * {@inheritDoc}
157         */
158        public boolean getAnimation() {
159                return animated;
160        }
161
162        /**
163         * {@inheritDoc} Relative to its trigger element.
164         */
165        public void setPlacement(Placement placement) {
166                this.placement = placement;
167        }
168
169        /**
170         * {@inheritDoc}
171         */
172        public Placement getPlacement() {
173                return placement;
174        }
175
176        /**
177         * {@inheritDoc}
178         */
179        public void setTrigger(Trigger trigger) {
180                this.trigger = trigger;
181        }
182
183        /**
184         * {@inheritDoc}
185         */
186        public Trigger getTrigger() {
187                return trigger;
188        }
189
190        /**
191         * {@inheritDoc}
192         */
193        public void setShowDelay(int delayInMilliseconds) {
194                showDelayInMilliseconds = delayInMilliseconds;
195        }
196
197        /**
198         * {@inheritDoc}
199         */
200        public int getShowDelay() {
201                return showDelayInMilliseconds;
202        }
203
204        /**
205         * {@inheritDoc}
206         */
207        public void setHideDelay(int delayInMilliseconds) {
208                hideDelayInMilliseconds = delayInMilliseconds;
209        }
210
211        /**
212         * {@inheritDoc}
213         */
214        public int getHideDelay() {
215                return hideDelayInMilliseconds;
216        }
217
218        /**
219         * {@inheritDoc}
220         */
221        public void show() {
222                changeVisibility(VisibilityChange.SHOW);
223        }
224
225        /**
226         * {@inheritDoc}
227         */
228        public void hide() {
229                changeVisibility(VisibilityChange.HIDE);
230        }
231
232        /**
233         * {@inheritDoc}
234         */
235        public void toggle() {
236                changeVisibility(VisibilityChange.TOGGLE);
237        }
238
239        /**
240         * Changes the visibility of the widget.
241         * 
242         * @param visibilityChange
243         *            the action to be performed
244         */
245        protected abstract void changeVisibility(VisibilityChange visibilityChange);
246
247        /**
248         * Get data name of JS Data API.
249         * @return data name
250         */
251        protected abstract String getDataName();
252}