001package org.gwtbootstrap3.extras.datetimepicker.client.ui.base;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2016 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
023import java.util.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import org.gwtbootstrap3.client.shared.event.HideEvent;
029import org.gwtbootstrap3.client.shared.event.HideHandler;
030import org.gwtbootstrap3.client.shared.event.ShowEvent;
031import org.gwtbootstrap3.client.shared.event.ShowHandler;
032import org.gwtbootstrap3.client.ui.TextBox;
033import org.gwtbootstrap3.client.ui.base.HasId;
034import org.gwtbootstrap3.client.ui.base.HasPlaceholder;
035import org.gwtbootstrap3.client.ui.base.HasReadOnly;
036import org.gwtbootstrap3.client.ui.base.HasResponsiveness;
037import org.gwtbootstrap3.client.ui.base.ValueBoxBase;
038import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
039import org.gwtbootstrap3.client.ui.base.mixin.BlankValidatorMixin;
040import org.gwtbootstrap3.client.ui.base.mixin.ErrorHandlerMixin;
041import org.gwtbootstrap3.client.ui.constants.DeviceSize;
042import org.gwtbootstrap3.client.ui.form.error.ErrorHandler;
043import org.gwtbootstrap3.client.ui.form.error.ErrorHandlerType;
044import org.gwtbootstrap3.client.ui.form.error.HasErrorHandler;
045import org.gwtbootstrap3.client.ui.form.validator.HasBlankValidator;
046import org.gwtbootstrap3.client.ui.form.validator.HasValidators;
047import org.gwtbootstrap3.client.ui.form.validator.ValidationChangedEvent.ValidationChangedHandler;
048import org.gwtbootstrap3.client.ui.form.validator.Validator;
049import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerDayOfWeek;
050import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerFormatViewType;
051import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerLanguage;
052import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerPosition;
053import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.DateTimePickerView;
054import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasAutoClose;
055import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasDateTimePickerHandlers;
056import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasDaysOfWeekDisabled;
057import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasEndDate;
058import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasForceParse;
059import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasFormat;
060import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasHighlightToday;
061import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasKeyboardNavigation;
062import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasLanguage;
063import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasMaxView;
064import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasMinView;
065import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasMinuteStep;
066import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasPosition;
067import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasShowMeridian;
068import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasShowTodayButton;
069import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasStartDate;
070import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasStartView;
071import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasViewSelect;
072import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.constants.HasWeekStart;
073import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeDateEvent;
074import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeDateHandler;
075import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeMonthEvent;
076import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeMonthHandler;
077import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeYearEvent;
078import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.ChangeYearHandler;
079import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.OutOfRangeEvent;
080import org.gwtbootstrap3.extras.datetimepicker.client.ui.base.events.OutOfRangeHandler;
081
082import com.google.gwt.core.client.ScriptInjector;
083import com.google.gwt.dom.client.Element;
084import com.google.gwt.editor.client.EditorError;
085import com.google.gwt.editor.client.HasEditorErrors;
086import com.google.gwt.editor.client.LeafValueEditor;
087import com.google.gwt.event.dom.client.BlurEvent;
088import com.google.gwt.event.dom.client.BlurHandler;
089import com.google.gwt.event.logical.shared.ValueChangeEvent;
090import com.google.gwt.event.logical.shared.ValueChangeHandler;
091import com.google.gwt.event.shared.HandlerRegistration;
092import com.google.gwt.i18n.client.DateTimeFormat;
093import com.google.gwt.user.client.Event;
094import com.google.gwt.user.client.ui.HasEnabled;
095import com.google.gwt.user.client.ui.HasName;
096import com.google.gwt.user.client.ui.HasValue;
097import com.google.gwt.user.client.ui.HasVisibility;
098import com.google.gwt.user.client.ui.Widget;
099
100/**
101 * @author Joshua Godi
102 * @author Steven Jardine
103 */
104public class DateTimePickerBase extends Widget implements HasEnabled, HasReadOnly, HasId, HasResponsiveness, HasVisibility,
105        HasPlaceholder, HasAutoClose, HasDaysOfWeekDisabled, HasEndDate, HasForceParse, HasFormat, HasHighlightToday,
106        HasKeyboardNavigation, HasMaxView, HasMinuteStep, HasMinView, HasShowMeridian, HasShowTodayButton, HasStartDate,
107        HasStartView, HasViewSelect, HasWeekStart, HasDateTimePickerHandlers, HasLanguage, HasName, HasValue<Date>, HasPosition,
108        LeafValueEditor<Date>, HasEditorErrors<Date>, HasErrorHandler, HasValidators<Date>, HasBlankValidator<Date> {
109
110    static class DatePickerValidatorMixin extends BlankValidatorMixin<DateTimePickerBase, Date> {
111
112        private boolean showing = false;
113
114        public void setShowing(boolean showing) {
115            this.showing = showing;
116        }
117
118        public DatePickerValidatorMixin(DateTimePickerBase inputWidget, ErrorHandler errorHandler) {
119            super(inputWidget, errorHandler);
120        }
121
122        @Override
123        protected com.google.web.bindery.event.shared.HandlerRegistration setupBlurValidation() {
124            return getInputWidget().addDomHandler(new BlurHandler() {
125                @Override
126                public void onBlur(BlurEvent event) {
127                    getInputWidget().validate(!showing && getValidateOnBlur());
128                }
129            }, BlurEvent.getType());
130        }
131
132    }
133
134    // Check http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/client/DateTimeFormat.html
135    // for more information on syntax
136    private static final Map<Character, Character> DATE_TIME_FORMAT_MAP = new HashMap<Character, Character>();
137    static {
138        DATE_TIME_FORMAT_MAP.put('h', 'H'); // 12/24 hours
139        DATE_TIME_FORMAT_MAP.put('H', 'h'); // 12/24 hours
140        DATE_TIME_FORMAT_MAP.put('m', 'M'); // months
141        DATE_TIME_FORMAT_MAP.put('i', 'm'); // minutes
142        DATE_TIME_FORMAT_MAP.put('p', 'a'); // meridian
143        DATE_TIME_FORMAT_MAP.put('P', 'a'); // meridian
144    }
145
146    private final TextBox textBox;
147    private DateTimeFormat dateTimeFormat;
148    private final DateTimeFormat startEndDateFormat = DateTimeFormat.getFormat("yyyy-MM-dd");
149    private LeafValueEditor<Date> editor;
150    private final ErrorHandlerMixin<Date> errorHandlerMixin = new ErrorHandlerMixin<Date>(this);
151    private final DatePickerValidatorMixin validatorMixin = new DatePickerValidatorMixin(this,
152            errorHandlerMixin.getErrorHandler());
153
154    /**
155     * DEFAULT values
156     */
157    private String format = "mm/dd/yyyy hh:ii";
158    private DateTimePickerFormatViewType formatViewType = DateTimePickerFormatViewType.DATE_TIME;
159    private DateTimePickerDayOfWeek weekStart = DateTimePickerDayOfWeek.SUNDAY;
160    private DateTimePickerDayOfWeek[] daysOfWeekDisabled = {};
161    private boolean autoClose = false;
162    private DateTimePickerView startView = DateTimePickerView.MONTH;
163    private DateTimePickerView minView = DateTimePickerView.HOUR;
164    private DateTimePickerView maxView = DateTimePickerView.DECADE;
165    private boolean showTodayButton = false;
166    private boolean highlightToday = false;
167    private boolean keyboardNavigation = true;
168    private boolean forceParse = true;
169    private int minuteStep = 5;
170    private DateTimePickerView viewSelect = DateTimePickerView.HOUR;
171    private boolean showMeridian = false;
172    private DateTimePickerLanguage language = DateTimePickerLanguage.EN;
173    private DateTimePickerPosition position = DateTimePickerPosition.BOTTOM_RIGHT;
174
175    public DateTimePickerBase() {
176        textBox = new TextBox();
177        setElement((Element) textBox.getElement());
178        setFormat(format);
179    }
180
181    public TextBox getTextBox() {
182        return textBox;
183    }
184
185    public void setAlignment(final ValueBoxBase.TextAlignment align) {
186        textBox.setAlignment(align);
187    }
188
189    /** {@inheritDoc} */
190    @Override
191    public void setPlaceholder(final String placeHolder) {
192        textBox.setPlaceholder(placeHolder);
193    }
194
195    /** {@inheritDoc} */
196    @Override
197    public String getPlaceholder() {
198        return textBox.getPlaceholder();
199    }
200
201    public void setReadOnly(final boolean readOnly) {
202        textBox.setReadOnly(readOnly);
203    }
204
205    public boolean isReadOnly() {
206        return textBox.isReadOnly();
207    }
208
209
210    /** {@inheritDoc} */
211    @Override
212    public boolean isEnabled() {
213        return textBox.isEnabled();
214    }
215
216    /** {@inheritDoc} */
217    @Override
218    public void setEnabled(final boolean enabled) {
219        textBox.setEnabled(enabled);
220    }
221
222    /** {@inheritDoc} */
223    @Override
224    public void setId(final String id) {
225        textBox.setId(id);
226    }
227
228    /** {@inheritDoc} */
229    @Override
230    public String getId() {
231        return textBox.getId();
232    }
233
234    /** {@inheritDoc} */
235    @Override
236    public void setName(final String name) {
237        textBox.setName(name);
238    }
239
240    /** {@inheritDoc} */
241    @Override
242    public String getName() {
243        return textBox.getName();
244    }
245
246    /** {@inheritDoc} */
247    @Override
248    public void setVisibleOn(final DeviceSize deviceSize) {
249        StyleHelper.setVisibleOn(this, deviceSize);
250    }
251
252    /** {@inheritDoc} */
253    @Override
254    public void setHiddenOn(final DeviceSize deviceSize) {
255        StyleHelper.setHiddenOn(this, deviceSize);
256    }
257
258    /** {@inheritDoc} */
259    @Override
260    public void setLanguage(final DateTimePickerLanguage language) {
261        this.language = language;
262
263        // Inject the JS for the language
264        if (language.getJs() != null) {
265            ScriptInjector.fromString(language.getJs().getText())
266                    .setWindow(ScriptInjector.TOP_WINDOW).inject();
267        }
268    }
269
270    /** {@inheritDoc} */
271    @Override
272    public DateTimePickerLanguage getLanguage() {
273        return language;
274    }
275
276    /** {@inheritDoc} */
277    @Override
278    public void setPosition(final DateTimePickerPosition position) {
279        this.position = position;
280    }
281
282    /** {@inheritDoc} */
283    @Override
284    public DateTimePickerPosition getPosition() {
285        return position;
286    }
287
288    /**
289     * Call this whenever changing any settings: minView, startView, format, etc. If you are changing
290     * format and date value, the updates must take in such order:
291     * <p/>
292     * 1. DateTimePicker.reload()
293     * 2. DateTimePicker.setValue(newDate); // Date newDate.
294     * <p/>
295     * Otherwise date value is not updated.
296     */
297    public void reload() {
298        configure();
299    }
300
301    public void show() {
302        show(getElement());
303    }
304
305    public void hide() {
306        hide(getElement());
307    }
308
309    /** {@inheritDoc} */
310    @Override
311    public void setAutoClose(final boolean autoClose) {
312        this.autoClose = autoClose;
313    }
314
315    /** {@inheritDoc} */
316    @Override
317    public void onShow(final Event e) {
318        validatorMixin.setShowing(true);
319        fireEvent(new ShowEvent(e));
320    }
321
322    /** {@inheritDoc} */
323    @Override
324    public HandlerRegistration addShowHandler(final ShowHandler showHandler) {
325        return addHandler(showHandler, ShowEvent.getType());
326    }
327
328    /** {@inheritDoc} */
329    @Override
330    public void onHide(final Event e) {
331        validatorMixin.setShowing(false);
332        validate(getValidateOnBlur());
333        fireEvent(new HideEvent(e));
334    }
335
336    /** {@inheritDoc} */
337    @Override
338    public HandlerRegistration addHideHandler(final HideHandler hideHandler) {
339        return addHandler(hideHandler, HideEvent.getType());
340    }
341
342    /** {@inheritDoc} */
343    @Override
344    public void onChangeDate(final Event e) {
345        fireEvent(new ChangeDateEvent(e));
346        ValueChangeEvent.fire(DateTimePickerBase.this, getValue());
347        hide();
348    }
349
350    /** {@inheritDoc} */
351    @Override
352    public HandlerRegistration addChangeDateHandler(final ChangeDateHandler changeDateHandler) {
353        return addHandler(changeDateHandler, ChangeDateEvent.getType());
354    }
355
356    /** {@inheritDoc} */
357    @Override
358    public void onChangeYear(final Event e) {
359        fireEvent(new ChangeYearEvent(e));
360    }
361
362    /** {@inheritDoc} */
363    @Override
364    public HandlerRegistration addChangeYearHandler(final ChangeYearHandler changeYearHandler) {
365        return addHandler(changeYearHandler, ChangeYearEvent.getType());
366    }
367
368    /** {@inheritDoc} */
369    @Override
370    public void onChangeMonth(final Event e) {
371        fireEvent(new ChangeMonthEvent(e));
372    }
373
374    /** {@inheritDoc} */
375    @Override
376    public HandlerRegistration addChangeMonthHandler(final ChangeMonthHandler changeMonthHandler) {
377        return addHandler(changeMonthHandler, ChangeMonthEvent.getType());
378    }
379
380    /** {@inheritDoc} */
381    @Override
382    public void onOutOfRange(final Event e) {
383        fireEvent(new OutOfRangeEvent(e));
384    }
385
386    /** {@inheritDoc} */
387    @Override
388    public HandlerRegistration addOutOfRangeHandler(final OutOfRangeHandler outOfRangeHandler) {
389        return addHandler(outOfRangeHandler, OutOfRangeEvent.getType());
390    }
391
392    /** {@inheritDoc} */
393    @Override
394    public void setDaysOfWeekDisabled(final DateTimePickerDayOfWeek... daysOfWeekDisabled) {
395        setDaysOfWeekDisabled(getElement(), toDaysOfWeekDisabledString(daysOfWeekDisabled));
396    }
397
398    /** {@inheritDoc} */
399    @Override
400    public void setEndDate(final Date endDate) {
401        // Has to be in the format YYYY-MM-DD
402        setEndDate(startEndDateFormat.format(endDate));
403    }
404
405    /** {@inheritDoc} */
406    @Override
407    public void setEndDate(final String endDate) {
408        // Has to be in the format YYYY-MM-DD
409        setEndDate(getElement(), endDate);
410    }
411
412    /** {@inheritDoc} */
413    @Override
414    public void clearEndDate() {
415        setStartDate(getElement(), null);
416    }
417
418    /** {@inheritDoc} */
419    @Override
420    public void setForceParse(final boolean forceParse) {
421        this.forceParse = forceParse;
422    }
423
424    /** {@inheritDoc} */
425    @Override
426    public void setHighlightToday(final boolean highlightToday) {
427        this.highlightToday = highlightToday;
428    }
429
430    /** {@inheritDoc} */
431    @Override
432    public void setHasKeyboardNavigation(final boolean hasKeyboardNavigation) {
433        this.keyboardNavigation = hasKeyboardNavigation;
434    }
435
436    /** {@inheritDoc} */
437    @Override
438    public void setMaxView(final DateTimePickerView dateTimePickerView) {
439        this.maxView = dateTimePickerView;
440    }
441
442    /** {@inheritDoc} */
443    @Override
444    public void setMinView(final DateTimePickerView dateTimePickerView) {
445        this.minView = dateTimePickerView;
446
447        // We keep the view select the same as the min view
448        if (viewSelect != minView) {
449            setViewSelect(dateTimePickerView);
450        }
451    }
452
453    /** {@inheritDoc} */
454    @Override
455    public void setMinuteStep(final int minuteStep) {
456        this.minuteStep = minuteStep;
457    }
458
459    /** {@inheritDoc} */
460    @Override
461    public void setShowMeridian(final boolean showMeridian) {
462        this.showMeridian = showMeridian;
463    }
464
465    /** {@inheritDoc} */
466    @Override
467    public void setShowTodayButton(final boolean showTodayButton) {
468        this.showTodayButton = showTodayButton;
469    }
470
471    /** {@inheritDoc} */
472    @Override
473    public void setStartDate(final Date startDate) {
474        // Has to be in the format YYYY-MM-DD
475        setStartDate(startEndDateFormat.format(startDate));
476    }
477
478    /** {@inheritDoc} */
479    @Override
480    public void setStartDate(final String startDate) {
481        // Has to be in the format YYYY-MM-DD
482        setStartDate(getElement(), startDate);
483    }
484
485    /** {@inheritDoc} */
486    @Override
487    public void clearStartDate() {
488        setStartDate(getElement(), null);
489    }
490
491    /** {@inheritDoc} */
492    @Override
493    public void setStartView(final DateTimePickerView dateTimePickerView) {
494        this.startView = dateTimePickerView;
495    }
496
497    /** {@inheritDoc} */
498    @Override
499    public void setViewSelect(final DateTimePickerView dateTimePickerView) {
500        this.viewSelect = dateTimePickerView;
501
502        // We keep the min view the same as the view select
503        if (viewSelect != minView) {
504            setMinView(dateTimePickerView);
505        }
506    }
507
508    /** {@inheritDoc} */
509    @Override
510    public void setWeekStart(final DateTimePickerDayOfWeek weekStart) {
511        this.weekStart = weekStart;
512    }
513
514    /**
515     * Convert GWT date time format to bootstrap date time format
516     *
517     * @param format date time format using GWT notation
518     * @return date time format using bootstrap notation
519     */
520    private static String toBootstrapDateFormat(final String format) {
521        String bootstrap_format = format;
522
523        // Replace long day name "EEEE" with "DD"
524        bootstrap_format = bootstrap_format.replace("EEEE", "DD");
525        // Replace short day name "EE" with "DD"
526        bootstrap_format = bootstrap_format.replaceAll("E{1,3}", "D");
527        // Replace minutes "m" with "i"
528        bootstrap_format = bootstrap_format.replaceAll("m", "i");
529        // Replace "H" with "h" and vice versa
530        bootstrap_format = bootstrap_format.replaceAll("H", "Q");
531        bootstrap_format = bootstrap_format.replaceAll("h", "H");
532        bootstrap_format = bootstrap_format.replaceAll("Q", "h");
533        // Replace "a" with "P" for AM/PM markers
534        bootstrap_format = bootstrap_format.replaceAll("a", "P");
535
536        // If there are at least 3 Ms there is month name in wording
537        if (bootstrap_format.contains("MMM")) {
538            // Replace long date month "MMMM" with "MM"
539            bootstrap_format = bootstrap_format.replace("MMMM", "MM");
540            // Replace month name "MMM" with "M"
541            bootstrap_format = bootstrap_format.replace("MMM", "M");
542        }
543        else {
544            // Replace month number with leading 0 "MM" with "mm"
545            bootstrap_format = bootstrap_format.replace("MM", "mm");
546            // Replace month number "M" with "m"
547            bootstrap_format = bootstrap_format.replace("M", "m");
548        }
549        if (!bootstrap_format.contains("yy")) {
550            // Replace full year format "y" with "yyyy"
551            bootstrap_format = bootstrap_format.replace("y", "yyyy");
552        }
553
554        return bootstrap_format;
555    }
556
557    /**
558     * Sets format of the date using GWT notation
559     *
560     * @param format date time format in GWT notation
561     */
562    public void setGWTFormat(final String format) {
563        this.format = toBootstrapDateFormat(format);
564
565        // Get the old value
566        final Date oldValue = getValue();
567
568        // Make the new DateTimeFormat
569        this.dateTimeFormat = DateTimeFormat.getFormat(format);
570
571        if (oldValue != null) {
572            setValue(oldValue);
573        }
574    }
575
576    /** {@inheritDoc} */
577    @Override
578    public void setFormat(final String format) {
579        this.format = format;
580
581        // Get the old value
582        final Date oldValue = getValue();
583
584        // Make the new DateTimeFormat
585        setDateTimeFormat(format);
586
587        if (oldValue != null) {
588            setValue(oldValue);
589        }
590    }
591
592    private void setDateTimeFormat(final String format) {
593        final StringBuilder fb = new StringBuilder(format);
594        for (int i = 0; i < fb.length(); i++) {
595            if (DATE_TIME_FORMAT_MAP.containsKey(fb.charAt(i))) {
596                fb.setCharAt(i, DATE_TIME_FORMAT_MAP.get(fb.charAt(i)));
597            }
598        }
599
600        this.dateTimeFormat = DateTimeFormat.getFormat(fb.toString());
601    }
602
603    /**
604     * Sets the format view type.
605     * 
606     * @param formatViewType
607     * @see DateTimePickerFormatViewType
608     */
609    public void setFormatViewType(DateTimePickerFormatViewType formatViewType) {
610        if (formatViewType != null)
611            this.formatViewType = formatViewType;
612    }
613
614    /** {@inheritDoc} */
615    @Override
616    public Date getValue() {
617        try {
618            return dateTimeFormat != null && textBox.getValue() != null ? dateTimeFormat.parse(textBox.getValue()) : null;
619        } catch (final Exception e) {
620            return null;
621        }
622    }
623
624    public String getBaseValue() {
625        return textBox.getValue();
626    }
627
628    /** {@inheritDoc} */
629    @Override
630    public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<Date> dateValueChangeHandler) {
631        textBox.addValueChangeHandler(new ValueChangeHandler<String>() {
632            @Override
633            public void onValueChange(ValueChangeEvent<String> event) {
634                ValueChangeEvent.fire(DateTimePickerBase.this, getValue());
635            }
636        });
637        return addHandler(dateValueChangeHandler, ValueChangeEvent.getType());
638    }
639
640    /** {@inheritDoc} */
641    @Override
642    public void setValue(final Date value) {
643        setValue(value, false);
644    }
645
646    /** {@inheritDoc} */
647    @Override
648    public void setValue(final Date value, final boolean fireEvents) {
649        errorHandlerMixin.clearErrors();
650        textBox.setValue(value != null ? dateTimeFormat.format(value) : null);
651        update(textBox.getElement());
652
653        if (fireEvents) {
654            ValueChangeEvent.fire(DateTimePickerBase.this, value);
655        }
656    }
657
658    /** {@inheritDoc} */
659    @Override
660    protected void onLoad() {
661        super.onLoad();
662        configure();
663    }
664
665    /** {@inheritDoc} */
666    @Override
667    protected void onUnload() {
668        super.onUnload();
669        remove(getElement());
670    }
671
672    protected void configure() {
673        getElement().setAttribute("data-date-format", format);
674
675        // If configuring not for the first time, datetimepicker must be removed first.
676        this.remove(getElement());
677
678        configure(getElement(), format, formatViewType.getValue(), weekStart.getValue(), toDaysOfWeekDisabledString(daysOfWeekDisabled),
679                autoClose, startView.getValue(), minView.getValue(), maxView.getValue(), showTodayButton, highlightToday,
680                keyboardNavigation, forceParse, minuteStep, viewSelect.getValue(), showMeridian, language.getCode(), position.getPosition());
681    }
682
683    protected void execute(final String cmd) {
684        execute(getElement(), cmd);
685    }
686
687    private native void execute(Element e, String cmd) /*-{
688        $wnd.jQuery(e).datetimepicker(cmd);
689    }-*/;
690
691    private native void remove(Element e) /*-{
692        $wnd.jQuery(e).datetimepicker('remove');
693    }-*/;
694
695    private native void show(Element e) /*-{
696        $wnd.jQuery(e).datetimepicker('show');
697    }-*/;
698
699    private native void hide(Element e) /*-{
700        $wnd.jQuery(e).datetimepicker('hide');
701    }-*/;
702
703    private native void update(Element e) /*-{
704        $wnd.jQuery(e).datetimepicker('update');
705    }-*/;
706
707    private native void setStartDate(Element e, String startDate) /*-{
708        $wnd.jQuery(e).datetimepicker('setStartDate', startDate);
709    }-*/;
710
711    private native void setEndDate(Element e, String endDate) /*-{
712        $wnd.jQuery(e).datetimepicker('setEndDate', endDate);
713    }-*/;
714
715    private native void setDaysOfWeekDisabled(Element e, String daysOfWeekDisabled) /*-{
716        $wnd.jQuery(e).datetimepicker('setDaysOfWeekDisabled', daysOfWeekDisabled);
717    }-*/;
718
719    protected native void configure(Element e, String format, String formatViewType, int weekStart,
720            String daysOfWeekDisabled, boolean autoClose, int startView, int minView, int maxView, boolean todayBtn, 
721            boolean highlightToday, boolean keyboardNavigation, boolean forceParse, int minuteStep, int viewSelect,
722            boolean showMeridian, String language, String position) /*-{
723        var that = this;
724        $wnd.jQuery(e).datetimepicker({
725            format: format,
726            formatViewType: formatViewType,
727            language: language,
728            weekStart: weekStart,
729            daysOfWeekDisabled: daysOfWeekDisabled,
730            autoclose: autoClose,
731            startView: startView,
732            minView: minView,
733            maxView: maxView,
734            todayBtn: todayBtn,
735            todayHighlight: highlightToday,
736            keyboardNavigation: keyboardNavigation,
737            forceParse: forceParse,
738            minuteStep: minuteStep,
739            showMeridian: showMeridian,
740            pickerPosition: position
741        })
742            .on('show', function (e) {
743                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onShow(Lcom/google/gwt/user/client/Event;)(e);
744            })
745            .on("hide", function (e) {
746                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onHide(Lcom/google/gwt/user/client/Event;)(e);
747            })
748            .on("changeDate", function (e) {
749                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onChangeDate(Lcom/google/gwt/user/client/Event;)(e);
750            })
751            .on("changeYear", function (e) {
752                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onChangeYear(Lcom/google/gwt/user/client/Event;)(e);
753            })
754            .on("changeMonth", function (e) {
755                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onChangeMonth(Lcom/google/gwt/user/client/Event;)(e);
756            })
757            .on("outOfRange", function (e) {
758                that.@org.gwtbootstrap3.extras.datetimepicker.client.ui.base.DateTimePickerBase::onOutOfRange(Lcom/google/gwt/user/client/Event;)(e);
759            });
760    }-*/;
761
762    protected String toDaysOfWeekDisabledString(final DateTimePickerDayOfWeek... dateTimePickerDayOfWeeks) {
763        this.daysOfWeekDisabled = dateTimePickerDayOfWeeks;
764
765        final StringBuilder builder = new StringBuilder();
766
767        if (dateTimePickerDayOfWeeks != null) {
768            int i = 0;
769            for (final DateTimePickerDayOfWeek dayOfWeek : dateTimePickerDayOfWeeks) {
770                builder.append(dayOfWeek.getValue());
771
772                i++;
773                if (i < dateTimePickerDayOfWeeks.length) {
774                    builder.append(",");
775                }
776            }
777        }
778        return builder.toString();
779    }
780
781    /** {@inheritDoc} */
782    @Override
783    public com.google.web.bindery.event.shared.HandlerRegistration addValidationChangedHandler(ValidationChangedHandler handler) {
784        return validatorMixin.addValidationChangedHandler(handler);
785    }
786
787    /** {@inheritDoc} */
788    @Override
789    public boolean getAllowBlank() {
790        return validatorMixin.getAllowBlank();
791    }
792
793    /** {@inheritDoc} */
794    @Override
795    public void setAllowBlank(boolean allowBlank) {
796        validatorMixin.setAllowBlank(allowBlank);
797    }
798
799    /** {@inheritDoc} */
800    @Override
801    public void addValidator(Validator<Date> validator) {
802        validatorMixin.addValidator(validator);
803    }
804
805    /** {@inheritDoc} */
806    @Override
807    public boolean getValidateOnBlur() {
808        return validatorMixin.getValidateOnBlur();
809    }
810
811    /** {@inheritDoc} */
812    @Override
813    public boolean removeValidator(Validator<Date> validator) {
814        return validatorMixin.removeValidator(validator);
815    }
816
817    /** {@inheritDoc} */
818    @Override
819    public void reset() {
820        validatorMixin.reset();
821    }
822
823    /** {@inheritDoc} */
824    @Override
825    public void setValidateOnBlur(boolean validateOnBlur) {
826        validatorMixin.setValidateOnBlur(validateOnBlur);
827    }
828
829    /** {@inheritDoc} */
830    @Override
831    public void setValidators(@SuppressWarnings("unchecked") Validator<Date>... validators) {
832        validatorMixin.setValidators(validators);
833    }
834
835    /** {@inheritDoc} */
836    @Override
837    public boolean validate() {
838        return validatorMixin.validate();
839    }
840
841    /** {@inheritDoc} */
842    @Override
843    public boolean validate(boolean show) {
844        return validatorMixin.validate(show);
845    }
846
847    /** {@inheritDoc} */
848    @Override
849    public ErrorHandler getErrorHandler() {
850        return errorHandlerMixin.getErrorHandler();
851    }
852
853    /** {@inheritDoc} */
854    @Override
855    public void setErrorHandler(ErrorHandler errorHandler) {
856        errorHandlerMixin.setErrorHandler(errorHandler);
857        validatorMixin.setErrorHandler(errorHandler);
858    }
859
860    /** {@inheritDoc} */
861    @Override
862    public ErrorHandlerType getErrorHandlerType() {
863        return errorHandlerMixin.getErrorHandlerType();
864    }
865
866    /** {@inheritDoc} */
867    @Override
868    public void setErrorHandlerType(ErrorHandlerType errorHandlerType) {
869        errorHandlerMixin.setErrorHandlerType(errorHandlerType);
870    }
871
872    /** {@inheritDoc} */
873    @Override
874    public void showErrors(List<EditorError> errors) {
875        errorHandlerMixin.showErrors(errors);
876    }
877
878}