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