001    package org.gwtbootstrap3.extras.slider.client.ui.base;
002    
003    /*
004     * #%L
005     * GwtBootstrap3
006     * %%
007     * Copyright (C) 2013 - 2015 GwtBootstrap3
008     * %%
009     * Licensed under the Apache License, Version 2.0 (the "License");
010     * you may not use this file except in compliance with the License.
011     * You may obtain a copy of the License at
012     * 
013     *      http://www.apache.org/licenses/LICENSE-2.0
014     * 
015     * Unless required by applicable law or agreed to in writing, software
016     * distributed under the License is distributed on an "AS IS" BASIS,
017     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018     * See the License for the specific language governing permissions and
019     * limitations under the License.
020     * #L%
021     */
022    
023    import java.util.ArrayList;
024    import java.util.Collections;
025    import java.util.List;
026    
027    import org.gwtbootstrap3.client.ui.TextBox;
028    import org.gwtbootstrap3.client.ui.base.HasId;
029    import org.gwtbootstrap3.client.ui.base.HasResponsiveness;
030    import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
031    import org.gwtbootstrap3.client.ui.base.mixin.AttributeMixin;
032    import org.gwtbootstrap3.client.ui.constants.DeviceSize;
033    import org.gwtbootstrap3.extras.slider.client.ui.base.constants.HandleType;
034    import org.gwtbootstrap3.extras.slider.client.ui.base.constants.OrientationType;
035    import org.gwtbootstrap3.extras.slider.client.ui.base.constants.ScaleType;
036    import org.gwtbootstrap3.extras.slider.client.ui.base.constants.SelectionType;
037    import org.gwtbootstrap3.extras.slider.client.ui.base.constants.TooltipType;
038    import org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers;
039    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideDisabledEvent;
040    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideDisabledHandler;
041    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideEnabledEvent;
042    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideEnabledHandler;
043    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideEvent;
044    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideHandler;
045    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideStartEvent;
046    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideStartHandler;
047    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideStopEvent;
048    import org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideStopHandler;
049    
050    import com.google.gwt.core.client.JavaScriptObject;
051    import com.google.gwt.core.client.JsArrayNumber;
052    import com.google.gwt.core.client.JsArrayString;
053    import com.google.gwt.core.client.JsonUtils;
054    import com.google.gwt.dom.client.Element;
055    import com.google.gwt.editor.client.IsEditor;
056    import com.google.gwt.editor.client.LeafValueEditor;
057    import com.google.gwt.editor.client.adapters.TakesValueEditor;
058    import com.google.gwt.event.logical.shared.ValueChangeEvent;
059    import com.google.gwt.event.logical.shared.ValueChangeHandler;
060    import com.google.gwt.event.shared.HandlerRegistration;
061    import com.google.gwt.user.client.Event;
062    import com.google.gwt.user.client.ui.HasEnabled;
063    import com.google.gwt.user.client.ui.HasValue;
064    import com.google.gwt.user.client.ui.UIObject;
065    import com.google.gwt.user.client.ui.Widget;
066    
067    /**
068     *
069     *
070     * @param <T> slider value type
071     *
072     * @see https://github.com/seiyria/bootstrap-slider
073     * @author Xiaodong Sun
074     */
075    public abstract class SliderBase<T> extends Widget implements
076            HasValue<T>, IsEditor<LeafValueEditor<T>>, HasEnabled, HasId,
077            HasResponsiveness, HasAllSlideHandlers<T> {
078    
079        private final TextBox textBox;
080        private FormatterCallback formatterCallback;
081        private LeafValueEditor<T> editor;
082    
083        private final AttributeMixin<SliderBase<T>> attributeMixin = new AttributeMixin<SliderBase<T>>(this);
084    
085        protected SliderBase() {
086            textBox = new TextBox();
087            // now remove the bootstrap styles
088            textBox.removeStyleName(UIObject.getStyleName(textBox.getElement()));
089            setElement((Element) textBox.getElement());
090        }
091    
092        @Override
093        protected void onLoad() {
094            super.onLoad();
095            final JavaScriptObject options = JavaScriptObject.createObject();
096            if (formatterCallback != null) {
097                setFormatterOption(options);
098            }
099            initSlider(getElement(), options);
100            bindSliderEvents(getElement());
101        }
102    
103        @Override
104        protected void onUnload() {
105            super.onUnload();
106            unbindSliderEvents(getElement());
107            sliderCommand(getElement(), SliderCommand.DESTROY);
108        }
109    
110        /**
111         * Sets the id of the slider element when it's created.
112         */
113        @Override
114        public void setId(final String id) {
115            updateSlider(SliderOption.ID, id);
116        }
117    
118        @Override
119        public String getId() {
120            return getStringAttribute(SliderOption.ID);
121        }
122    
123        public double getMin() {
124            return getDoubleAttribute(SliderOption.MIN, 0);
125        }
126    
127        /**
128         * Sets the minimum possible value.
129         *
130         * @param min
131         */
132        public void setMin(final double min) {
133            updateSlider(SliderOption.MIN, min);
134        }
135    
136        public double getMax() {
137            return getDoubleAttribute(SliderOption.MAX, 10);
138        }
139    
140        /**
141         * Sets the maximum possible value.
142         *
143         * @param max
144         */
145        public void setMax(final double max) {
146            updateSlider(SliderOption.MAX, max);
147        }
148    
149        public double getStep() {
150            return getDoubleAttribute(SliderOption.STEP, 1);
151        }
152    
153        /**
154         * Sets the increment step.
155         *
156         * @param step
157         */
158        public void setStep(final double step) {
159            updateSlider(SliderOption.STEP, step);
160        }
161    
162        public double getPrecision() {
163            return getDoubleAttribute(SliderOption.PRECISION, 0);
164        }
165    
166        /**
167         * Sets the number of digits shown after the decimal.<br>
168         * <br>
169         * Defaults to the number of digits after the decimal of step value.
170         *
171         * @param precision
172         */
173        public void setPrecision(final double precision) {
174            updateSlider(SliderOption.PRECISION, precision);
175        }
176    
177        public OrientationType getOrientation() {
178            return getEnumAttribute(SliderOption.ORIENTATION, OrientationType.class, OrientationType.HORIZONTAL);
179        }
180    
181        /**
182         * Sets the orientation.
183         *
184         * @param orientation
185         * @see OrientationType
186         */
187        public void setOrientation(final OrientationType orientation) {
188            updateSlider(SliderOption.ORIENTATION, orientation.getType());
189        }
190    
191        protected boolean isRange() {
192            return getBooleanAttribute(SliderOption.RANGE, false);
193        }
194    
195        /**
196         * Make range slider if set to <code>true</code>. If initial value is scalar,
197         * max will be used for second value.
198         *
199         * @param range
200         */
201        protected void setRange(final boolean range) {
202            updateSlider(SliderOption.RANGE, range);
203        }
204    
205        public SelectionType getSelection() {
206            return getEnumAttribute(SliderOption.SELECTION, SelectionType.class, SelectionType.BEFORE);
207        }
208    
209        /**
210         * Sets the selection type.
211         *
212         * @param selection
213         * @see SelectionType
214         */
215        public void setSelection(final SelectionType selection) {
216            updateSlider(SliderOption.SELECTION, selection.getType());
217        }
218    
219        public TooltipType getTooltip() {
220            return getEnumAttribute(SliderOption.TOOLTIP, TooltipType.class, TooltipType.SHOW);
221        }
222    
223        /**
224         * Sets the tool-tip type.
225         *
226         * @param tooltip
227         * @see TooltipType
228         */
229        public void setTooltip(final TooltipType tooltip) {
230            updateSlider(SliderOption.TOOLTIP, tooltip.getType());
231        }
232    
233        public boolean isTooltipSplit() {
234            return getBooleanAttribute(SliderOption.TOOLTIP_SPLIT, false);
235        }
236    
237        /**
238         * Show one too-tip if set to <code>false</code>, otherwise
239         * show two tool-tips one for each handler.
240         *
241         * @param tooltipSplit
242         */
243        public void setTooltipSplit(final boolean tooltipSplit) {
244            updateSlider(SliderOption.TOOLTIP_SPLIT, tooltipSplit);
245        }
246    
247        public HandleType getHandle() {
248            return getEnumAttribute(SliderOption.HANDLE, HandleType.class, HandleType.ROUND);
249        }
250    
251        /**
252         * Sets the handle shape.
253         *
254         * @param handle
255         * @see HandleType
256         */
257        public void setHandle(final HandleType handle) {
258            updateSlider(SliderOption.HANDLE, handle.getType());
259        }
260    
261        public boolean isReversed() {
262            return getBooleanAttribute(SliderOption.REVERSED, false);
263        }
264    
265        /**
266         * Sets whether or not the slider should be reversed.
267         *
268         * @param reversed
269         */
270        public void setReversed(final boolean reversed) {
271            updateSlider(SliderOption.REVERSED, reversed);
272        }
273    
274        @Override
275        public boolean isEnabled() {
276            if (isAttached()) {
277                return isEnabled(getElement());
278            }
279            return getBooleanAttribute(SliderOption.ENABLED, true);
280        }
281    
282        @Override
283        public void setEnabled(final boolean enabled) {
284            if (isAttached()) {
285                if (enabled) {
286                    sliderCommand(getElement(), SliderCommand.ENABLE);
287                } else {
288                    sliderCommand(getElement(), SliderCommand.DISABLE);
289                }
290            } else {
291                updateSlider(SliderOption.ENABLED, enabled);
292            }
293        }
294    
295        /**
296         * Sets the formatter callback.
297         *
298         * @param formatterCallback
299         */
300        public void setFormatter(final FormatterCallback formatterCallback) {
301            this.formatterCallback = formatterCallback;
302            if (isAttached()) {
303                setFormatter(getElement());
304                refresh();
305            }
306        }
307    
308        private String formatter(final double value) {
309            if (formatterCallback != null)
310                return formatterCallback.formatTooltip(value);
311            return Double.toString(value);
312        }
313    
314        public boolean isNaturalArrowKeys() {
315            return getBooleanAttribute(SliderOption.NATURAL_ARROW_KEYS, false);
316        }
317    
318        /**
319         * The natural order is used for the arrow keys. Arrow up select the
320         * upper slider value for vertical sliders, arrow right the righter
321         * slider value for a horizontal slider ; no matter if the slider
322         * was reversed or not.<br>
323         * <br>
324         * By default the arrow keys are oriented by arrow up/right to the
325         * higher slider value, arrow down/left to the lower slider value.
326         *
327         * @param naturalArrowKeys
328         */
329        public void setNaturalArrowKeys(final boolean naturalArrowKeys) {
330            updateSlider(SliderOption.NATURAL_ARROW_KEYS, naturalArrowKeys);
331        }
332    
333        public List<Double> getTicks() {
334            return getNumberArrayAttribute(SliderOption.TICKS, Collections.<Double>emptyList());
335        }
336    
337        /**
338         * Sets the values of ticks. Tick marks are indicators to denote
339         * special values in the range.<br>
340         * <br>
341         * This option overwrites min and max options.
342         *
343         * @param ticks
344         */
345        public void setTicks(final List<Double> ticks) {
346            updateSliderForNumberArray(SliderOption.TICKS, ticks);
347        }
348    
349        public List<String> getTicksLabels() {
350            return getStringArrayAttribute(SliderOption.TICKS_LABELS, Collections.<String>emptyList());
351        }
352    
353        /**
354         * Sets the labels below the tick marks.<br>
355         * <br>
356         * Accepts HTML input.
357         *
358         * @param ticksLabels
359         */
360        public void setTicksLabels(final List<String> ticksLabels) {
361            updateSliderForStringArray(SliderOption.TICKS_LABELS, ticksLabels);
362        }
363    
364        public double getTicksSnapBounds() {
365            return getDoubleAttribute(SliderOption.TICKS_SNAP_BOUNDS, 0);
366        }
367    
368        /**
369         * Sets the snap bounds of a tick. Snaps to the tick if value
370         * is within these bounds.
371         *
372         * @param ticksSnapBounds
373         */
374        public void setTicksSnapBounds(final double ticksSnapBounds) {
375            updateSlider(SliderOption.TICKS_SNAP_BOUNDS, ticksSnapBounds);
376        }
377    
378        public ScaleType getScale() {
379            return getEnumAttribute(SliderOption.SCALE, ScaleType.class, ScaleType.LINEAR);
380        }
381    
382        /**
383         * Sets the slider scale type.
384         *
385         * @param scale
386         * @see ScaleType
387         */
388        public void setScale(final ScaleType scale) {
389            updateSlider(SliderOption.SCALE, scale.getType());
390        }
391    
392        @Override
393        public void setVisible(final boolean visible) {
394            if (isAttached()) {
395                Element elem = getElement().getPreviousSiblingElement();
396                if (elem != null) {
397                    setVisible(elem, visible);
398                    return;
399                }
400            }
401            super.setVisible(visible);
402        }
403    
404        @Override
405        public boolean isVisible() {
406            if (isAttached()) {
407                Element elem = getElement().getPreviousSiblingElement();
408                if (elem != null) {
409                    return isVisible(elem);
410                }
411            }
412            return isVisible();
413        }
414    
415        @Override
416        public void setVisibleOn(final DeviceSize deviceSize) {
417            StyleHelper.setVisibleOn(this, deviceSize);
418        }
419    
420        @Override
421        public void setHiddenOn(final DeviceSize deviceSize) {
422            StyleHelper.setHiddenOn(this, deviceSize);
423        }
424    
425        @Override
426        public void setValue(final T value) {
427            setValue(value, false);
428        }
429    
430        @Override
431        public void setValue(final T value, final boolean fireEvents) {
432    
433            T oldValue = fireEvents ? getValue() : null;
434    
435            if (isAttached()) {
436                setValue(getElement(), value);
437            } else {
438                String attrVal = (value == null) ? null : value.toString();
439                attributeMixin.setAttribute(SliderOption.VALUE.getDataAttribute(), attrVal);
440            }
441    
442            if (fireEvents) {
443                T newValue = getValue();
444                ValueChangeEvent.fireIfNotEqual(this, oldValue, newValue);
445            }
446        }
447    
448        /**
449         * Sets the given value to the slider. This method is only relevant if the
450         * slider has been initialized and it will NOT fire the <b>slide</b> event.
451         *
452         * @param e
453         * @param value
454         */
455        protected abstract void setValue(Element e, T value);
456    
457        @Override
458        public T getValue() {
459            if (isAttached()) {
460                return getValue(getElement());
461            }
462            String attrVal = attributeMixin.getAttribute(SliderOption.VALUE.getDataAttribute());
463            return convertValue(attrVal);
464        }
465    
466        /**
467         * Returns the value by invoking the JSNI <strong>getValue</strong> command.
468         *
469         * @param e
470         * @return
471         */
472        protected abstract T getValue(Element e);
473    
474        /**
475         * Converts the value of the {@link SliderOption.VALUE} attribute to the
476         * slider value.
477         *
478         * @param value
479         * @return
480         */
481        protected abstract T convertValue(String value);
482    
483        /**
484         * Toggles the slider between enabled and disabled.
485         */
486        public void toggle() {
487            if (isAttached()) {
488                sliderCommand(getElement(), SliderCommand.TOGGLE);
489            } else {
490                setEnabled(!isEnabled());
491            }
492        }
493    
494        /**
495         * Refreshes the current slider. This method does nothing if the slider has
496         * not been initialized.
497         */
498        public void refresh() {
499            if (isAttached()) {
500                refreshWorkaround(getElement());
501                sliderCommand(getElement(), SliderCommand.REFEESH);
502            }
503        }
504    
505        /**
506         * Renders the tool-tip again, after initialization. Useful in situations
507         * when the slider and tool-tip are initially hidden.
508         */
509        public void relayout() {
510            if (isAttached()) {
511                sliderCommand(getElement(), SliderCommand.RELAYOUT);
512            }
513        }
514    
515        @Override
516        public LeafValueEditor<T> asEditor() {
517            if (editor == null) {
518                editor = TakesValueEditor.of(this);
519            }
520            return editor;
521        }
522    
523        @Override
524        public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<T> handler) {
525            return addHandler(handler, ValueChangeEvent.getType());
526        }
527    
528        @Override
529        public HandlerRegistration addSlideHandler(final SlideHandler<T> handler) {
530            return addHandler(handler, SlideEvent.getType());
531        }
532    
533        @Override
534        public HandlerRegistration addSlideStartHandler(final SlideStartHandler<T> handler) {
535            return addHandler(handler, SlideStartEvent.getType());
536        }
537    
538        @Override
539        public HandlerRegistration addSlideStopHandler(final SlideStopHandler<T> handler) {
540            return addHandler(handler, SlideStopEvent.getType());
541        }
542    
543        @Override
544        public HandlerRegistration addSlideEnabledHandler(final SlideEnabledHandler handler) {
545            return addHandler(handler, SlideEnabledEvent.getType());
546        }
547    
548        @Override
549        public HandlerRegistration addSlideDisabledHandler(final SlideDisabledHandler handler) {
550            return addHandler(handler, SlideDisabledEvent.getType());
551        }
552    
553        private void updateSlider(SliderOption option, String value) {
554            if (isAttached()) {
555                setAttribute(getElement(), option.getName(), value);
556                refresh();
557            } else {
558                attributeMixin.setAttribute(option.getDataAttribute(), value);
559            }
560        }
561    
562        private void updateSlider(SliderOption option, boolean value) {
563            if (isAttached()) {
564                setAttribute(getElement(), option.getName(), value);
565                refresh();
566            } else {
567                attributeMixin.setAttribute(option.getDataAttribute(), Boolean.toString(value));
568            }
569        }
570    
571        private void updateSlider(SliderOption option, double value) {
572            if (isAttached()) {
573                setAttribute(getElement(), option.getName(), value);
574                refresh();
575            } else {
576                attributeMixin.setAttribute(option.getDataAttribute(), Double.toString(value));
577            }
578        }
579    
580        private void updateSliderForNumberArray(SliderOption option, List<Double> value) {
581            JsArrayNumber array = JavaScriptObject.createArray().cast();
582            for (Double val : value) {
583                array.push(val);
584            }
585            if (isAttached()) {
586                setAttribute(getElement(), option.getName(), array);
587                refresh();
588            } else {
589                String arrayStr = JsonUtils.stringify(array);
590                attributeMixin.setAttribute(option.getDataAttribute(), arrayStr);
591            }
592        }
593    
594        private void updateSliderForStringArray(SliderOption option, List<String> value) {
595            JsArrayString array = JavaScriptObject.createArray().cast();
596            for (String val : value) {
597                array.push(val);
598            }
599            if (isAttached()) {
600                setAttribute(getElement(), option.getName(), array);
601                refresh();
602            } else {
603                String arrayStr = JsonUtils.stringify(array);
604                attributeMixin.setAttribute(option.getDataAttribute(), arrayStr);
605            }
606        }
607    
608        private String getStringAttribute(SliderOption option) {
609            if (isAttached()) {
610                return getStringAttribute(getElement(), option.getName());
611            }
612            return attributeMixin.getAttribute(option.getDataAttribute());
613        }
614    
615        private boolean getBooleanAttribute(SliderOption option, boolean defaultValue) {
616            if (isAttached()) {
617                return getBooleanAttribute(getElement(), option.getName());
618            }
619            String value = attributeMixin.getAttribute(option.getDataAttribute());
620            if (value != null && !value.isEmpty()) {
621                return Boolean.valueOf(value);
622            }
623            return defaultValue;
624        }
625    
626        private double getDoubleAttribute(SliderOption option, double defaultValue) {
627            if (isAttached()) {
628                return getDoubleAttribute(getElement(), option.getName());
629            }
630            String value = attributeMixin.getAttribute(option.getDataAttribute());
631            if (value != null && !value.isEmpty()) {
632                return Double.valueOf(value);
633            }
634            return defaultValue;
635        }
636    
637        private <E extends Enum<E>> E getEnumAttribute(SliderOption option, Class<E> clazz, E defaultValue) {
638            String value;
639            if (isAttached()) {
640                value = getStringAttribute(getElement(), option.getName());
641            } else {
642                value = attributeMixin.getAttribute(option.getDataAttribute());
643            }
644            try {
645                return Enum.valueOf(clazz, value);
646            } catch (Throwable e) {
647                return defaultValue;
648            }
649        }
650    
651        private List<Double> getNumberArrayAttribute(SliderOption option, List<Double> defaultValue) {
652    
653            // Get array attribute
654            JsArrayNumber array = null;
655            if (isAttached()) {
656                array = getNumberArrayAttribute(getElement(), option.getName());
657            } else {
658                String value = attributeMixin.getAttribute(option.getDataAttribute());
659                if (value != null && !value.isEmpty()) {
660                    array = JsonUtils.safeEval(value);
661                }
662            }
663    
664            // Attribute not set
665            if (array == null) {
666                return defaultValue;
667            }
668    
669            // Put array to list
670            List<Double> list = new ArrayList<Double>(array.length());
671            for (int i = 0; i < array.length(); i++) {
672                list.add(array.get(i));
673            }
674            return list;
675        }
676    
677        private List<String> getStringArrayAttribute(SliderOption option, List<String> defaultValue) {
678    
679            // Get array attribute
680            JsArrayString array = null;
681            if (isAttached()) {
682                array = getStringArrayAttribute(getElement(), option.getName());
683            } else {
684                String value = attributeMixin.getAttribute(option.getDataAttribute());
685                if (value != null && !value.isEmpty()) {
686                    array = JsonUtils.safeEval(value);
687                }
688            }
689    
690            // Attribute not set
691            if (array == null) {
692                return defaultValue;
693            }
694    
695            // Put array to list
696            List<String> list = new ArrayList<String>(array.length());
697            for (int i = 0; i < array.length(); i++) {
698                list.add(array.get(i));
699            }
700            return list;
701        }
702    
703        private native void initSlider(Element e, JavaScriptObject options) /*-{
704            $wnd.jQuery(e).slider(options);
705        }-*/;
706    
707        /**
708         * Called when a {@link SlideEvent} is fired.
709         *
710         * @param event the native event
711         */
712        protected abstract void onSlide(final Event event);
713    
714        /**
715         * Fires a {@link SlideEvent} event.
716         *
717         * @param value the new slide value
718         */
719        protected void fireSlideEvent(final T value) {
720            SlideEvent.fire(this, value);
721        }
722    
723        /**
724         * Called when a {@link SlideStartEvent} is fired.
725         *
726         * @param event the native event
727         */
728        protected abstract void onSlideStart(final Event event);
729    
730        /**
731         * Fires a {@link SlideStartEvent} event.
732         *
733         * @param value the new slide value
734         */
735        protected void fireSlideStartEvent(final T value) {
736            SlideStartEvent.fire(this, value);
737        }
738    
739        /**
740         * Called when a {@link SlideStopEvent} is fired.
741         *
742         * @param event the native event
743         */
744        protected abstract void onSlideStop(final Event event);
745    
746        /**
747         * Fires a {@link SlideStopEvent} event.
748         *
749         * @param value the new slide value
750         */
751        protected void fireSlideStopEvent(final T value) {
752            SlideStopEvent.fire(this, value);
753        }
754    
755        /**
756         * Called when a {@link ValueChangeEvent} is fired.
757         *
758         * @param event the native event
759         */
760        protected abstract void onSlideChange(final Event event);
761    
762        /**
763         * Fires a {@link ValueChangeEvent} event.
764         *
765         * @param value the new slide value
766         */
767        protected void fireChangeEvent(final T value) {
768            ValueChangeEvent.fire(this, value);
769        }
770    
771        /**
772         * Binds the slider events.
773         *
774         * @param e
775         */
776        private native void bindSliderEvents(Element e) /*-{
777            var slider = this;
778            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_EVENT, function(event) {
779                slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::onSlide(Lcom/google/gwt/user/client/Event;)(event);
780            });
781            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_START_EVENT, function(event) {
782                slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::onSlideStart(Lcom/google/gwt/user/client/Event;)(event);
783            });
784            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_STOP_EVENT, function(event) {
785                slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::onSlideStop(Lcom/google/gwt/user/client/Event;)(event);
786            });
787            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_CHANGE_EVENT, function(event) {
788                slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::onSlideChange(Lcom/google/gwt/user/client/Event;)(event);
789            });
790            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_ENABLED_EVENT, function(event) {
791                @org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideEnabledEvent::fire(Lorg/gwtbootstrap3/extras/slider/client/ui/base/event/HasSlideEnabledHandlers;)(slider);
792            });
793            $wnd.jQuery(e).on(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_DISABLED_EVENT, function(event) {
794                @org.gwtbootstrap3.extras.slider.client.ui.base.event.SlideDisabledEvent::fire(Lorg/gwtbootstrap3/extras/slider/client/ui/base/event/HasSlideDisabledHandlers;)(slider);
795            });
796        }-*/;
797    
798        /**
799         * Unbinds the slider events.
800         *
801         * @param e
802         */
803        private native void unbindSliderEvents(Element e) /*-{
804            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_EVENT);
805            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_START_EVENT);
806            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_STOP_EVENT);
807            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_CHANGE_EVENT);
808            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_ENABLED_EVENT);
809            $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.slider.client.ui.base.event.HasAllSlideHandlers::SLIDE_DISABLED_EVENT);
810        }-*/;
811    
812        private native boolean isEnabled(Element e) /*-{
813            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::IS_ENABLED);
814        }-*/;
815    
816        private native void setFormatterOption(JavaScriptObject options) /*-{
817            var slider = this;
818            options.formatter = function(value) {
819                return slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::formatter(D)(value);
820            };
821        }-*/;
822    
823        private native void setFormatter(Element e) /*-{
824            var slider = this;
825            var attr = @org.gwtbootstrap3.extras.slider.client.ui.base.SliderOption::FORMATTER;
826            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, function(value) {
827                return slider.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderBase::formatter(D)(value);
828            });
829        }-*/;
830    
831        /**
832         * FIXME: This is a workaround for the refresh command, since it is buggy in
833         * the current version (4.5.6). After executing this command, the slider
834         * becomes consistently a range slider with 2 handles. This should be
835         * removed once the bug is fixed in a future version.
836         *
837         * @see https://github.com/seiyria/bootstrap-slider/issues/306
838         *
839         * @param e
840         */
841        private native void refreshWorkaround(Element e) /*-{
842            var value = $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_VALUE);
843            var option = @org.gwtbootstrap3.extras.slider.client.ui.base.SliderOption::VALUE;
844            var attr = option.@org.gwtbootstrap3.extras.slider.client.ui.base.SliderOption::getName()();
845            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
846        }-*/;
847    
848        private native void sliderCommand(Element e, String cmd) /*-{
849            $wnd.jQuery(e).slider(cmd);
850        }-*/;
851    
852        private native void setAttribute(Element e, String attr, String value) /*-{
853            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
854        }-*/;
855    
856        private native void setAttribute(Element e, String attr, boolean value) /*-{
857            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
858        }-*/;
859    
860        private native void setAttribute(Element e, String attr, double value) /*-{
861            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
862        }-*/;
863    
864        private native void setAttribute(Element e, String attr, JsArrayNumber value) /*-{
865            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
866        }-*/;
867    
868        private native void setAttribute(Element e, String attr, JsArrayString value) /*-{
869            $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::SET_ATTRIBUTE, attr, value);
870        }-*/;
871    
872        private native String getStringAttribute(Element e, String attr) /*-{
873            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_ATTRIBUTE, attr);
874        }-*/;
875    
876        private native boolean getBooleanAttribute(Element e, String attr) /*-{
877            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_ATTRIBUTE, attr);
878        }-*/;
879    
880        private native double getDoubleAttribute(Element e, String attr) /*-{
881            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_ATTRIBUTE, attr);
882        }-*/;
883    
884        private native JsArrayNumber getNumberArrayAttribute(Element e, String attr) /*-{
885            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_ATTRIBUTE, attr);
886        }-*/;
887    
888        private native JsArrayString getStringArrayAttribute(Element e, String attr) /*-{
889            return $wnd.jQuery(e).slider(@org.gwtbootstrap3.extras.slider.client.ui.base.SliderCommand::GET_ATTRIBUTE, attr);
890        }-*/;
891    
892    }