package org.richfaces.renderkit; import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.faces.FacesException; import javax.faces.application.ResourceDependencies; import javax.faces.application.ResourceDependency; import javax.faces.component.UIComponent; import javax.faces.component.UINamingContainer; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import org.richfaces.component.AbstractSchedule; import org.richfaces.component.AbstractScheduleAgendaDayView; import org.richfaces.component.AbstractScheduleAgendaWeekView; import org.richfaces.component.AbstractScheduleBasicDayView; import org.richfaces.component.AbstractScheduleBasicWeekView; import org.richfaces.component.AbstractScheduleMonthView; import org.richfaces.component.ScheduleCommonViewAttributes; import org.richfaces.component.event.ScheduleDateRangeChangeEvent; import org.richfaces.component.event.ScheduleDateRangeSelectEvent; import org.richfaces.component.event.ScheduleDateSelectEvent; import org.richfaces.component.event.ScheduleItemMoveEvent; import org.richfaces.component.event.ScheduleItemResizeEvent; import org.richfaces.component.event.ScheduleItemSelectEvent; import org.richfaces.component.event.ScheduleViewChangeEvent; import org.richfaces.javascript.JSFunction; import org.richfaces.javascript.JSFunctionDefinition; import org.richfaces.javascript.JSObject; import org.richfaces.javascript.JSReference; import org.richfaces.javascript.ScriptString; import org.richfaces.ui.common.AjaxFunction; import org.richfaces.ui.common.SwitchType; import org.richfaces.util.AjaxRendererUtils; import org.richfaces.util.RendererUtils; @ResourceDependencies({ @ResourceDependency(library = "org.richfaces", name = "base-component.reslib"), @ResourceDependency(name = "richfaces-event.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.core.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.widget.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.mouse.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.draggable.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.droppable.js"), @ResourceDependency(library = "com.jqueryui", name = "jquery.ui.resizable.js"), @ResourceDependency(library = "com.arshaw/fullcalendar", name = "fullcalendar.js"), @ResourceDependency(library = "org.richfaces", name = "richfaces.schedule.js"), @ResourceDependency(library = "org.richfaces", name = "richfaces.schedule.css"), @ResourceDependency(name = "org.richfaces.renderkit.html.scripts.ScheduleMessages"), @ResourceDependency(library = "com.arshaw/fullcalendar", name = "fullcalendar.css")}) public abstract class ScheduleRendererBase extends RendererBase { // ------------------------------ FIELDS ------------------------------ public static final String DATE_RANGE_CHANGED_EVENT = "dateRangeChange"; public static final String DATE_RANGE_SELECTED_EVENT = "dateRangeSelect"; public static final String DATE_SELECTED_EVENT = "dateSelect"; public static final String ITEM_MOVE_EVENT = "itemMove"; public static final String ITEM_RESIZE_EVENT = "itemResize"; public static final String ITEM_SELECTED_EVENT = "itemSelect"; public static final String RENDERER_TYPE = "org.richfaces.ScheduleRenderer"; public static final String VIEW_CHANGED_EVENT = "viewChange"; private static final String ALL_DAY_PARAM = "allDay"; private static final String CALLBACK = "callback"; private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static final String DAY_DELTA_PARAM = "dayDelta"; private static final Map<String, Object> DEFAULTS; private static final String END_DATE_PARAM = "endDate"; private static final String EVENT_TYPE_PARAM = "eventType"; private static final String ITEM_ID_PARAM = "itemId"; private static final String MINUTE_DELTA_PARAM = "minuteDelta"; private static final String START_DATE_PARAM = "startDate"; private static final String VIEW_PARAM = "view"; // -------------------------- STATIC METHODS -------------------------- /** * Following defaults are be used by addOptionIfSetAndNotDefault */ static { Map<String, Object> defaults = new HashMap<String, Object>(); defaults.put("eventColor", ""); defaults.put("eventBackgroundColor", ""); defaults.put("eventBorderColor", ""); defaults.put("eventTextColor", ""); defaults.put("styleClass", ""); defaults.put("defaultView", AbstractSchedule.DEFAULT_VIEW); defaults.put("firstDay", AbstractSchedule.DEFAULT_FIRST_DAY - 1); defaults.put("isRTL", AbstractSchedule.DEFAULT_RTL); defaults.put("weekends", AbstractSchedule.DEFAULT_SHOW_WEEKENDS); defaults.put("weekMode", AbstractSchedule.DEFAULT_WEEK_MODE); defaults.put("aspectRatio", AbstractSchedule.DEFAULT_ASPECT_RATIO); defaults.put("allDaySlot", AbstractSchedule.DEFAULT_ALL_DAY_SLOT); defaults.put("axisFormat", AbstractSchedule.DEFAULT_AXIS_FORMAT); defaults.put("slotMinutes", AbstractSchedule.DEFAULT_SLOT_MINUTES); defaults.put("defaultEventMinutes", AbstractSchedule.DEFAULT_EVENT_MINUTES); defaults.put("firstHour", AbstractSchedule.DEFAULT_FIRST_HOUR); defaults.put("minTime", AbstractSchedule.DEFAULT_MIN_TIME); defaults.put("maxTime", AbstractSchedule.DEFAULT_MAX_TIME); defaults.put("editable", AbstractSchedule.DEFAULT_EDITABLE); defaults.put("selectable", AbstractSchedule.DEFAULT_SELECTABLE); defaults.put("selectHelper", AbstractSchedule.DEFAULT_SELECT_HELPER); defaults.put("unselectAuto", AbstractSchedule.DEFAULT_UNSELECT_AUTO); defaults.put("unselectCancel", AbstractSchedule.DEFAULT_UNSELECT_CANCEL); defaults.put("disableDragging", AbstractSchedule.DEFAULT_DISABLE_DRAGGING); defaults.put("disableResizing", AbstractSchedule.DEFAULT_DISABLE_RESIZING); defaults.put("dragRevertDuration", AbstractSchedule.DEFAULT_DRAG_REVERT_DURATION); defaults.put("allDayDefault", AbstractSchedule.DEFAULT_ALL_DAY_DEFAULT); defaults.put("autoRefreshOnDateRangeSelect", AbstractSchedule.DEFAULT_AUTO_REFRESH_ON_DATE_RANGE_SELECT); defaults.put("onbeforeitemselect", ""); defaults.put("onitemselect", ""); defaults.put("onbeforeitemdrop", ""); defaults.put("onitemdrop", ""); defaults.put("onbeforeitemresize", ""); defaults.put("onitemresize", ""); defaults.put("onitemresizestart", ""); defaults.put("onitemresizestop", ""); defaults.put("onitemdragstart", ""); defaults.put("onitemdragstop", ""); defaults.put("onitemmouseover", ""); defaults.put("onitemmouseout", ""); defaults.put("onviewchange", ""); defaults.put("onviewdisplay", ""); defaults.put("onbeforedateselect", ""); defaults.put("ondateselect", ""); defaults.put("onbeforedaterangeselect", ""); defaults.put("ondaterangeselect", ""); defaults.put("ondaterangechange", ""); defaults.put("showCurrentTimeline", false); DEFAULTS = Collections.unmodifiableMap(defaults); } private static void copyAttribute(String attribute, String suffix, UIComponent source, UIComponent target) { Object value = source.getAttributes().get(attribute); if (value != null) { target.getAttributes().put(attribute + suffix, value); } } // -------------------------- OTHER METHODS -------------------------- @Override public void decode(FacesContext context, UIComponent component) { if (!component.isRendered()) { return; } Map<String, String> requestParameterMap = context.getExternalContext().getRequestParameterMap(); if (requestParameterMap.get(component.getClientId(context)) != null) { String startDateParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, START_DATE_PARAM)); String endDateParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, END_DATE_PARAM)); String itemIdParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, ITEM_ID_PARAM)); String dayDeltaParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, DAY_DELTA_PARAM)); String minuteDeltaParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, MINUTE_DELTA_PARAM)); String allDayParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, ALL_DAY_PARAM)); String eventTypeParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, EVENT_TYPE_PARAM)); String viewParam = requestParameterMap.get(getFieldId(context, (AbstractSchedule) component, VIEW_PARAM)); try { if (DATE_RANGE_CHANGED_EVENT.equals(eventTypeParam)) { Date startDate = DATE_FORMAT.parse(startDateParam); Date endDate = DATE_FORMAT.parse(endDateParam); new ScheduleDateRangeChangeEvent(component, startDate, endDate).queue(); } else if (ITEM_MOVE_EVENT.equals(eventTypeParam)) { int dayDelta = Integer.parseInt(dayDeltaParam); int minuteDelta = Integer.parseInt(minuteDeltaParam); boolean allDay = Boolean.parseBoolean(allDayParam); new ScheduleItemMoveEvent(component, itemIdParam, dayDelta, minuteDelta, allDay).queue(); } else if (ITEM_RESIZE_EVENT.equals(eventTypeParam)) { int dayDelta = Integer.parseInt(dayDeltaParam); int minuteDelta = Integer.parseInt(minuteDeltaParam); new ScheduleItemResizeEvent(component, itemIdParam, dayDelta, minuteDelta).queue(); } else if (ITEM_SELECTED_EVENT.equals(eventTypeParam)) { new ScheduleItemSelectEvent(component, itemIdParam).queue(); } else if (VIEW_CHANGED_EVENT.equals(eventTypeParam)) { new ScheduleViewChangeEvent(component, viewParam).queue(); } else if (DATE_SELECTED_EVENT.equals(eventTypeParam)) { Date startDate = DATE_FORMAT.parse(startDateParam); boolean allDay = Boolean.parseBoolean(allDayParam); new ScheduleDateSelectEvent(component, startDate, allDay).queue(); } else if (DATE_RANGE_SELECTED_EVENT.equals(eventTypeParam)) { Date startDate = DATE_FORMAT.parse(startDateParam); Date endDate = DATE_FORMAT.parse(endDateParam); boolean allDay = Boolean.parseBoolean(allDayParam); new ScheduleDateRangeSelectEvent(component, startDate, endDate, allDay).queue(); } } catch (ParseException ex) { throw new FacesException("Cannot convert request parmeters", ex); } } } private void addOptionHash(String attribute, UIComponent source, Map<String, Object> options) { Map<String, Object> hash = new HashMap<String, Object>(3); Map<String, Object> attributes = source.getAttributes(); addOptionIfSetAndNotDefault("month", attributes.get(attribute + "Month"), hash); addOptionIfSetAndNotDefault("basicWeek", attributes.get(attribute + "BasicWeek"), hash); addOptionIfSetAndNotDefault("agendaWeek", attributes.get(attribute + "AgendaWeek"), hash); addOptionIfSetAndNotDefault("basicDay", attributes.get(attribute + "BasicDay"), hash); addOptionIfSetAndNotDefault("agendaDay", attributes.get(attribute + "AgendaDay"), hash); addOptionIfSetAndNotDefault("", attributes.get(attribute), hash); if (hash.size() > 0) { options.put(attribute, hash); } } protected void addOptionIfSetAndNotDefault(String optionName, Object value, Map<String, Object> options) { if (value != null && !"".equals(value) && !value.equals(DEFAULTS.get(optionName))) { options.put(optionName, value); } } protected Object createSubmitEventFunction(FacesContext context, AbstractSchedule component) { ScriptString jsFunction; Map<String, Object> params = new HashMap<String, Object>(); params.put(getFieldId(context, component, START_DATE_PARAM), new JSReference(START_DATE_PARAM)); params.put(getFieldId(context, component, END_DATE_PARAM), new JSReference(END_DATE_PARAM)); params.put(getFieldId(context, component, ITEM_ID_PARAM), new JSReference(ITEM_ID_PARAM)); params.put(getFieldId(context, component, DAY_DELTA_PARAM), new JSReference(DAY_DELTA_PARAM)); params.put(getFieldId(context, component, MINUTE_DELTA_PARAM), new JSReference(MINUTE_DELTA_PARAM)); params.put(getFieldId(context, component, ALL_DAY_PARAM), new JSReference(ALL_DAY_PARAM)); params.put(getFieldId(context, component, EVENT_TYPE_PARAM), new JSReference(EVENT_TYPE_PARAM)); params.put(getFieldId(context, component, VIEW_PARAM), new JSReference(VIEW_PARAM)); String clientId = component.getClientId(); params.put(clientId, clientId); if (isAjaxMode(component)) { AjaxFunction ajaxFunction = AjaxRendererUtils.buildAjaxFunction(context, component); ajaxFunction.getOptions().getParameters().putAll(params); ajaxFunction.getOptions().set("complete", new JSReference(CALLBACK)); jsFunction = ajaxFunction; } else if (SwitchType.server.equals(component.getSwitchType())) { jsFunction = new JSFunction("RichFaces.submitForm", "#" + RendererUtils.getInstance().getNestingForm(component).getClientId(context), params, ""); } else { return null; } return new JSFunctionDefinition("event", VIEW_PARAM, EVENT_TYPE_PARAM, ITEM_ID_PARAM, START_DATE_PARAM, END_DATE_PARAM, DAY_DELTA_PARAM, MINUTE_DELTA_PARAM, ALL_DAY_PARAM, CALLBACK).addToBody(jsFunction); } protected String getFieldId(FacesContext context, AbstractSchedule component, String attribute) { return RendererUtils.getInstance().clientId(context, component) + UINamingContainer.getSeparatorChar(context) + attribute; } protected Map<String, Object> getOptions(AbstractSchedule schedule) throws IOException { /** * Copy attributes from child view components */ for (UIComponent child : schedule.getChildren()) { if (!child.isRendered()) { continue; } String suffix = ""; if (child instanceof AbstractScheduleMonthView) { copyAttribute("weekMode", "", child, schedule); suffix = "Month"; } else if (child instanceof AbstractScheduleAgendaDayView) { suffix = "AgendaDay"; } else if (child instanceof AbstractScheduleAgendaWeekView) { suffix = "AgendaWeek"; } else if (child instanceof AbstractScheduleBasicDayView) { suffix = "BasicDay"; } else if (child instanceof AbstractScheduleBasicWeekView) { suffix = "BasicWeek"; } if (child instanceof ScheduleCommonViewAttributes) { copyAttribute("timeFormat", suffix, child, schedule); copyAttribute("columnFormat", suffix, child, schedule); copyAttribute("titleFormat", suffix, child, schedule); copyAttribute("dragOpacity", suffix, child, schedule); } } /** * Include only attributes that are actually set. */ Map<String, Object> options = new HashMap<String, Object>(); addOptionIfSetAndNotDefault("defaultView", schedule.getView(), options); /** * firstDayOfWeek numeration in Calendar (sunday=1,monday=2,etc.) and in widget(sunday=0,monday=1,etc.) */ Integer firstDay = schedule.getFirstDay(); if (firstDay != null) { addOptionIfSetAndNotDefault("firstDay", firstDay - 1, options); } addOptionIfSetAndNotDefault("isRTL", schedule.getRTL(), options); addOptionIfSetAndNotDefault("weekends", schedule.getShowWeekends(), options); addOptionIfSetAndNotDefault("weekMode", schedule.getWeekMode(), options); addOptionIfSetAndNotDefault("height", schedule.getHeight(), options); addOptionIfSetAndNotDefault("contentHeight", schedule.getContentHeight(), options); addOptionIfSetAndNotDefault("aspectRatio", schedule.getAspectRatio(), options); addOptionIfSetAndNotDefault("allDaySlot", schedule.getAllDaySlot(), options); addOptionIfSetAndNotDefault("allDayText", schedule.getAllDayText(), options); addOptionIfSetAndNotDefault("axisFormat", schedule.getAxisFormat(), options); addOptionIfSetAndNotDefault("slotMinutes", schedule.getSlotMinutes(), options); addOptionIfSetAndNotDefault("defaultEventMinutes", schedule.getDefaultEventMinutes(), options); addOptionIfSetAndNotDefault("firstHour", schedule.getFirstHour(), options); addOptionIfSetAndNotDefault("minTime", schedule.getMinTime(), options); addOptionIfSetAndNotDefault("maxTime", schedule.getMaxTime(), options); addOptionIfSetAndNotDefault("editable", schedule.getEditable(), options); addOptionIfSetAndNotDefault("selectable", schedule.getSelectable(), options); addOptionIfSetAndNotDefault("selectHelper", schedule.getSelectHelper(), options); addOptionIfSetAndNotDefault("unselectAuto", schedule.getUnselectAuto(), options); addOptionIfSetAndNotDefault("unselectCancel", schedule.getUnselectCancel(), options); addOptionIfSetAndNotDefault("disableDragging", schedule.getDisableDragging(), options); addOptionIfSetAndNotDefault("disableResizing", schedule.getDisableResizing(), options); addOptionIfSetAndNotDefault("dragRevertDuration", schedule.getDragRevertDuration(), options); addOptionIfSetAndNotDefault("eventColor", schedule.getEventColor(), options); addOptionIfSetAndNotDefault("eventBackgroundColor", schedule.getEventBackgroundColor(), options); addOptionIfSetAndNotDefault("eventBorderColor", schedule.getEventBorderColor(), options); addOptionIfSetAndNotDefault("eventTextColor", schedule.getEventTextColor(), options); addOptionIfSetAndNotDefault("showCurrentTimeline", schedule.isShowCurrentTimeline(), options); addOptionHash("dragOpacity", schedule, options); addOptionHash("titleFormat", schedule, options); addOptionHash("timeFormat", schedule, options); addOptionHash("columnFormat", schedule, options); Map<String, Object> headerOptions = new HashMap<String, Object>(3); addOptionIfSetAndNotDefault("left", schedule.getHeaderLeft(), headerOptions); addOptionIfSetAndNotDefault("center", schedule.getHeaderCenter(), headerOptions); addOptionIfSetAndNotDefault("right", schedule.getHeaderRight(), headerOptions); if (headerOptions.size() > 0) { options.put("header", headerOptions); } addOptionIfSetAndNotDefault("allDayDefault", schedule.getAllDayByDefault(), options); addOptionIfSetAndNotDefault("autoRefreshOnDateRangeSelect", schedule.isAutoRefreshOnDateRangeSelect(), options); addOptionIfSetAndNotDefault("onbeforeitemselect", schedule.getOnbeforeitemselect(), options); addOptionIfSetAndNotDefault("onitemselect", schedule.getOnitemselect(), options); addOptionIfSetAndNotDefault("onbeforeitemdrop", schedule.getOnbeforeitemdrop(), options); addOptionIfSetAndNotDefault("onitemdrop", schedule.getOnitemdrop(), options); addOptionIfSetAndNotDefault("onbeforeitemresize", schedule.getOnbeforeitemresize(), options); addOptionIfSetAndNotDefault("onitemresize", schedule.getOnitemresize(), options); addOptionIfSetAndNotDefault("onitemresizestart", schedule.getOnitemresizestart(), options); addOptionIfSetAndNotDefault("onitemresizestop", schedule.getOnitemresizestop(), options); addOptionIfSetAndNotDefault("onitemdragstart", schedule.getOnitemdragstart(), options); addOptionIfSetAndNotDefault("onitemdragstop", schedule.getOnitemdragstop(), options); addOptionIfSetAndNotDefault("onitemmouseover", schedule.getOnitemmouseover(), options); addOptionIfSetAndNotDefault("onitemmouseout", schedule.getOnitemmouseout(), options); addOptionIfSetAndNotDefault("onviewchange", schedule.getOnviewchange(), options); addOptionIfSetAndNotDefault("onviewdisplay", schedule.getOnviewdisplay(), options); addOptionIfSetAndNotDefault("onbeforedateselect", schedule.getOnbeforedateselect(), options); addOptionIfSetAndNotDefault("ondateselect", schedule.getOndateselect(), options); addOptionIfSetAndNotDefault("onbeforedaterangeselect", schedule.getOnbeforedaterangeselect(), options); addOptionIfSetAndNotDefault("ondaterangeselect", schedule.getOndaterangeselect(), options); addOptionIfSetAndNotDefault("ondaterangechange", schedule.getOndaterangechange(), options); if (schedule.getDate() != null) { Calendar calendar = Calendar.getInstance(); calendar.setTime(schedule.getDate()); options.put("year", calendar.get(Calendar.YEAR)); options.put("month", calendar.get(Calendar.MONTH)); options.put("date", calendar.get(Calendar.DATE)); } if (!isClientMode(schedule)) { Map<String, Object> initialItems = new HashMap<String, Object>(); Date startDate = AbstractSchedule.getFirstDisplayedDay(schedule); Date endDate = AbstractSchedule.getLastDisplayedDate(schedule); initialItems.put("items", schedule.getScheduleData(startDate, endDate)); Map<String, Object> date = new HashMap<String, Object>(); Calendar calendar = Calendar.getInstance(); calendar.setTime(startDate); date.put("year", calendar.get(Calendar.YEAR)); date.put("month", calendar.get(Calendar.MONTH)); date.put("date", calendar.get(Calendar.DATE)); initialItems.put("startDate", date); date = new HashMap<String, Object>(); calendar.setTime(endDate); date.put("year", calendar.get(Calendar.YEAR)); date.put("month", calendar.get(Calendar.MONTH)); date.put("date", calendar.get(Calendar.DATE)); initialItems.put("endDate", date); options.put("initialItems", initialItems); } else { options.put("events", schedule.getScheduleData(null, null)); } return options; } protected boolean isAjaxMode(AbstractSchedule component) { SwitchType mode = component.getSwitchType(); return SwitchType.ajax.equals(mode) || null == mode; } protected boolean isClientMode(AbstractSchedule component) { SwitchType mode = component.getSwitchType(); return SwitchType.client.equals(mode); } protected void writeInitFunction(FacesContext context, UIComponent component) throws IOException { AbstractSchedule schedule = (AbstractSchedule) component; ResponseWriter writer = context.getResponseWriter(); String clientId = schedule.getClientId(context); Locale locale = context.getViewRoot().getLocale(); String widgetVar = schedule.getWidgetVar(); if (widgetVar != null) { writer.writeText("var " + widgetVar + " = ", null); } final Map<String, Object> options = getOptions(schedule); options.put("locale", locale.toString()); options.put("submitEventFunction", createSubmitEventFunction(context, schedule)); writer.writeText(new JSObject("RichFaces.ui.Schedule", clientId, options).toScript(), null); } }