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.datetimepicker.client.ui.base;
017
018import com.github.gwtbootstrap.client.ui.TextBox;
019import com.github.gwtbootstrap.client.ui.base.HasAlternateSize;
020import com.github.gwtbootstrap.client.ui.base.HasId;
021import com.github.gwtbootstrap.client.ui.base.HasPlaceholder;
022import com.github.gwtbootstrap.client.ui.base.HasSize;
023import com.github.gwtbootstrap.client.ui.base.HasStyle;
024import com.github.gwtbootstrap.client.ui.base.HasVisibility;
025import com.github.gwtbootstrap.client.ui.event.HasVisibleHandlers;
026import com.github.gwtbootstrap.client.ui.base.IsResponsive;
027import com.github.gwtbootstrap.client.ui.base.IsSearchQuery;
028import com.github.gwtbootstrap.client.ui.base.PlaceholderHelper;
029import com.github.gwtbootstrap.client.ui.base.ResponsiveHelper;
030import com.github.gwtbootstrap.client.ui.base.SearchQueryStyleHelper;
031import com.github.gwtbootstrap.client.ui.base.SizeHelper;
032import com.github.gwtbootstrap.client.ui.base.Style;
033import com.github.gwtbootstrap.client.ui.base.StyleHelper;
034import com.github.gwtbootstrap.client.ui.constants.AlternateSize;
035import com.github.gwtbootstrap.client.ui.constants.Device;
036import com.github.gwtbootstrap.client.ui.event.HiddenHandler;
037import com.github.gwtbootstrap.client.ui.event.HideEvent;
038import com.github.gwtbootstrap.client.ui.event.HideHandler;
039import com.github.gwtbootstrap.client.ui.event.ShowEvent;
040import com.github.gwtbootstrap.client.ui.event.ShowHandler;
041import com.github.gwtbootstrap.client.ui.event.ShownHandler;
042import com.github.gwtbootstrap.datetimepicker.client.ui.util.LocaleUtil;
043import com.google.gwt.core.client.GWT;
044import com.google.gwt.dom.client.Element;
045import com.google.gwt.editor.client.IsEditor;
046import com.google.gwt.editor.client.adapters.TakesValueEditor;
047import com.google.gwt.event.dom.client.ChangeEvent;
048import com.google.gwt.event.dom.client.ChangeHandler;
049import com.google.gwt.event.dom.client.HasChangeHandlers;
050import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
051import com.google.gwt.event.logical.shared.ValueChangeEvent;
052import com.google.gwt.event.logical.shared.ValueChangeHandler;
053import com.google.gwt.event.shared.HandlerRegistration;
054import com.google.gwt.i18n.client.DateTimeFormat;
055import com.google.gwt.i18n.client.LocaleInfo;
056import com.google.gwt.user.client.Event;
057import com.google.gwt.user.client.ui.HasEnabled;
058import com.google.gwt.user.client.ui.HasValue;
059import com.google.gwt.user.client.ui.ValueBoxBase.TextAlignment;
060import com.google.gwt.user.client.ui.Widget;
061
062import java.util.Date;
063
064/**
065 * Base DateTimePicker component.
066 *
067 * @author Carlos Alexandro Becker
068 * @author ohashi keisuke
069 * @author Alain Penders
070 * @since 2.1.1.0
071 */
072public class DateTimeBoxBase
073        extends Widget implements HasValue<Date>,HasEnabled, HasValueChangeHandlers<Date>, HasVisibility,
074        HasChangeHandlers, HasVisibleHandlers,
075        HasAllDateTimePickerHandlers, IsEditor<TakesValueEditor<Date>>, HasPlaceholder, HasAlternateSize, IsSearchQuery, HasSize, HasId, IsResponsive , HasStyle {
076
077    private final TextBox box;
078    private String format;
079    private String language;
080    private DateTimeFormat dtf;
081    private TakesValueEditor<Date> editor;
082
083    /** placeholderHelper */
084    private PlaceholderHelper placeholderHelper = GWT.create(PlaceholderHelper.class);
085    private boolean autoclose;
086    private int minuteStep = 5;
087    private boolean todayButton;
088    private boolean highlightToday;
089    private String minViewMode = ViewMode.HOUR.name().toLowerCase();
090    private String startViewMode = ViewMode.MONTH.name().toLowerCase();
091    private String maxViewMode = ViewMode.DECADE.name().toLowerCase();
092    private PickerPosition pickerPosition = PickerPosition.BOTTOM_RIGHT;
093    private Element decoratedElement;
094
095    public DateTimeBoxBase() {
096        this.box = new TextBox();
097        this.language = LocaleUtil.getLanguage();
098        setElement(box.getElement());
099        setFormat("yyyy/mm/dd hh:ii");
100        setWeekStart(LocaleInfo.getCurrentLocale().getDateTimeFormatInfo().firstDayOfTheWeek());
101        setValue(new Date());
102    }
103
104    public void setAlignment(TextAlignment align) {
105        box.setAlignment(align);
106    }
107
108    /**
109     * @see com.google.gwt.user.client.ui.ValueBoxBase#isReadOnly()
110     */
111    public boolean isReadOnly() {
112        return box.isReadOnly();
113    }
114
115    /**
116     * @see com.google.gwt.user.client.ui.ValueBoxBase#setReadOnly(boolean)
117     */
118    public void setReadOnly(boolean readonly) {
119        box.setReadOnly(readonly);
120    }
121
122    /**
123     * {@inheritDoc}
124     */
125    @Override
126    public void setFormat(String format) {
127        this.format = format;
128        Date oldValue = getValue();
129        this.dtf = DateTimeFormat.getFormat(dpGlobalFormatToDateTimeFormat(format));
130        if (oldValue != null) {
131            setValue(oldValue);
132        }
133    }
134
135    public void setLanguage(String language) {
136        this.language = language;
137        LocaleUtil.forceLocale(language);
138    }
139
140    /**
141     * Returns the internal instance of textbox element. Use only if know what you are doing.
142     *
143     * @return internal textbox intance.
144     */
145    protected TextBox getBox() {
146        return box;
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public Date getValue() {
154        try {
155            return dtf != null && box.getValue() != null ? dtf.parse(box.getValue()) : null;
156        } catch(Exception e) {
157            return null;
158        }
159    }
160
161    /**
162     * Get un-transformed text
163     * @return text box value
164     */
165    public String getOriginalValue() {
166        return box.getValue();
167    }
168
169    /**
170     * {@inheritDoc}
171     */
172    @Override
173    public void setValue(Date value) {
174        setValue(value, false);
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    @Override
181    public void setValue(Date value, boolean fireEvents) {
182        box.setValue(value != null ? dtf.format(value) : null);
183
184        updateValue(decoratedElement);
185
186        if (fireEvents) {
187            ValueChangeEvent.fire(this, value);
188        }
189    }
190
191    protected native void updateValue(Element e)/*-{
192        if($wnd.jQuery(e).data('datetimepicker')) {
193            $wnd.jQuery(e).data('datetimepicker').update();
194        }
195    }-*/;
196
197    /**
198     * {@inheritDoc}
199     */
200    @Override
201    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Date> dateValueChangeHandler) {
202        return addHandler(dateValueChangeHandler, ValueChangeEvent.getType());
203    }
204
205    /**
206     * {@inheritDoc}
207     */
208    @Override
209    protected void onLoad() {
210        super.onLoad();
211        configure();
212    }
213
214    @Override
215    protected void onUnload() {
216        super.onUnload();
217        execute("remove");
218    }
219
220    /**
221     * Configure the elements for a specific widget.
222     * Use only if you know what you are doing.
223     *
224     * @param w: the widget to configure.
225     */
226    protected void configure(Widget w) {
227        this.decoratedElement = w.getElement();
228        w.getElement().setAttribute("data-date-format", format);
229        w.getElement().setAttribute("data-date-language", language);
230        configure(w.getElement(), autoclose, minuteStep, todayButton, highlightToday,
231                minViewMode, startViewMode, maxViewMode, pickerPosition.getValue());
232    }
233
234    /**
235     * dateChange event handler.
236     */
237    public void onChange() {
238        ValueChangeEvent.fire(this, getValue());
239    }
240
241    public void onShow(Event e) {
242        fireEvent(new ShowEvent(e));
243    }
244
245    public void onHide(Event e) {
246        fireEvent(new HideEvent(e));
247    }
248    public void reconfigure() {
249        removeDataIfExists(getElement());
250        configure();
251    }
252
253    /**
254     * configure this datetimepicker.
255     */
256    protected void configure() {
257        configure(this);
258    }
259
260    protected native void removeDataIfExists(Element e) /*-{
261        var $that = $wnd.jQuery(e);
262        if($that.data('datetimepicker')) {
263            console.log($that.data());
264            $that.removeData('dateFormat');
265            $that.removeData('dateLanguage');
266            $that.removeData('dateWeekstart');
267            $that.removeData('dateStartdate');
268            $that.removeData('dateEnddate');
269            $that.removeData('dateStartView');
270            $that.removeData('datetimepicker');
271            $that.off();
272        }
273    }-*/;
274
275    /**
276     * call jquery datetimepicker plugin in a element.
277     *
278     * @param e: Element that will be transformed in a datetimepicker.
279     * @param autoclose  is autoclose?
280     */
281        @SuppressWarnings("ParameterHidesMemberVariable")
282    protected native void configure(Element e,
283                                         boolean autoclose,
284                                         int minuteStep,
285                                         boolean todayButton,
286                                         boolean highlightToday,
287                                    String minViewMode,
288                                    String startViewMode,
289                                    String maxViewMode,
290                                    String pickerPosition) /*-{
291        var that = this;
292        $wnd.jQuery(e).datetimepicker({
293            autoclose : autoclose,
294            minuteStep : minuteStep,
295            todayBtn : todayButton,
296            todayHighlight : highlightToday,
297            minView : minViewMode,
298            startView : startViewMode,
299            maxView : maxViewMode,
300            pickerPosition: pickerPosition
301        })
302        .on('change' , function() {
303            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onChange()();
304        })
305        .on('changeDate', function () {
306            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onChange()();
307        })
308        .on("show", function (e) {
309            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onShow(Lcom/google/gwt/user/client/Event;)(e);
310        })
311        .on("hide", function (e) {
312            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onHide(Lcom/google/gwt/user/client/Event;)(e);
313        });
314    }-*/;
315
316    private native void execute(Element e, String cmd) /*-{
317        $wnd.jQuery(e).datetimepicker(cmd);
318    }-*/;
319
320    private void execute(String cmd) {
321        execute(getElement(), cmd);
322    }
323
324    /**
325     * {@inheritDoc}
326     */
327    @Override
328    public void show() {
329        execute("show");
330    }
331
332    /**
333     * {@inheritDoc}
334     */
335    @Override
336    public void hide() {
337        execute("hide");
338    }
339
340    /**
341     * {@inheritDoc}
342     */
343    @Override
344    public void toggle() {
345        //TODO 2012/06/21 ohashi keisuke should be support
346        throw new UnsupportedOperationException("not support toggle");
347    }
348
349    /**
350     * {@inheritDoc}
351     */
352    @Override
353    public HandlerRegistration addHideHandler(HideHandler handler) {
354        return addHandler(handler, HideEvent.getType());
355    }
356
357    /**
358     * {@inheritDoc}
359     */
360    @Override
361    public HandlerRegistration addHiddenHandler(HiddenHandler handler) {
362        //TODO 2012/06/21 ohashi keisuke should be support
363        throw new UnsupportedOperationException("not support hidden event");
364    }
365
366    /**
367     * {@inheritDoc}
368     */
369    @Override
370    public HandlerRegistration addShowHandler(ShowHandler handler) {
371        return addHandler(handler, ShowEvent.getType());
372    }
373
374    /**
375     * {@inheritDoc}
376     */
377    @Override
378    public HandlerRegistration addShownHandler(ShownHandler handler) {
379        //TODO 2012/06/21 ohashi keisuke should be support
380        throw new UnsupportedOperationException("not support shown event");
381    }
382
383    /**
384     * {@inheritDoc}
385     */
386    @Override
387    public void setWeekStart(int start) {
388        getElement().setAttribute("data-date-weekstart", start + "");
389    }
390
391    /**
392     * {@inheritDoc}
393     */
394    @Override
395    public void setStartDate(String startDate) {
396        getElement().setAttribute("data-date-startdate", startDate);
397    }
398
399    /**
400     * {@inheritDoc}
401     */
402    @Override
403    public void setStartDate_(Date startDate) {
404        setStartDate(dtf.format(startDate));
405    }
406
407
408    /**
409     * {@inheritDoc}
410     */
411    @Override
412    public void setEndDate(String endDate) {
413        getElement().setAttribute("data-date-enddate", endDate);
414    }
415
416    /**
417     * {@inheritDoc}
418     */
419    @Override
420    public void setEndDate_(Date endDate) {
421        setEndDate(dtf.format(endDate));
422    }
423
424    /**
425     * {@inheritDoc}
426     */
427    @Override
428    public void setAutoClose(boolean autoclose) {
429        this.autoclose = autoclose;
430    }
431
432    /**
433     * {@inheritDoc}
434     */
435    @Override
436    public void setMinView(ViewMode mode) {
437        setMinView(mode.name());
438    }
439
440    /**
441     * {@inheritDoc}
442     */
443    @Override
444    public void setMinView(String mode) {
445        this.minViewMode = mode.toLowerCase();
446    }
447
448
449    /**
450     * {@inheritDoc}
451     */
452    @Override
453    public void setStartView(HasViewMode.ViewMode mode) {
454        setStartView(mode.name());
455    }
456
457    /**
458     * {@inheritDoc}
459     */
460    @Override
461    public void setStartView(String mode) {
462        this.startViewMode = mode.toLowerCase();
463    }
464
465    /**
466     * {@inheritDoc}
467     */
468    @Override
469    public void setMaxView(ViewMode mode) {
470        setMaxView(mode.name());
471    }
472
473    /**
474     * {@inheritDoc}
475     */
476    @Override
477    public void setMaxView(String mode) {
478        this.maxViewMode = mode.toLowerCase();
479    }
480
481
482    /**
483         * Return Editor
484     *
485         * @return editor
486         */
487        @Override
488        public TakesValueEditor<Date> asEditor() {
489                if(editor == null){
490                        editor = TakesValueEditor.of(this);
491                }
492                return editor;
493        }
494
495    @Override
496    public HandlerRegistration addChangeHandler(ChangeHandler handler) {
497        return addHandler(handler, ChangeEvent.getType());
498    }
499
500    /**
501     * {@inheritDoc}
502     */
503    @Override
504    public void setPlaceholder(String placeholder) {
505        placeholderHelper.setPlaceholer(getElement(), placeholder);
506    }
507
508    /**
509     * {@inheritDoc}
510     */
511    @Override
512    public String getPlaceholder() {
513        return placeholderHelper.getPlaceholder(getElement());
514    }
515
516    /**
517     * {@inheritDoc}
518     */
519    @Override
520    public void setSearchQuery(boolean searchQuery) {
521        SearchQueryStyleHelper.setSearchQuery(this, searchQuery);
522    }
523
524    /**
525     * {@inheritDoc}
526     */
527    @Override
528    public boolean isSearchQuery() {
529        return SearchQueryStyleHelper.isSearchQuery(this);
530    }
531
532    /**
533     * {@inheritDoc}
534     */
535    @Override
536    public void setAlternateSize(AlternateSize size) {
537        StyleHelper.changeStyle(this, size, AlternateSize.class);
538    }
539
540    /**
541     * {@inheritDoc}
542     */
543    @Override
544    public void setSize(int size) {
545        SizeHelper.setSize(this, size);
546    }
547
548    /**
549     * {@inheritDoc}
550     */
551    @Override
552    public String getId() {
553        return getElement().getId();
554    }
555
556    /**
557     * {@inheritDoc}
558     */
559    @Override
560    public void setId(String id) {
561        getElement().setId(id);
562    }
563
564    /**
565     * {@inheritDoc}
566     */
567    @Override
568    public void setShowOn(Device device) {
569        ResponsiveHelper.setShowOn(this, device);
570    }
571
572    /**
573     * {@inheritDoc}
574     */
575    @Override
576    public void setHideOn(Device device) {
577        ResponsiveHelper.setHideOn(this, device);
578
579    }
580
581    /**
582     * {@inheritDoc}
583     */
584    @Override
585    public void setStyle(Style style) {
586        StyleHelper.setStyle(this, style);
587    }
588
589    /**
590     * {@inheritDoc}
591     */
592    @Override
593    public void addStyle(Style style) {
594        StyleHelper.addStyle(this, style);
595    }
596
597    /**
598     * {@inheritDoc}
599     */
600    @Override
601    public void removeStyle(Style style) {
602        StyleHelper.removeStyle(this, style);
603
604    }
605
606    /**
607     * {@inheritDoc}
608     */
609    @Override
610    public boolean isEnabled() {
611        return false;
612    }
613
614    /**
615     * {@inheritDoc}
616     */
617    @Override
618    public void setEnabled(boolean enabled) {
619        box.setEnabled(enabled);
620    }
621
622
623        @Override
624        public void setDaysOfWeekDisabled(String value) {
625                getElement().setAttribute("date-days-of-week-disabled", value);
626        }
627
628
629        @Override
630        public void setMinuteStep(int minutes) {
631//              getElement().setAttribute("date-days-of-week-disabled", value);
632                this.minuteStep = minutes;
633        }
634
635
636        @Override
637        public void setShowTodayButton(boolean show) {
638                this.todayButton = show;
639        }
640
641
642        @Override
643        public void setHighlightToday(boolean highlight) {
644                this.highlightToday = highlight;
645        }
646
647    public PickerPosition getPickerPosition() {
648        return pickerPosition;
649    }
650
651    public void setPickerPosition(PickerPosition pickerPosition) {
652        this.pickerPosition = pickerPosition;
653    }
654
655    private String dpGlobalFormatToDateTimeFormat(String dpGlobalFormat)
656    {
657        if(dpGlobalFormat == null || dpGlobalFormat.length() == 0)
658            return "";
659
660        char current;
661        char last = dpGlobalFormat.charAt(0);
662        int count = 1;
663        String out = "";
664
665        for(int index = 1; index < dpGlobalFormat.length(); index++)
666        {
667            current = dpGlobalFormat.charAt(index);
668
669            if(current == last)
670            {
671                count++;
672                continue;
673            }
674
675            out += processToken(last, count);
676
677            last = current;
678            count = 1;
679        }
680
681        out += processToken(last, count);
682
683        return out;
684    }
685
686    private String processToken(char token, int count)
687    {
688        if (token == 'y') {
689            if (count == 2)
690                return "yy";
691            if(count == 4)
692                return "yyyy";
693        }
694        else if(token == 'm') {
695            if(count == 1)
696                return "M";
697            if(count == 2)
698                return "MM";
699        }
700        else if(token == 'M') {
701            if(count == 1)
702                return "MMM";
703            if(count == 2)
704                return "MMMM";
705        }
706        else if(token == 'h') {
707            token = 'H';
708        }
709        else if(token == 'i') {
710            token = 'm';
711        }
712
713        String out = "";
714        for(int i=0; i<count; i++)
715            out += token;
716
717        return out;
718
719        // TODO: Support PHP format so we can do more complex formatting
720    }
721
722    /*
723    DateTimeFormat
724
725    G   era designator  Text    AD
726    y   year    Number  1996
727    M   month in year   Text or Number  July (or) 07
728    d   day in month    Number  10
729    h   hour in am/pm (1-12)    Number  12
730    H   hour in day (0-23)      Number  0
731    m   minute in hour  Number  30
732    s   second in minute        Number  55
733    S   fractional second       Number  978
734    E   day of week     Text    Tuesday
735    a   am/pm marker    Text    PM
736    k   hour in day (1-24)      Number  24
737    K   hour in am/pm (0-11)    Number  0
738    z   time zone       Text    Pacific Standard Time
739    Z   time zone (RFC 822)     Number  -0800
740    v   time zone (generic)     Text    Pacific Time
741    '   escape for text Delimiter       'Date='
742    ''  single quote    Literal 'o''clock'
743     */
744}