001package org.gwtbootstrap3.extras.fullcalendar.client.ui;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 - 2015 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import com.google.gwt.core.client.JavaScriptObject;
024import com.google.gwt.core.client.JsArray;
025import com.google.gwt.core.client.JsDate;
026import com.google.gwt.core.client.ScriptInjector;
027import com.google.gwt.dom.client.Document;
028import com.google.gwt.dom.client.Element;
029import com.google.gwt.event.dom.client.DomEvent;
030import com.google.gwt.event.dom.client.HasLoadHandlers;
031import com.google.gwt.event.dom.client.LoadEvent;
032import com.google.gwt.event.dom.client.LoadHandler;
033import com.google.gwt.event.shared.HandlerRegistration;
034import com.google.gwt.user.client.ui.FlowPanel;
035
036import java.util.Date;
037import java.util.HashMap;
038import java.util.List;
039import java.util.Map;
040
041/**
042 * Wrapper around FullCalendar
043 *
044 * @author Jeff Isenhart
045 * @see http://arshaw.com/fullcalendar/
046 */
047public class FullCalendar extends FlowPanel implements HasLoadHandlers {
048
049    private ViewOption currentView;//http://arshaw.com/fullcalendar/docs/views/defaultView/
050    private CalendarConfig config;//a bunch of options and events encapsulated in one place
051    private final boolean editable;//@see http://arshaw.com/fullcalendar/docs/event_ui/editable/
052    private boolean loaded;
053    private static Map<String, JavaScriptObject> languageScripts;
054
055    public FullCalendar(final String id, final ViewOption defaultView, final boolean editable) {
056        this(id, defaultView, null, editable);
057    }
058
059    public FullCalendar(final String id, final ViewOption defaultView, final boolean editable, final Header header) {
060        this(id, defaultView, new CalendarConfig(header), editable);
061    }
062
063    public FullCalendar(final String id, final ViewOption defaultView, final CalendarConfig config, final boolean editable) {
064        getElement().setId(id);
065        this.currentView = defaultView == null ? ViewOption.month : defaultView;
066        this.config = config;
067        this.editable = editable;
068        loaded = false;
069        if (languageScripts == null) {
070            languageScripts = new HashMap<String, JavaScriptObject>();
071        }
072    }
073
074    /**
075     * This method is called immediately after a widget becomes attached to the
076     * browser's document.
077     */
078    @Override
079    protected void onLoad() {
080        super.onLoad();
081        loaded = true;
082        renderCalendar();
083    }
084
085    private void renderCalendar() {
086        boolean selectable = false;
087        boolean selectHelper = false;
088        boolean unselectAuto = true;
089        boolean selectOverlap = true;
090
091        JsArray<JavaScriptObject> javascriptParams = null;
092        String language = null;
093        String timezone = null;
094        String weekNumberTitle = null;
095        String unselectCancel = null;
096        String selectContraint = null;
097        if (config != null) {
098            selectable = config.isSelectable();
099            selectHelper = config.isSelectHelper();
100            unselectAuto = config.isUnselectAuto();
101            selectOverlap = config.isSelectOverlap();
102            timezone = config.getTimezone();
103            weekNumberTitle = config.getWeekNumberTitle();
104            unselectCancel = config.getUnselectCancel();
105            selectContraint = config.getSelectContraint();
106            javascriptParams = config.getJavaScriptParameters();
107            if (config.getLangauge() != null) {
108                language = config.getLangauge().getCode();
109                ensureInjected(config.getLangauge());
110            }
111        }
112        addCalendar(getElement().getId(),
113                currentView.name(),
114                editable,
115                selectable,
116                selectHelper,
117                unselectAuto,
118                selectOverlap,
119                language,
120                timezone,
121                weekNumberTitle,
122                unselectCancel,
123                selectContraint,
124                javascriptParams
125        );
126        //Let everyone know it is ok to add events and set properties on the instance
127        DomEvent.fireNativeEvent(Document.get().createLoadEvent(), this);
128    }
129
130    public void changeLangauge(final Language language) {
131        if (language != null) {
132            if (config == null) {
133                config = new CalendarConfig();
134            }
135            config.setLangauge(language);
136            destroy();
137            renderCalendar();
138        }
139    }
140
141    public void changeTimezone(final String timezone) {
142        if (timezone != null) {
143            if (config == null) {
144                config = new CalendarConfig();
145            }
146            config.setTimezone(timezone);
147            destroy();
148            renderCalendar();
149        }
150    }
151
152    private void ensureInjected(final Language language) {
153        if (!languageScripts.isEmpty()) {
154            for (final JavaScriptObject script : languageScripts.values()) {
155                try {
156                    final Element ele = (Element) script;
157                    ele.removeFromParent();
158                } catch (final Exception e) {
159                    // TODO: handle exception
160                }
161            }
162        }
163        final JavaScriptObject scriptElement = ScriptInjector.fromString(language.getResource().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject();
164        languageScripts.put(language.getCode(), scriptElement);
165    }
166
167    private native void addCalendar(String id,
168                                    String currentView,
169                                    boolean editable,
170                                    boolean selectable,
171                                    boolean selectHelper,
172                                    boolean unselectAuto,
173                                    boolean selectOverlap,
174                                    String lang,
175                                    String timezone,
176                                    String weekNumberTitle,
177                                    String unselectCancel,
178                                    String selectContraint,
179                                    JsArray<JavaScriptObject> options
180    ) /*-{
181        var fullCalendarParams = {
182            defaultView: currentView,
183            selectable: selectable,
184            selectHelper: selectHelper,
185            editable: editable,
186            unselectAuto: unselectAuto,
187            selectOverlap: selectOverlap
188        };
189        if (lang) {
190            fullCalendarParams.lang = lang;
191        }
192        if (timezone) {
193            fullCalendarParams.timezone = timezone;
194        }
195        if (weekNumberTitle) {
196            fullCalendarParams.weekNumberTitle = weekNumberTitle;
197        }
198        if (unselectCancel) {
199            fullCalendarParams.unselectCancel = unselectCancel;
200        }
201        if (selectContraint) {
202            fullCalendarParams.selectContraint = selectContraint;
203        }
204        if (options) {
205            for (var i = 0; i < options.length; i++) {
206                $wnd.jQuery.extend(fullCalendarParams, options[i]);
207            }
208        }
209        $wnd.jQuery('#' + id).fullCalendar(fullCalendarParams);
210    }-*/;
211
212    public void addEvent(final Event event) {
213        if (loaded && event != null) {
214            addEvent(getElement().getId(), event.toJavaScript());
215        }
216    }
217
218    public void addEvents(final List<Event> events) {
219        if (loaded && events != null && !events.isEmpty()) {
220            JsArray<JavaScriptObject> jsEvents = JavaScriptObject.createArray(events.size()).cast();
221            int i = 0;
222            for (final Event evt : events) {
223                jsEvents.set(i++, evt.toJavaScript());
224            }
225            addEventSource(getElement().getId(), jsEvents);
226        }
227    }
228
229    private native void addEventSource(String id, JsArray<JavaScriptObject> events) /*-{
230        $wnd.jQuery('#' + id).fullCalendar('addEventSource', events);
231    }-*/;
232
233    public ViewOption getCurrentView() {
234        return currentView;
235    }
236
237    private native void addEvent(String id, JavaScriptObject event) /*-{
238        $wnd.jQuery('#' + id).fullCalendar('renderEvent', event, true);
239    }-*/;
240
241    public void setView(final ViewOption view) {
242        if (view != null) {
243            currentView = view;
244            setView(getElement().getId(), view.name());
245        }
246    }
247
248    private native void setView(String id, String viewName) /*-{
249        $wnd.jQuery('#' + id).fullCalendar('changeView', viewName);
250    }-*/;
251
252    public void goToDate(final Date d) {
253        if (d != null) {
254            JsDate date = JsDate.create((double) d.getTime());
255            goToDate(getElement().getId(), date);
256        }
257    }
258
259    private native void goToDate(String id, JsDate date) /*-{
260        $wnd.jQuery('#' + id).fullCalendar('gotoDate',date);
261    }-*/;
262
263    @Override
264    public HandlerRegistration addLoadHandler(final LoadHandler handler) {
265        return super.addDomHandler(handler, LoadEvent.getType());
266    }
267
268    public JsArray<JavaScriptObject> getEvent(final String eventId) {
269        if (eventId != null) {
270            return getEvent(getElement().getId(), eventId);
271        }
272        return null;
273    }
274
275    public native JsArray<JavaScriptObject> getEvent(String id, String eventId) /*-{
276        return $wnd.jQuery('#' + id).fullCalendar('clientEvents', eventId);
277    }-*/;
278
279    public void removeEvent(final String eventId) {
280        if (eventId != null) {
281            removeEvent(getElement().getId(), eventId);
282        }
283    }
284
285    public void removeAllEvents() {
286        removeAllEvents(getElement().getId());
287    }
288
289    private native void removeAllEvents(String id) /*-{
290        $wnd.jQuery('#' + id).fullCalendar('removeEvents');
291    }-*/;
292
293    public native void removeEvent(String id, String eventId) /*-{
294        $wnd.jQuery('#' + id).fullCalendar('removeEvents', eventId);
295    }-*/;
296
297    public void updateEvent(final Event evt) {
298        if (evt != null && evt.getId() != null) {
299            updateEvent(getElement().getId(), evt.toJavaScript());
300        }
301    }
302
303    public native void updateEvent(String id, JavaScriptObject event) /*-{
304        $wnd.jQuery('#' + id).fullCalendar('updateEvent', event);
305    }-*/;
306
307    public void addEventSource(final EventSource eventSource) {
308        if (eventSource != null) {
309            addEventSource(getElement().getId(), eventSource.toJavaScript());
310        }
311    }
312
313    private native void addEventSource(String id, JavaScriptObject eventSource) /*-{
314        $wnd.jQuery('#' + id).fullCalendar('addEventSource', eventSource);
315    }-*/;
316
317    public void removeEventSource(final EventSource eventSource) {
318        if (eventSource != null) {
319            removeEventSource(getElement().getId(), eventSource.toJavaScript());
320        }
321    }
322
323    private native void removeEventSource(String id, JavaScriptObject eventSource) /*-{
324        $wnd.jQuery('#' + id).fullCalendar('removeEventSource', eventSource);
325    }-*/;
326
327    public void previous() {
328        previous(getElement().getId());
329    }
330
331    private native void previous(String id) /*-{
332        $wnd.jQuery('#' + id).fullCalendar('prev');
333    }-*/;
334
335    public void next() {
336        next(getElement().getId());
337    }
338
339    private native void next(String id) /*-{
340        $wnd.jQuery('#' + id).fullCalendar('next');
341    }-*/;
342
343    public Date getDate() {
344        final JsDate jsDate = getDate(getElement().getId());
345        final long time = (long) jsDate.getTime();
346        return new Date(time);
347    }
348
349    private native JsDate getDate(String id) /*-{
350        return $wnd.jQuery('#' + id).fullCalendar('getDate').toDate();
351    }-*/;
352
353    public void today() {
354        today(getElement().getId());
355    }
356
357    private native void today(String id) /*-{
358        $wnd.jQuery('#' + id).fullCalendar('today');
359    }-*/;
360
361    public View getView() {
362        return new View(getView(getElement().getId()));
363    }
364
365    private native JavaScriptObject getView(String id) /*-{
366        $wnd.jQuery('#' + id).fullCalendar('getView');
367    }-*/;
368
369    public void destroy() {
370        destroy(getElement().getId());
371    }
372
373    private native void destroy(String id) /*-{
374        $wnd.jQuery('#' + id).fullCalendar('destroy');
375    }-*/;
376
377    public void render() {
378        render(getElement().getId());
379    }
380
381    private native void render(String id) /*-{
382        $wnd.jQuery('#' + id).fullCalendar('render');
383    }-*/;
384
385    public void setHeight(final int height) {
386        if (height >= 0) {
387            setHeight(getElement().getId(), height);
388        }
389    }
390
391    private native void setHeight(String id, int height) /*-{
392        $wnd.jQuery('#' + id).fullCalendar('option', 'height', height);
393    }-*/;
394
395    public void setContentHeight(final int height) {
396        if (height >= 0) {
397            setContentHeight(getElement().getId(), height);
398        }
399    }
400
401    private native void setContentHeight(String id, int height) /*-{
402        $wnd.jQuery('#' + id).fullCalendar('option', 'contentHeight', height);
403    }-*/;
404
405    public void setAspectRatio(final double ratio) {
406        if (ratio > 0) {
407            setAspectRatio(getElement().getId(), ratio);
408        }
409    }
410
411    private native void setAspectRatio(String id, double ratio) /*-{
412        $wnd.jQuery('#' + id).fullCalendar('option', 'aspectRatio', ratio);
413    }-*/;
414
415    /**
416     * Useful for callback cancel/revert functions
417     *
418     * @param revertFunction
419     */
420    public native void executeFunction(JavaScriptObject revertFunction)/*-{
421        revertFunction();
422    }-*/;
423    
424    public void select(Date start, Date end) {
425        select(getElement().getId(), Event.getDateAsISO8601(start), Event.getDateAsISO8601(end));
426    }
427
428    public void select(String start, String end) {
429        select(getElement().getId(), start, end);
430    }
431    
432    private native void select(String id, String start, String end) /*-{
433        $wnd.jQuery('#' + id).fullCalendar('select', start, end);
434    }-*/;
435    
436    public void unselect() {
437        unselect(getElement().getId());
438    }
439    
440    private native void unselect(String id) /*-{
441        $wnd.jQuery('#' + id).fullCalendar('unselect');
442    }-*/;
443}