diff --git a/src/main/config/component/schedule.xml b/src/main/config/component/schedule.xml index be12fbe..53a35ca 100644 --- a/src/main/config/component/schedule.xml +++ b/src/main/config/component/schedule.xml @@ -861,10 +861,6 @@ javax.faces.el.MethodBinding - - &listeners; diff --git a/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java b/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java index 6beb3ac..6d40dfa 100644 --- a/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java +++ b/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java @@ -10,6 +10,7 @@ import javax.faces.context.FacesContext; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.util.Iterator; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -19,7 +20,6 @@ public class ScheduleMessages extends ClientScript { public static final String BUNDLE_NAME = "org.richfaces.component.UIScheduleMessages"; private static final String MESSAGE_KEY_BASE = "org.richfaces.component.UISchedule."; private static final Log log = LogFactory.getLog(ClientScript.class); -// private Locale recentLocale; @Override public InputStream getResourceAsStream(ResourceContext context) { @@ -27,25 +27,30 @@ public class ScheduleMessages extends ClientScript { ClassLoader loader = Thread.currentThread().getContextClassLoader(); FacesContext facesContext = FacesContext.getCurrentInstance(); Application application = facesContext.getApplication(); - Locale locale = application.getViewHandler().calculateLocale(facesContext); -// recentLocale = locale; - ResourceBundle applicationBundle = ResourceBundle.getBundle(application.getMessageBundle(), locale, loader); - ResourceBundle stockBundle = ResourceBundle.getBundle(BUNDLE_NAME, locale, loader); - String[] months = new String[]{"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"}; - String[] days = new String[]{"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"}; StringBuilder out = new StringBuilder(); - out.append("RichFaces.Schedule.prototype.messages=jQuery.extend(RichFaces.Schedule.prototype.messages,{").append("'").append(locale.toString()).append("':{"); - out.append("allDayText:'").append(escape(getMessageFromBundle(MESSAGE_KEY_BASE + "allDay", applicationBundle, stockBundle))).append("',"); - appendArray(out, applicationBundle, stockBundle, "monthNames", "monthNames", months); - out.append(","); - appendArray(out, applicationBundle, stockBundle, "monthNamesShort", "monthNamesShort", months); - out.append(","); - appendArray(out, applicationBundle, stockBundle, "dayNames", "dayNames", days); - out.append(","); - appendArray(out, applicationBundle, stockBundle, "dayNamesShort", "dayNamesShort", days); - out.append(","); - appendMap(out, applicationBundle, stockBundle, "buttonText", "buttonTexts", new String[]{"prev", "next", "prevYear", "nextYear", "today", "month", "day", "week"}); - out.append("}").append("})"); + out.append("RichFaces.Schedule.prototype.messages=jQuery.extend(RichFaces.Schedule.prototype.messages,{"); + Iterator supportedLocales = application.getSupportedLocales(); + while (supportedLocales.hasNext()) { + Locale locale = supportedLocales.next(); + ResourceBundle applicationBundle = ResourceBundle.getBundle(application.getMessageBundle(), locale, loader); + ResourceBundle stockBundle = ResourceBundle.getBundle(BUNDLE_NAME, locale, loader); + String[] months = new String[]{"JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER"}; + String[] days = new String[]{"SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY"}; + out.append("'").append(locale.toString()).append("':{"); + out.append("allDayText:'").append(escape(getMessageFromBundle(MESSAGE_KEY_BASE + "allDay", applicationBundle, stockBundle))).append("',"); + appendArray(out, applicationBundle, stockBundle, "monthNames", "monthNames", months); + out.append(","); + appendArray(out, applicationBundle, stockBundle, "monthNamesShort", "monthNamesShort", months); + out.append(","); + appendArray(out, applicationBundle, stockBundle, "dayNames", "dayNames", days); + out.append(","); + appendArray(out, applicationBundle, stockBundle, "dayNamesShort", "dayNamesShort", days); + out.append(","); + appendMap(out, applicationBundle, stockBundle, "buttonText", "buttonTexts", new String[]{"prev", "next", "prevYear", "nextYear", "today", "month", "day", "week"}); + out.append("},"); + } + out.delete(out.length() - 1, out.length()); + out.append("})"); try { // TODO where to get encoding from? It should match properties file's encoding, but probably be converted to response encoding return new ByteArrayInputStream(out.toString().getBytes(application.getViewHandler().calculateCharacterEncoding(facesContext))); @@ -108,21 +113,4 @@ public class ScheduleMessages extends ClientScript { public boolean requireFacesContext() { return true; } -//TODO if locale changes then recreate this resource -// @Override -// public boolean isCacheable(ResourceContext resourceContext) { -// if (resourceContext instanceof FacesResourceContext) { -// FacesContext facesContext = ((FacesResourceContext) resourceContext).getFacesContext(); -// Locale locale = facesContext.getApplication().getViewHandler().calculateLocale(facesContext); - - // if (locale != null && !locale.equals(recentLocale)) { -// return false; -// } -// } -// return super.isCacheable(resourceContext); -// } - public boolean isCacheable(ResourceContext resourceContext) { - return false; - } - } diff --git a/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js b/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js index 8cab6d8..ad6ea2a 100644 --- a/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js +++ b/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js @@ -719,7 +719,7 @@ var prevButton; $.each(this.split(','), function(j, buttonName) { if (buttonName == 'title') { - tr.append("

 

"); + tr.append("

 

"); if (prevButton) { prevButton.addClass(tm + '-corner-right'); } @@ -1115,7 +1115,7 @@ ' fc-today ' + tm + '-state-highlight' : ' fc-not-today') + "'>" + (showNumbers ? "
" + d.getDate() + "
" : '') + - "
 
"; + "
 
"; addDays(d, 1); if (nwe) { skipWeekend(d); @@ -1146,7 +1146,7 @@ tm + '-state-default fc-new fc-day' + (i * colCnt + j) + (j == dit ? ' fc-leftmost' : '') + "'>" + (showNumbers ? "
" : '') + - "
 
" + + "
 
" + ""; addDays(d, 1); if (nwe) { @@ -1833,7 +1833,7 @@ "" + "" + ""; + tm + "-state-default'> "; for (i = 0; i < colCnt; i++) { s += ""; + s += ""; if (options.allDaySlot) { s += "" + "" + "" + - "" + + "
 
" + + "" + ""; } @@ -1871,9 +1871,9 @@ s += ""; + tm + "-state-default'>
 
"; addMinutes(d, options.slotMinutes); slotCnt++; } @@ -1897,7 +1897,7 @@ tm + '-state-default ' + (!i ? 'fc-leftmost ' : '') + (+d == +today ? tm + '-state-highlight fc-today' : 'fc-not-today') + - "'>
 
"; + "'>
 
"; addDays(d, dis); if (nwe) { skipWeekend(d, dis); @@ -3873,11 +3873,11 @@ function htmlEscape(s) { return s - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/'/g, ''') - .replace(/"/g, '"'); + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"'); } diff --git a/src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js b/src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js index 9c0a831..9798b30 100644 --- a/src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js +++ b/src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js @@ -1,406 +1,559 @@ window.RichFaces = window.RichFaces || {}; -window.RichFaces.Schedule = (function() { +//TODO test ajax reRender +RichFaces.Schedule = function(id, locale, options, loadItemsEventName, itemSelectedEventName, itemMovedEventName, itemResizedEventName, viewChangedEventName, daySelectedEventName, dateRangeSelectedEventName, submitEventFunction) { + + var _this = this; + this.scheduleNode = document.getElementById(id); + this.scheduleNode.component = this; + if (!this.scheduleNode) { + throw "No element with id '" + id + "' found."; + } + + /** + * Message bundle setup. + */ + options = jQuery.extend(this.messages[locale], options); + + jQuery(document).ready(function() { + _this.delegate = jQuery(_this.scheduleNode).fullCalendar(options); + window.status = _this.delegate; + }); + + + // --- + /** + * Utility functions. + */ + // --- + /** + * Converts javascript date into integer that can be used as param + * for new Date(long) - milliseconds since epoch. + */ var formatDateParam = function(date) { return Math.round(date.getTime() / 1000); }; - - return function(id, locale, options, loadItemsEventName, itemSelectedEventName, itemMovedEventName, itemResizedEventName, viewChangedEventName, daySelectedEventName, dateRangeSelectedEventName, submitEventFunction) { - this.id = id; - var component; - /** - * submitEventFunction should have following params: - * event,view,eventType,itemId,startDate,endDate,dayDelta,minuteDelta,allDay,callback - */ - this.submitEventFunction = submitEventFunction; - options = jQuery.extend(this.messages[locale], options); - var elt = document.getElementById(id); - if (!elt) { - throw "No element with id '" + id + "' found."; + /** + * Compares two dates with with an accuracy of a day. + */ + var isSameDay = function(dateA, dateB) { + if (!dateA instanceof Date || !dateB instanceof Date) { + throw "Both params must be Date objects"; } - var _this = this; - var isSameDay = function(a, b) { - if (!a instanceof Date || !b instanceof Date) { - throw "Both params must be Date objects"; - } - return a.getYear() == b.getYear() - && a.getMonth() == b.getMonth() - && a.getDate() == b.getDate(); - }; - var fillCalendarFunction = function(startDate, endDate, callback) { - if (options.initialItems != null) { - var startDateData = options.initialItems.startDate; - var endDateData = options.initialItems.endDate; - var initialStartDate = new Date(startDateData.year, startDateData.month, startDateData.date); - var initialEndDate = new Date(endDateData.year, endDateData.month, endDateData.date); - if (isSameDay(startDate, initialStartDate) && isSameDay(endDate, initialEndDate)) { - callback(options.initialItems.items); - /** - * After initial load this should be cleaned so items are not cached. - */ - options.initialItems = null; - return; - } - } - if (options.onDateRangeChanged != null) { - RichFaces.Schedule.eval("(function(){" + options.onDateRangeChanged + "})()", { - 'startDate':startDate, - 'endDate':endDate - }); + return dateA.getYear() == dateB.getYear() + && dateA.getMonth() == dateB.getMonth() + && dateA.getDate() == dateB.getDate(); + }; + // --- + /** + * DELEGATE SETUP. + * Delegate (fullCalendar) needs callback methods + * for various events such as item clicking, dragging, resizing or loading + * items. + * Functions below can be overriden by ones declared in "options" parameter. + */ + // --- + /** + * Called by fullCalendar when it needs to load items - initial load, + * view type change, time navigation. + * If in ajax mode, then some initial items should be provided. + * If so then they are used for the first invocation of this function. + * This avoids creating additional ajax request on initial rendering. + * Custom users code cannot raise veto so any return statements there are + * ignored. + */ + var dateRangeChange = function(startDate, endDate, callback) { + var firstInvocation = options.initialItems != null; + if (firstInvocation) { + var startDateData = options.initialItems.startDate; + var endDateData = options.initialItems.endDate; + var initialStartDate = new Date(startDateData.year, startDateData.month, startDateData.date); + var initialEndDate = new Date(endDateData.year, endDateData.month, endDateData.date); + var items = options.initialItems.items; + /** + * After initial load this should be cleaned so items are not cached. + */ + options.initialItems = null; + /** + * In case the JSF component made a mistake in calculating initial + * date range we don't use initial items and just continue. + */ + if (isSameDay(startDate, initialStartDate) && isSameDay(endDate, initialEndDate)) { + callback(items); + return; } - if (_this.submitEventFunction != null) { - _this.submitEventFunction({} /* stub event */, - null, - loadItemsEventName, - null, - formatDateParam(startDate), - formatDateParam(endDate), - null, null, null, - function(request, event, data) { - var scheduleData = request.getJSON('_ajax:scheduleData') - if (scheduleData != undefined) { - callback(scheduleData); - } - if (options.onDateRangeChangedComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onDateRangeChangedComplete + "})()", { - 'startDate':startDate, - 'endDate':endDate, - 'request':request, - 'data':data, - 'items':scheduleData - }); - } + } + if (!firstInvocation && options.onDateRangeChanged != null) { + RichFaces.Schedule.eval("(function(){" + options.onDateRangeChanged + "})()", { + 'startDate':startDate, + 'endDate':endDate + }); + } + if (submitEventFunction != null) { + submitEventFunction({} /* stub event */, + null, + loadItemsEventName, + null, + formatDateParam(startDate), + formatDateParam(endDate), + null, null, null, + function(request, event, data) { + var scheduleData = request.getJSON('_ajax:scheduleData') + if (scheduleData != undefined) { + callback(scheduleData); } - ); - } - }; - var itemDragStart = function(item, event, ui, view) { - if (options.onItemDragStart != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemDragStart + "})()", { - 'item':item, - 'event':event, - 'ui':ui, - 'view':view - }); - } - }; - var itemDragStop = function(item, event, ui, view) { - if (options.onItemDragStop != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemDragStop + "})()", { - 'item':item, - 'event':event, - 'ui':ui, - 'view':view - }); - } - }; - var itemDrop = function(item, dayDelta, minuteDelta, allDay, revertFunc, event, ui, view) { - var result; - if (options.onItemDrop != null) { - result = RichFaces.Schedule.eval("(function(){" + options.onItemDrop + "})()", { - 'item':item, - 'dayDelta':dayDelta, - 'minuteDelta':minuteDelta, - 'allDay':allDay, - 'event':event, - 'ui':ui, - 'view':view - }); - if (result === false) { - revertFunc(); - return; - } - } - if (_this.submitEventFunction != null) { - _this.submitEventFunction(event, - null, - itemMovedEventName, - item.id, - null, - null, - dayDelta, minuteDelta, allDay, - function(request, event, data) { - var decision = request.getJSON('_ajax:scheduleData'); - var vetoed = false; - if (decision != undefined && decision !== true) { - revertFunc(); - vetoed = true; - } - if (options.onItemDropComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemDropComplete + "})()", { - 'item':item, - 'dayDelta':dayDelta, - 'minuteDelta':minuteDelta, - 'allDay':allDay, - 'event':event, - 'ui':ui, - 'view':view, - 'request':request, - 'data':data, - 'vetoed':vetoed - }); - } + if (options.onDateRangeChangedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onDateRangeChangedComplete + "})()", { + 'startDate':startDate, + 'endDate':endDate, + 'request':request, + 'data':data, + 'items':scheduleData + }); } - ); - } - return; - }; - var itemResized = function(item, dayDelta, minuteDelta, revertFunc, event, ui, view) { - var result; - if (options.onItemResized != null) { - result = RichFaces.Schedule.eval("(function(){" + options.onItemResized + "})()", { - 'item':item, - 'dayDelta':dayDelta, - 'minuteDelta':minuteDelta, - 'event':event, - 'ui':ui, - 'view':view - }); - if (result === false) { - revertFunc(); - return; - } + } + ); + } + }; + /** + * Called by fullCalendar when item has started to be dragged. + */ + var itemDragStart = function(item, event, ui, view) { + if (options.onItemDragStart != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemDragStart + "})()", { + 'item':item, + 'event':event, + 'ui':ui, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when item has stopped to be dragged. + * This is invoked between itemDragStart and itemDrop. + */ + var itemDragStop = function(item, event, ui, view) { + if (options.onItemDragStop != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemDragStop + "})()", { + 'item':item, + 'event':event, + 'ui':ui, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when item was dropped (dragging finished). + * This is invoked after itemDragStop. + * Custom users code may raise veto by returning "false". In such case + * changes will be reverted and no event will be sent to server. + */ + var itemDrop = function(item, dayDelta, minuteDelta, allDay, revertFunc, event, ui, view) { + var result; + if (options.onItemDrop != null) { + result = RichFaces.Schedule.eval("(function(){" + options.onItemDrop + "})()", { + 'item':item, + 'dayDelta':dayDelta, + 'minuteDelta':minuteDelta, + 'allDay':allDay, + 'event':event, + 'ui':ui, + 'view':view + }); + if (result === false) { + revertFunc(); + return; } - if (_this.submitEventFunction != null) { - _this.submitEventFunction(event, - null, - itemResizedEventName, - item.id, - null, - null, - dayDelta, minuteDelta, null, - function(request, event, data) { - var decision = request.getJSON('_ajax:scheduleData'); - var vetoed = false; - if (decision != undefined && decision !== true) { - revertFunc(); - vetoed = true; - } - if (options.onItemResizedComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemResizedComplete + "})()", { - 'item':item, - 'dayDelta':dayDelta, - 'minuteDelta':minuteDelta, - 'event':event, - 'ui':ui, - 'view':view, - 'request':request, - 'data':data, - 'vetoed':vetoed - }); - } + } + if (submitEventFunction != null) { + submitEventFunction(event, + null, + itemMovedEventName, + item.id, + null, + null, + dayDelta, minuteDelta, allDay, + function(request, event, data) { + var decision = request.getJSON('_ajax:scheduleData'); + var vetoed = false; + if (decision != undefined && decision !== true) { + revertFunc(); + vetoed = true; } - ); - } - }; - var itemResizeStart = function(item, event, ui, view) { - if (options.onItemResizeStart != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemResizeStart + "})()", { - 'item':item, - 'event':event, - 'ui':ui, - 'view':view - }); - } - }; - var itemResizeStop = function(item, event, ui, view) { - if (options.onItemResizeStop != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemResizeStop + "})()", { - 'item':item, - 'event':event, - 'ui':ui, - 'view':view - }); - } - }; - var itemMouseover = function(item, event, view) { - if (options.onItemMouseover != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemMouseover + "})()", { - 'item':item, - 'event':event, - 'view':view - }); - } - }; - var itemMouseout = function(item, event, view) { - if (options.onItemMouseout != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemMouseout + "})()", { - 'item':item, - 'event':event, - 'view':view - }); + if (options.onItemDropComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemDropComplete + "})()", { + 'item':item, + 'dayDelta':dayDelta, + 'minuteDelta':minuteDelta, + 'allDay':allDay, + 'event':event, + 'ui':ui, + 'view':view, + 'request':request, + 'data':data, + 'vetoed':vetoed + }); + } + } + ); + } + return; + }; + /** + * Called by fullCalendar when item has started to be resized. + */ + var itemResizeStart = function(item, event, ui, view) { + if (options.onItemResizeStart != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemResizeStart + "})()", { + 'item':item, + 'event':event, + 'ui':ui, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when item has stopped to be resized. + * This is invoked between itemResizeStart and itemResized. + */ + var itemResizeStop = function(item, event, ui, view) { + if (options.onItemResizeStop != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemResizeStop + "})()", { + 'item':item, + 'event':event, + 'ui':ui, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when item was resized. + * This is invoked after itemResizeStop. + * Custom users code may raise veto by returning "false". In such case + * changes will be reverted and no event will be sent to server. + */ + var itemResized = function(item, dayDelta, minuteDelta, revertFunc, event, ui, view) { + var result; + if (options.onItemResized != null) { + result = RichFaces.Schedule.eval("(function(){" + options.onItemResized + "})()", { + 'item':item, + 'dayDelta':dayDelta, + 'minuteDelta':minuteDelta, + 'event':event, + 'ui':ui, + 'view':view + }); + if (result === false) { + revertFunc(); + return; } - }; - var itemClick = function(item, event, view) { - var result; - if (options.onItemSelected != null) { - result = RichFaces.Schedule.eval("(function(){" + options.onItemSelected + "})()", { - 'item':item, - 'event':event, - 'view':view - }); + } + if (submitEventFunction != null) { + submitEventFunction(event, + null, + itemResizedEventName, + item.id, + null, + null, + dayDelta, minuteDelta, null, + function(request, event, data) { + var decision = request.getJSON('_ajax:scheduleData'); + var vetoed = false; + if (decision != undefined && decision !== true) { + revertFunc(); + vetoed = true; + } + if (options.onItemResizedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemResizedComplete + "})()", { + 'item':item, + 'dayDelta':dayDelta, + 'minuteDelta':minuteDelta, + 'event':event, + 'ui':ui, + 'view':view, + 'request':request, + 'data':data, + 'vetoed':vetoed + }); + } + } + ); + } + }; + /** + * Called by fullCalendar when mouse moves over item. + */ + var itemMouseover = function(item, event, view) { + if (options.onItemMouseover != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemMouseover + "})()", { + 'item':item, + 'event':event, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when mouse leaves item. + */ + var itemMouseout = function(item, event, view) { + if (options.onItemMouseout != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemMouseout + "})()", { + 'item':item, + 'event':event, + 'view':view + }); + } + }; + /** + * Called by fullCalendar when item is clicked. + * Custom users code may return "false". In such case + * changes no event will be sent to server and false will be returned + * to fullCalendar, which will prevent redirecting to URL associated + * with item (if such url was defined for the item). + */ + var itemClick = function(item, event, view) { + var result; + if (options.onItemSelected != null) { + result = RichFaces.Schedule.eval("(function(){" + options.onItemSelected + "})()", { + 'item':item, + 'event':event, + 'view':view + }); + } + if (result === false) { + return false; + } + if (submitEventFunction != null) { + submitEventFunction(event, + null, + itemSelectedEventName, + item.id, + null, null, null, null, null, function(request, event, data) { + if (options.onItemSelectedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onItemSelectedComplete + "})()", { + 'item':item, + 'event':event, + 'view':view, + 'request':request, + 'data':data + }); + } } + ); + } + return result; + }; + /** + * Called by fullCalendar when day is clicked. + * Custom users code may raise veto by returning "false". In such case + * changes will be reverted and no event will be sent to server. + */ + var dayClick = function(date, allDay, event, view) { + if (options.onDateSelected != null) { + var result = RichFaces.Schedule.eval("(function(){" + options.onDateSelected + "})()", { + 'date':date, + 'allDay':allDay, + 'event':event, + 'view':view + }); if (result === false) { - return false; + return; } - if (_this.submitEventFunction != null) { - _this.submitEventFunction(event, - null, - itemSelectedEventName, - item.id, - null, null, null, null, null, function(request, event, data) { - if (options.onItemSelectedComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onItemSelectedComplete + "})()", { - 'item':item, - 'event':event, - 'view':view, - 'request':request, - 'data':data - }); - } + } + if (submitEventFunction != null) { + submitEventFunction(event, + null, + daySelectedEventName, + null, formatDateParam(date), null, null, null, allDay, function(request, event, data) { + if (options.onDateSelectedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onDateSelectedComplete + "})()", { + 'date':date, + 'allDay':allDay, + 'request':request, + 'event':event, + 'view':view, + 'data':data + }); } - ); } - return result; - }; - var dayClick = function(date, allDay, event, view) { - var result; - if (options.onDateSelected != null) { - result = RichFaces.Schedule.eval("(function(){" + options.onDateSelected + "})()", { - 'date':date, - 'allDay':allDay, - 'event':event, + ); + } + }; + var selectedView; + /** + * Called by fullCalendar when view or dates change. + * We want to notify user only about view change, so we cache current view + * on private variable "selectedView" and compare it with value passed + * in parameter. + * Custom users code may not raise veto so any "return" statements are + * ignored. + */ + var viewChanged = function(view) { + if (selectedView != view && selectedView != undefined) { + if (options.onViewChanged != null) { + RichFaces.Schedule.eval("(function(){" + options.onViewChanged + "})()", { 'view':view }); } - if (result === false) { - return false; - } - if (_this.submitEventFunction != null) { - _this.submitEventFunction(event, - null, - daySelectedEventName, - null, formatDateParam(date), null, null, null, allDay, function(request, event, data) { - if (options.onDateSelectedComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onDateSelectedComplete + "})()", { - 'date':date, - 'allDay':allDay, - 'request':request, - 'event':event, + if (submitEventFunction != null) { + submitEventFunction({}, + view.name, + viewChangedEventName, + null, null, null, null, null, null, function(request, event, data) { + if (options.onViewChangedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onViewChangedComplete + "})()", { 'view':view, + 'request':request, 'data':data }); } } ); } - return result; - }; - var selectedView; - var viewChanged = function(view) { - if (selectedView != view && selectedView != undefined) { - if (options.onViewChanged != null) { - RichFaces.Schedule.eval("(function(){" + options.onViewChanged + "})()", { - 'view':view - }); - } - if (_this.submitEventFunction != null) { - _this.submitEventFunction({}, - view.name, - viewChangedEventName, - null, null, null, null, null, null, function(request, event, data) { - if (options.onViewChangedComplete != null) { - RichFaces.Schedule.eval("(function(){" + options.onViewChangedComplete + "})()", { + } + selectedView = view; + }; + /** + * Called by fullCalendar when some date range is selected (user clicks + * and drags over empty time cells). + * Custom users code may raise veto by returning "false". In such case + * changes will be reverted and no event will be sent to server. + * What is more, selection will be cleared at the end of this function. + * (This is bad, i guess, but for now i don't see other way to + * hide selection marker after selection was made, event sent to server, + * and server side listeners have created new event for that selection. + * If no unselect would happen then we selection helper would still be there + * and mess the looks) + */ + var dateRangeSelected = function(startDate, endDate, allDay, view) { + if (!_this.delegate.fullCalendar('option', 'selectable')) { + return; + } + var result; + if (options.onDateRangeSelected != null) { + result = RichFaces.Schedule.eval("(function(){" + options.onDateRangeSelected + "})()", { + 'startDate':startDate, + 'endDate':endDate, + 'allDay':allDay, + 'view':view + }); + } + if (result === false) { + return; + } + if (submitEventFunction != null) { + submitEventFunction({}, + null, + dateRangeSelectedEventName, + null, formatDateParam(startDate), formatDateParam(endDate), null, null, allDay, + function(request, event, data) { + _this.refetchItems(); + if (options.onDateRangeSelectedComplete != null) { + RichFaces.Schedule.eval("(function(){" + options.onDateRangeSelectedComplete + "})()", { + 'startDate':startDate, + 'endDate':endDate, + 'allDay':allDay, 'view':view, 'request':request, 'data':data }); } } - ); - } - } - selectedView = view; - }; - var onDateRangeSelected = function(startDate, endDate, allDay, view) { - if (!component.fullCalendar('option', 'selectable')) { - return false; - } - var result; - if (options.onDateRangeSelected != null) { - result = RichFaces.Schedule.eval("(function(){" + options.onDateRangeSelected + "})()", { - 'startDate':startDate, - 'endDate':endDate, - 'allDay':allDay, - 'view':view - }); - } - if (result === false) { - /** - * Unselect must be in all three places below in order to - * avoid situation where, while event is being sent via ajax - * to server, client side selection disapeares. - * Current implementation shortens delay between selection - * disapearing and potentialy created event appearing. - */ - component.fullCalendar('unselect'); - return false; - } - if (_this.submitEventFunction != null) { - _this.submitEventFunction({}, - null, - dateRangeSelectedEventName, - null, formatDateParam(startDate), formatDateParam(endDate), null, null, allDay, - function(request, event, data) { - component.fullCalendar('refetchEvents'); - component.fullCalendar('unselect'); - if (options.onDateRangeSelected != null) { - RichFaces.Schedule.eval("(function(){" + options.onDateRangeSelectedComplete + "})()", { - 'startDate':startDate, - 'endDate':endDate, - 'allDay':allDay, - 'view':view, - 'request':request, - 'data':data - }); - } - } - ); - } else { - component.fullCalendar('unselect'); - } - return result; - }; - options = jQuery.extend({ - events: fillCalendarFunction, - eventDragStart: itemDragStart, - eventDragStop: itemDragStop, - eventDrop: itemDrop, - eventResizeStart: itemResizeStart, - eventResizeStop: itemResizeStop, - eventResize: itemResized, - eventClick: itemClick, - eventMouseover: itemMouseover, - eventMouseout: itemMouseout, - viewDisplay: viewChanged, - dayClick: dayClick, - select: onDateRangeSelected - }, options); - jQuery(document).ready(function() { - component = jQuery(elt).fullCalendar(options); - elt.component = _this; - _this.component = component; - }); + ); + } }; + options = jQuery.extend({ + events: dateRangeChange, + eventDragStart: itemDragStart, + eventDragStop: itemDragStop, + eventDrop: itemDrop, + eventResizeStart: itemResizeStart, + eventResizeStop: itemResizeStop, + eventResize: itemResized, + eventClick: itemClick, + eventMouseover: itemMouseover, + eventMouseout: itemMouseout, + viewDisplay: viewChanged, + dayClick: dayClick, + select: dateRangeSelected + }, options); -}()); -window.RichFaces.Schedule.prototype.refetchItems = function() { - this.component.fullCalendar('refetchEvents'); }; -window.RichFaces.Schedule.prototype.messages = {}; -window.RichFaces.Schedule.eval = function(template, object) { +RichFaces.Schedule.prototype.messages = {}; +/** + * This function evaluates code in template with object in ScopeChain. + * This is usefull if you need to evaluate code that uses member names + * that colide with external names that the code refers to. + * There is almost exact method in utils.js called Richfaces.eval, + * but it swallows exception thrown during evaluation, which makes debugging + * hard. + */ +RichFaces.Schedule.eval = function(template, object) { var value = ''; with (object) { value = eval(template); } return value; -}; \ No newline at end of file +} +RichFaces.Schedule.prototype.select = function(startDate, endDate, allDay) { + this.delegate.fullCalendar('select', startDate, endDate, allDay); +} +RichFaces.Schedule.prototype.unselect = function() { + this.delegate.fullCalendar('unselect'); +} +RichFaces.Schedule.prototype.render = function() { + this.delegate.fullCalendar('render'); +} +RichFaces.Schedule.prototype.destroy = function() { + this.delegate.fullCalendar('destroy'); +} +RichFaces.Schedule.prototype.getView = function() { + return this.delegate.fullCalendar('getView'); +} +RichFaces.Schedule.prototype.changeView = function(viewName) { + this.delegate.fullCalendar('changeView', viewName); +} +RichFaces.Schedule.prototype.prev = function() { + this.delegate.fullCalendar('prev'); +} +RichFaces.Schedule.prototype.next = function() { + this.delegate.fullCalendar('next'); +} +RichFaces.Schedule.prototype.prevYear = function() { + this.delegate.fullCalendar('prevYear'); +} +RichFaces.Schedule.prototype.nextYear = function() { + this.delegate.fullCalendar('nextYear'); +} +RichFaces.Schedule.prototype.today = function() { + this.delegate.fullCalendar('today'); +} +RichFaces.Schedule.prototype.gotoDate = function(year, month, date) { + this.delegate.fullCalendar('gotoDate', year, month, date); +} +RichFaces.Schedule.prototype.incrementDate = function(years, months, days) { + this.delegate.fullCalendar('incrementDate', years, months, days); +} +RichFaces.Schedule.prototype.updateItem = function(item) { + this.delegate.fullCalendar('updateItem', item); +} +RichFaces.Schedule.prototype.getItems = function(idOrFilter) { + return this.delegate.fullCalendar('clientEvents', idOrFilter); +} +RichFaces.Schedule.prototype.removeItems = function(idOrFilter) { + this.delegate.fullCalendar('removeEvents', idOrFilter); +} +RichFaces.Schedule.prototype.refetchItems = function() { + this.delegate.fullCalendar('refetchEvents'); +} +RichFaces.Schedule.prototype.addItemsSource = function(source) { + this.delegate.fullCalendar('addEventSource', source); +} +RichFaces.Schedule.prototype.removeItemsSource = function(source) { + this.delegate.fullCalendar('removeEventSource', source); +} +RichFaces.Schedule.prototype.addItem = function(event, stick) { + this.delegate.fullCalendar('renderEvent', event, stick); +} +RichFaces.Schedule.prototype.reRender = function() { + this.delegate.fullCalendar('rerenderEvents'); +} \ No newline at end of file diff --git a/src/main/templates/org/richfaces/htmlSchedule.jspx b/src/main/templates/org/richfaces/htmlSchedule.jspx index d2a78b3..7cb83a5 100644 --- a/src/main/templates/org/richfaces/htmlSchedule.jspx +++ b/src/main/templates/org/richfaces/htmlSchedule.jspx @@ -16,7 +16,6 @@ org.ajax4jsf.javascript.AjaxScript, /org/ajax4jsf/javascript/scripts/form.js, /org/richfaces/renderkit/html/scripts/form.js, - /org/richfaces/renderkit/html/scripts/utils.js, /org/richfaces/renderkit/html/scripts/jquery/jquery.js, /org/richfaces/renderkit/html/scripts/ui.core.js, /org/richfaces/renderkit/html/scripts/ui.draggable.js,
  
 
" + options.allDayText + "" + - "
 
  
" + - ((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') + + ((!slotNormal || !minutes) ? formatDate(d, options.axisFormat) : ' ') + "