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.base.IsResponsive;
026import com.github.gwtbootstrap.client.ui.base.IsSearchQuery;
027import com.github.gwtbootstrap.client.ui.base.PlaceholderHelper;
028import com.github.gwtbootstrap.client.ui.base.ResponsiveHelper;
029import com.github.gwtbootstrap.client.ui.base.SearchQueryStyleHelper;
030import com.github.gwtbootstrap.client.ui.base.SizeHelper;
031import com.github.gwtbootstrap.client.ui.base.Style;
032import com.github.gwtbootstrap.client.ui.base.StyleHelper;
033import com.github.gwtbootstrap.client.ui.constants.AlternateSize;
034import com.github.gwtbootstrap.client.ui.constants.Device;
035import com.github.gwtbootstrap.client.ui.event.HasVisibleHandlers;
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        removeDataIfExists(getElement());
219    }
220
221    /**
222     * Configure the elements for a specific widget.
223     * Use only if you know what you are doing.
224     *
225     * @param w: the widget to configure.
226     */
227    protected void configure(Widget w) {
228        this.decoratedElement = w.getElement();
229        w.getElement().setAttribute("data-date-format", format);
230        w.getElement().setAttribute("data-date-language", language);
231        configure(w.getElement(), autoclose, minuteStep, todayButton, highlightToday,
232                minViewMode, startViewMode, maxViewMode, pickerPosition.getValue());
233    }
234
235    /**
236     * dateChange event handler.
237     */
238    public void onChange() {
239        ValueChangeEvent.fire(this, getValue());
240    }
241
242    public void onShow(Event e) {
243        fireEvent(new ShowEvent(e));
244    }
245
246    public void onHide(Event e) {
247        fireEvent(new HideEvent(e));
248    }
249
250    public void reconfigure() {
251        removeDataIfExists(getElement());
252        configure();
253    }
254
255    /**
256     * configure this datetimepicker.
257     */
258    protected void configure() {
259        configure(this);
260    }
261
262    protected native void removeDataIfExists(Element e) /*-{
263        var $that = $wnd.jQuery(e);
264        dtpData = $that.data('datetimepicker');
265        if(dtpData) {
266            picker = dtpData.picker;
267            $that.removeData('dateFormat');
268            $that.removeData('dateLanguage');
269            $that.removeData('dateWeekstart');
270            $that.removeData('dateStartdate');
271            $that.removeData('dateEnddate');
272            $that.removeData('dateStartView');
273            $that.removeData('datetimepicker');
274            picker.remove();
275            $that.off();
276        }
277    }-*/;
278
279    /**
280     * call jquery datetimepicker plugin in a element.
281     *
282     * @param e: Element that will be transformed in a datetimepicker.
283     * @param autoclose  is autoclose?
284     */
285        @SuppressWarnings("ParameterHidesMemberVariable")
286    protected native void configure(Element e,
287                                         boolean autoclose,
288                                         int minuteStep,
289                                         boolean todayButton,
290                                         boolean highlightToday,
291                                    String minViewMode,
292                                    String startViewMode,
293                                    String maxViewMode,
294                                    String pickerPosition) /*-{
295        var that = this;
296        $wnd.jQuery(e).datetimepicker({
297            autoclose : autoclose,
298            minuteStep : minuteStep,
299            todayBtn : todayButton,
300            todayHighlight : highlightToday,
301            minView : minViewMode,
302            startView : startViewMode,
303            maxView : maxViewMode,
304            pickerPosition: pickerPosition
305        })
306        .on('change' , function() {
307            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onChange()();
308        })
309        .on('changeDate', function () {
310            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onChange()();
311        })
312        .on("show", function (e) {
313            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onShow(Lcom/google/gwt/user/client/Event;)(e);
314        })
315        .on("hide", function (e) {
316            that.@com.github.gwtbootstrap.datetimepicker.client.ui.base.DateTimeBoxBase::onHide(Lcom/google/gwt/user/client/Event;)(e);
317        });
318    }-*/;
319
320    private native void execute(Element e, String cmd) /*-{
321        $wnd.jQuery(e).datetimepicker(cmd);
322    }-*/;
323
324    private void execute(String cmd) {
325        execute(getElement(), cmd);
326    }
327
328    /**
329     * {@inheritDoc}
330     */
331    @Override
332    public void show() {
333        execute("show");
334    }
335
336    /**
337     * {@inheritDoc}
338     */
339    @Override
340    public void hide() {
341        execute("hide");
342    }
343
344    /**
345     * {@inheritDoc}
346     */
347    @Override
348    public void toggle() {
349        //TODO 2012/06/21 ohashi keisuke should be support
350        throw new UnsupportedOperationException("not support toggle");
351    }
352
353    /**
354     * {@inheritDoc}
355     */
356    @Override
357    public HandlerRegistration addHideHandler(HideHandler handler) {
358        return addHandler(handler, HideEvent.getType());
359    }
360
361    /**
362     * {@inheritDoc}
363     */
364    @Override
365    public HandlerRegistration addHiddenHandler(HiddenHandler handler) {
366        //TODO 2012/06/21 ohashi keisuke should be support
367        throw new UnsupportedOperationException("not support hidden event");
368    }
369
370    /**
371     * {@inheritDoc}
372     */
373    @Override
374    public HandlerRegistration addShowHandler(ShowHandler handler) {
375        return addHandler(handler, ShowEvent.getType());
376    }
377
378    /**
379     * {@inheritDoc}
380     */
381    @Override
382    public HandlerRegistration addShownHandler(ShownHandler handler) {
383        //TODO 2012/06/21 ohashi keisuke should be support
384        throw new UnsupportedOperationException("not support shown event");
385    }
386
387    /**
388     * {@inheritDoc}
389     */
390    @Override
391    public void setWeekStart(int start) {
392        getElement().setAttribute("data-date-weekstart", start + "");
393    }
394
395    /**
396     * {@inheritDoc}
397     */
398    @Override
399    public void setStartDate(String startDate) {
400        getElement().setAttribute("data-date-startdate", startDate);
401    }
402
403    /**
404     * {@inheritDoc}
405     */
406    @Override
407    public void setStartDate_(Date startDate) {
408        setStartDate(dtf.format(startDate));
409    }
410
411
412    /**
413     * {@inheritDoc}
414     */
415    @Override
416    public void setEndDate(String endDate) {
417        getElement().setAttribute("data-date-enddate", endDate);
418    }
419
420    /**
421     * {@inheritDoc}
422     */
423    @Override
424    public void setEndDate_(Date endDate) {
425        setEndDate(dtf.format(endDate));
426    }
427
428    /**
429     * {@inheritDoc}
430     */
431    @Override
432    public void setAutoClose(boolean autoclose) {
433        this.autoclose = autoclose;
434    }
435
436    /**
437     * {@inheritDoc}
438     */
439    @Override
440    public void setMinView(ViewMode mode) {
441        setMinView(mode.name());
442    }
443
444    /**
445     * {@inheritDoc}
446     */
447    @Override
448    public void setMinView(String mode) {
449        this.minViewMode = mode.toLowerCase();
450    }
451
452
453    /**
454     * {@inheritDoc}
455     */
456    @Override
457    public void setStartView(HasViewMode.ViewMode mode) {
458        setStartView(mode.name());
459    }
460
461    /**
462     * {@inheritDoc}
463     */
464    @Override
465    public void setStartView(String mode) {
466        this.startViewMode = mode.toLowerCase();
467    }
468
469    /**
470     * {@inheritDoc}
471     */
472    @Override
473    public void setMaxView(ViewMode mode) {
474        setMaxView(mode.name());
475    }
476
477    /**
478     * {@inheritDoc}
479     */
480    @Override
481    public void setMaxView(String mode) {
482        this.maxViewMode = mode.toLowerCase();
483    }
484
485
486    /**
487         * Return Editor
488     *
489         * @return editor
490         */
491        @Override
492        public TakesValueEditor<Date> asEditor() {
493                if(editor == null){
494                        editor = TakesValueEditor.of(this);
495                }
496                return editor;
497        }
498
499    @Override
500    public HandlerRegistration addChangeHandler(ChangeHandler handler) {
501        return addHandler(handler, ChangeEvent.getType());
502    }
503
504    /**
505     * {@inheritDoc}
506     */
507    @Override
508    public void setPlaceholder(String placeholder) {
509        placeholderHelper.setPlaceholer(getElement(), placeholder);
510    }
511
512    /**
513     * {@inheritDoc}
514     */
515    @Override
516    public String getPlaceholder() {
517        return placeholderHelper.getPlaceholder(getElement());
518    }
519
520    /**
521     * {@inheritDoc}
522     */
523    @Override
524    public void setSearchQuery(boolean searchQuery) {
525        SearchQueryStyleHelper.setSearchQuery(this, searchQuery);
526    }
527
528    /**
529     * {@inheritDoc}
530     */
531    @Override
532    public boolean isSearchQuery() {
533        return SearchQueryStyleHelper.isSearchQuery(this);
534    }
535
536    /**
537     * {@inheritDoc}
538     */
539    @Override
540    public void setAlternateSize(AlternateSize size) {
541        StyleHelper.changeStyle(this, size, AlternateSize.class);
542    }
543
544    /**
545     * {@inheritDoc}
546     */
547    @Override
548    public void setSize(int size) {
549        SizeHelper.setSize(this, size);
550    }
551
552    /**
553     * {@inheritDoc}
554     */
555    @Override
556    public String getId() {
557        return getElement().getId();
558    }
559
560    /**
561     * {@inheritDoc}
562     */
563    @Override
564    public void setId(String id) {
565        getElement().setId(id);
566    }
567
568    /**
569     * {@inheritDoc}
570     */
571    @Override
572    public void setShowOn(Device device) {
573        ResponsiveHelper.setShowOn(this, device);
574    }
575
576    /**
577     * {@inheritDoc}
578     */
579    @Override
580    public void setHideOn(Device device) {
581        ResponsiveHelper.setHideOn(this, device);
582
583    }
584
585    /**
586     * {@inheritDoc}
587     */
588    @Override
589    public void setStyle(Style style) {
590        StyleHelper.setStyle(this, style);
591    }
592
593    /**
594     * {@inheritDoc}
595     */
596    @Override
597    public void addStyle(Style style) {
598        StyleHelper.addStyle(this, style);
599    }
600
601    /**
602     * {@inheritDoc}
603     */
604    @Override
605    public void removeStyle(Style style) {
606        StyleHelper.removeStyle(this, style);
607
608    }
609
610    /**
611     * {@inheritDoc}
612     */
613    @Override
614    public boolean isEnabled() {
615        return false;
616    }
617
618    /**
619     * {@inheritDoc}
620     */
621    @Override
622    public void setEnabled(boolean enabled) {
623        box.setEnabled(enabled);
624    }
625
626
627        @Override
628        public void setDaysOfWeekDisabled(String value) {
629                getElement().setAttribute("date-days-of-week-disabled", value);
630        }
631
632
633        @Override
634        public void setMinuteStep(int minutes) {
635//              getElement().setAttribute("date-days-of-week-disabled", value);
636                this.minuteStep = minutes;
637        }
638
639
640        @Override
641        public void setShowTodayButton(boolean show) {
642                this.todayButton = show;
643        }
644
645
646        @Override
647        public void setHighlightToday(boolean highlight) {
648                this.highlightToday = highlight;
649        }
650
651    public PickerPosition getPickerPosition() {
652        return pickerPosition;
653    }
654
655    public void setPickerPosition(PickerPosition pickerPosition) {
656        this.pickerPosition = pickerPosition;
657    }
658
659    private String dpGlobalFormatToDateTimeFormat(String dpGlobalFormat)
660    {
661        if(dpGlobalFormat == null || dpGlobalFormat.length() == 0)
662            return "";
663
664        char current;
665        char last = dpGlobalFormat.charAt(0);
666        int count = 1;
667        String out = "";
668
669        for(int index = 1; index < dpGlobalFormat.length(); index++)
670        {
671            current = dpGlobalFormat.charAt(index);
672
673            if(current == last)
674            {
675                count++;
676                continue;
677            }
678
679            out += processToken(last, count);
680
681            last = current;
682            count = 1;
683        }
684
685        out += processToken(last, count);
686
687        return out;
688    }
689
690    private String processToken(char token, int count)
691    {
692        if (token == 'y') {
693            if (count == 2)
694                return "yy";
695            if(count == 4)
696                return "yyyy";
697        }
698        else if(token == 'm') {
699            if(count == 1)
700                return "M";
701            if(count == 2)
702                return "MM";
703        }
704        else if(token == 'M') {
705            if(count == 1)
706                return "MMM";
707            if(count == 2)
708                return "MMMM";
709        }
710        else if(token == 'h') {
711            token = 'H';
712        }
713        else if(token == 'i') {
714            token = 'm';
715        }
716
717        String out = "";
718        for(int i=0; i<count; i++)
719            out += token;
720
721        return out;
722
723        // TODO: Support PHP format so we can do more complex formatting
724    }
725
726    /*
727    DateTimeFormat
728
729    G   era designator  Text    AD
730    y   year    Number  1996
731    M   month in year   Text or Number  July (or) 07
732    d   day in month    Number  10
733    h   hour in am/pm (1-12)    Number  12
734    H   hour in day (0-23)      Number  0
735    m   minute in hour  Number  30
736    s   second in minute        Number  55
737    S   fractional second       Number  978
738    E   day of week     Text    Tuesday
739    a   am/pm marker    Text    PM
740    k   hour in day (1-24)      Number  24
741    K   hour in am/pm (0-11)    Number  0
742    z   time zone       Text    Pacific Standard Time
743    Z   time zone (RFC 822)     Number  -0800
744    v   time zone (generic)     Text    Pacific Time
745    '   escape for text Delimiter       'Date='
746    ''  single quote    Literal 'o''clock'
747     */
748}