From 327ce8965c1f45941b5301af893643e7df049842 Mon Sep 17 00:00:00 2001 From: Bernard Labno Date: Mon, 7 Jun 2010 20:28:11 +0000 Subject: [PATCH] Schedule initial import --- pom.xml | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/config/component/README | 0 src/main/config/component/commonViewAttributes.ent | 43 +++++++++++++++++++++++++++++++++++++++++++ src/main/config/component/listeners.ent | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/config/component/schedule.xml | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/config/component/scheduleAgendaDayView.xml | 33 +++++++++++++++++++++++++++++++++ src/main/config/component/scheduleAgendaWeekView.xml | 33 +++++++++++++++++++++++++++++++++ src/main/config/component/scheduleBasicDayView.xml | 33 +++++++++++++++++++++++++++++++++ src/main/config/component/scheduleBasicWeekView.xml | 33 +++++++++++++++++++++++++++++++++ src/main/config/component/scheduleItem.xml | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/config/component/scheduleMonthView.xml | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/config/resources/resources-config.xml | 6 ++++++ src/main/java/org/richfaces/component/README | 0 src/main/java/org/richfaces/component/ScheduleCommonViewAttributes.java | 20 ++++++++++++++++++++ src/main/java/org/richfaces/component/UISchedule.java | 609 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/UIScheduleAgendaDayView.java | 9 +++++++++ src/main/java/org/richfaces/component/UIScheduleAgendaWeekView.java | 9 +++++++++ src/main/java/org/richfaces/component/UIScheduleBasicDayView.java | 9 +++++++++ src/main/java/org/richfaces/component/UIScheduleBasicWeekView.java | 9 +++++++++ src/main/java/org/richfaces/component/UIScheduleItem.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/UIScheduleMonthView.java | 13 +++++++++++++ src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedEvent.java | 43 +++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedListener.java | 8 ++++++++ src/main/java/org/richfaces/component/event/ScheduleDateSelectedEvent.java | 39 +++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleDateSelectedListener.java | 8 ++++++++ src/main/java/org/richfaces/component/event/ScheduleItemMoveEvent.java | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleItemMoveListener.java | 8 ++++++++ src/main/java/org/richfaces/component/event/ScheduleItemResizeEvent.java | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleItemResizeListener.java | 8 ++++++++ src/main/java/org/richfaces/component/event/ScheduleItemSelectedEvent.java | 32 ++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleItemSelectedListener.java | 8 ++++++++ src/main/java/org/richfaces/component/event/ScheduleListenerEventsProducer.java | 28 ++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleViewChangedEvent.java | 32 ++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/component/event/ScheduleViewChangedListener.java | 8 ++++++++ src/main/java/org/richfaces/component/model/DateRange.java | 34 ++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java | 291 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/org/richfaces/taglib/ScheduleTagHandlerBase.java | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/component/scheduleMessages.properties | 1 + src/main/resources/org/richfaces/renderkit/html/css/fullcalendar.css | 575 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js | 3418 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/gcal.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/ui.core.js | 519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/ui.draggable.js | 766 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/org/richfaces/renderkit/html/scripts/ui.resizable.js | 800 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/templates/README | 0 src/main/templates/org/richfaces/htmlSchedule.jspx | 38 ++++++++++++++++++++++++++++++++++++++ src/test/java/org/richfaces/component/JSFComponentTest.java | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/test/resources/README | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/test/resources/displayedDaysForDayViewCalculationTestData.txt | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/test/resources/displayedDaysForMonthViewCalculationTestData.txt | 3547 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/test/resources/displayedDaysForWeekViewCalculationTestData.txt | 792 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 53 files changed, 13632 insertions(+), 0 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/config/component/README create mode 100644 src/main/config/component/commonViewAttributes.ent create mode 100644 src/main/config/component/listeners.ent create mode 100644 src/main/config/component/schedule.xml create mode 100644 src/main/config/component/scheduleAgendaDayView.xml create mode 100644 src/main/config/component/scheduleAgendaWeekView.xml create mode 100644 src/main/config/component/scheduleBasicDayView.xml create mode 100644 src/main/config/component/scheduleBasicWeekView.xml create mode 100644 src/main/config/component/scheduleItem.xml create mode 100644 src/main/config/component/scheduleMonthView.xml create mode 100644 src/main/config/resources/resources-config.xml create mode 100644 src/main/java/org/richfaces/component/README create mode 100644 src/main/java/org/richfaces/component/ScheduleCommonViewAttributes.java create mode 100644 src/main/java/org/richfaces/component/UISchedule.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleAgendaDayView.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleAgendaWeekView.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleBasicDayView.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleBasicWeekView.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleItem.java create mode 100644 src/main/java/org/richfaces/component/UIScheduleMonthView.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedListener.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleDateSelectedEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleDateSelectedListener.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemMoveEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemMoveListener.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemResizeEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemResizeListener.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemSelectedEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleItemSelectedListener.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleListenerEventsProducer.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleViewChangedEvent.java create mode 100644 src/main/java/org/richfaces/component/event/ScheduleViewChangedListener.java create mode 100644 src/main/java/org/richfaces/component/model/DateRange.java create mode 100644 src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java create mode 100644 src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java create mode 100644 src/main/java/org/richfaces/taglib/ScheduleTagHandlerBase.java create mode 100644 src/main/resources/org/richfaces/component/scheduleMessages.properties create mode 100644 src/main/resources/org/richfaces/renderkit/html/css/fullcalendar.css create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/gcal.js create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/richfaces.schedule.js create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/ui.core.js create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/ui.draggable.js create mode 100644 src/main/resources/org/richfaces/renderkit/html/scripts/ui.resizable.js create mode 100644 src/main/templates/README create mode 100644 src/main/templates/org/richfaces/htmlSchedule.jspx create mode 100644 src/test/java/org/richfaces/component/JSFComponentTest.java create mode 100644 src/test/resources/README create mode 100644 src/test/resources/displayedDaysForDayViewCalculationTestData.txt create mode 100644 src/test/resources/displayedDaysForMonthViewCalculationTestData.txt create mode 100644 src/test/resources/displayedDaysForWeekViewCalculationTestData.txt diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..e62761b --- /dev/null +++ b/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + ui + org.richfaces.sandbox + 3.3.3.Final + + org.richfaces.sandbox.ui + schedule + schedule + + + + org.richfaces.cdk + maven-cdk-plugin + ${project.version} + + + generate-sources + + generate + + + + generate-test-sources + generate-test-sources + + generate-tests + + + + + + org.richfaces.ui + + schedule + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + displayedDaysForMonthViewCalculationTestData + ${basedir}/src/test/resources/displayedDaysForMonthViewCalculationTestData.txt + + + displayedDaysForWeekViewCalculationTestData + ${basedir}/src/test/resources/displayedDaysForWeekViewCalculationTestData.txt + + + displayedDaysForDayViewCalculationTestData + ${basedir}/src/test/resources/displayedDaysForDayViewCalculationTestData.txt + + + + + + + + + org.richfaces.framework + richfaces-impl + ${project.version} + + + diff --git a/src/main/config/component/README b/src/main/config/component/README new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/main/config/component/README diff --git a/src/main/config/component/commonViewAttributes.ent b/src/main/config/component/commonViewAttributes.ent new file mode 100644 index 0000000..8a77f0e --- /dev/null +++ b/src/main/config/component/commonViewAttributes.ent @@ -0,0 +1,43 @@ + + timeFormat + java.lang.String + + Determines the time-text that will be displayed on each event. + Time-text will only be displayed for Event Objects that have allDay equal to false. + default for agendaDay and agendaWeek: 'h:mm{ - h:mm}', // 5:00 - 6:30 + default for all others: 'h(:mm)t' // 7p + + null + + + columnFormat + java.lang.String + + Determines the text that will be displayed on the calendar's column headings. + default for month: 'ddd', // Mon + default for week: 'ddd M/d', // Mon 9/7 + default for day: 'dddd M/d' // Monday 9/7 + + null + + + titleFormat + java.lang.String + + Determines the text that will be displayed in the header's title. + default for month: 'MMMM yyyy', // September 2009 + default for week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", // Sep 7 - 13 2009 + default for day: 'dddd, MMM d, yyyy' // Tuesday, Sep 8, 2009 + + null + + + dragOpacity + java.lang.Double + + The opacity of an event while it is being dragged. + default for agendaWeek: .5 + default for other views: 1 + + null + \ No newline at end of file diff --git a/src/main/config/component/listeners.ent b/src/main/config/component/listeners.ent new file mode 100644 index 0000000..d52fcdc --- /dev/null +++ b/src/main/config/component/listeners.ent @@ -0,0 +1,215 @@ + + itemSelectedListener + + binding + org.richfaces.scheduleItemSelectedListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleItemSelectedListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleItemSelectedEvent + + + + org.richfaces.taglib.ScheduleItemSelectedListenerTagHandler + + + + + org.richfaces.taglib.ItemSelectedListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + + + + itemMoveListener + + binding + org.richfaces.scheduleItemMoveListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleItemMoveListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleItemMoveEvent + + + + org.richfaces.taglib.ScheduleItemMoveListenerTagHandler + + + + + org.richfaces.taglib.ItemMoveListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + + + + itemResizeListener + + binding + org.richfaces.scheduleItemResizeListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleItemResizeListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleItemResizeEvent + + + + org.richfaces.taglib.ScheduleItemResizeListenerTagHandler + + + + + org.richfaces.taglib.ItemResizeListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + + + + viewChangedListener + + binding + org.richfaces.scheduleViewChangedListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleViewChangedListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleViewChangedEvent + + + + org.richfaces.taglib.ScheduleViewChangedListenerTagHandler + + + + + org.richfaces.taglib.ViewChangedListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + + + + dateRangeChangedListener + + binding + org.richfaces.scheduleDateRangeChangedListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleDateRangeChangedListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleDateRangeChangedEvent + + + + org.richfaces.taglib.ScheduleDateRangeChangedListenerTagHandler + + + + + org.richfaces.taglib.DateRangeChangedListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + + + + dateSelectedListener + + binding + org.richfaces.scheduleDateSelectedListener + The attribute takes a value-binding expression for a component property of + a backing bean + + + + org.richfaces.component.event.ScheduleDateSelectedListener + + + org.richfaces.component.event.ScheduleListenerEventsProducer + + + org.richfaces.component.event.ScheduleDateSelectedEvent + + + + org.richfaces.taglib.ScheduleDateSelectedListenerTagHandler + + + + + org.richfaces.taglib.DateSelectedListenerTag + + + + + type + java.lang.String + The fully qualified Java class name for the listener + + \ No newline at end of file diff --git a/src/main/config/component/schedule.xml b/src/main/config/component/schedule.xml new file mode 100644 index 0000000..66b8c49 --- /dev/null +++ b/src/main/config/component/schedule.xml @@ -0,0 +1,537 @@ + + + + ] + > + + + org.richfaces.Schedule + org.richfaces.Schedule + org.richfaces.component.html.HtmlSchedule + org.richfaces.component.UISchedule + + + + + org.richfaces.renderkit.ScheduleRenderer + + org.richfaces.renderkit.ScheduleRendererBase + + + schedule + org.richfaces.taglib.ScheduleTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + org.richfaces.taglib.ScheduleTagHandler + org.richfaces.taglib.ScheduleTagHandlerBase + + + &ui_component_attributes; + &commonViewAttributes; + + + + + switchType + java.lang.String + + Available options: +
    +
  • ajax
  • +
  • server
  • +
  • client
  • +
+
+ "ajax" +
+ + view + java.lang.String + + Schedule has a number of different "views", + or ways of displaying days and events. + The following 5 views are all built in to schedule: +
    +
  • month
  • +
  • basicWeek
  • +
  • basicDay
  • +
  • agendaWeek
  • +
  • agendaDay
  • +
+ default: 'month' +
+ null +
+ + headerLeft + java.lang.String + + Defines the buttons and title at the top of the calendar. + Values separated by a comma will be displayed adjacently. + Values separated by a space will be displayed with a small gap in between. + Strings can contain any of the following values: +
    +
  • + title + - text containing the current month/week/day +
  • +
  • + prev + - button for moving the calendar back one month/week/day +
  • +
  • + next + - button for moving the calendar forward one month/week/day +
  • +
  • + prevYear + - button for moving the calendar back on year +
  • +
  • + nextYear + - button for moving the calendar forward one year +
  • +
  • + today + - button for moving the calendar to the current month/week/day +
  • +
  • + a view name + - button that will switch the calendar to any of the available views; see defaultView +
  • +
+ default: 'title' +
+ null +
+ + headerCenter + java.lang.String + + Defines the buttons and title at the top of the calendar. + Values separated by a comma will be displayed adjacently. + Values separated by a space will be displayed with a small gap in between. + Strings can contain any of the following values: +
    +
  • + title + - text containing the current month/week/day +
  • +
  • + prev + - button for moving the calendar back one month/week/day +
  • +
  • + next + - button for moving the calendar forward one month/week/day +
  • +
  • + prevYear + - button for moving the calendar back on year +
  • +
  • + nextYear + - button for moving the calendar forward one year +
  • +
  • + today + - button for moving the calendar to the current month/week/day +
  • +
  • + a view name + - button that will switch the calendar to any of the available views; see defaultView +
  • +
+ default: '' +
+ null +
+ + headerRight + java.lang.String + + Defines the buttons and title at the top of the calendar. + Values separated by a comma will be displayed adjacently. + Values separated by a space will be displayed with a small gap in between. + Strings can contain any of the following values: +
    +
  • + title + - text containing the current month/week/day +
  • +
  • + prev + - button for moving the calendar back one month/week/day +
  • +
  • + next + - button for moving the calendar forward one month/week/day +
  • +
  • + prevYear + - button for moving the calendar back on year +
  • +
  • + nextYear + - button for moving the calendar forward one year +
  • +
  • + today + - button for moving the calendar to the current month/week/day +
  • +
  • + a view name + - button that will switch the calendar to any of the available views; see defaultView +
  • +
+ default: 'today prev,next' +
+ null +
+ + allDaySlot + java.lang.Boolean + + Determines if the "all-day" slot is displayed at the top of the calendar. + When hidden with false, all-day events will not be displayed in agenda views. + default: true + + null + + + allDayText + java.lang.String + + The text titling the "all-day" slot at the top of the calendar. + default: 'all-day' + + null + + + axisFormat + java.lang.String + + Determines the time-text that will be displayed on the vertical axis of the agenda views. + The default value will produce times that look like "5pm" and "5:30pm". + default: 'h(:mm)tt' + + null + + + slotMinutes + java.lang.Integer + + The frequency for displaying time slots, in minutes. + The default will make a slot every half hour. + default: 30 + + null + + + defaultEventMinutes + java.lang.Integer + + Determines the length (in minutes) an event appears to be when it has an unspecified end date. + By default, if an Event Object has no end, it will appear to be 2 hours. + This option only affects events that appear in the agenda slots, meaning they have allDay set to true. + default: 120 + + null + + + firstHour + java.lang.Integer + + Determines the first hour that will be visible in the scroll pane. + Values must be from 0-23, where 0=midnight, 1=1am, etc. + The user will be able to scroll upwards to see events before this time. + If you want to prevent users from doing this, use the minTime option instead. + default: 6 + + null + + + minTime + java.lang.String + + Determines the first hour/time that will be displayed, even when the scrollbars have been scrolled all + the way up. + This can be a number like 5 (which means 5am), a string like '5:30' (which means 5:30am) or a string + like '5:30am'. + default: 0 + + null + + + maxTime + java.lang.String + + Determines the last hour/time (exclusively) that will be displayed, even when the scrollbars have been + scrolled all the way down. + This can be a number like 22 (which means 10pm), a string like '22:30' (which means 10:30pm) or a string + like '10:30pm'. + default: 24 + + null + + + firstDay + java.lang.Integer + + The day that each week begins. + The value must be a number that represents the day of the week. + Sunday=0, Monday=1, Tuesday=2, etc. + default: 0 + + null + + + isRTL + java.lang.Boolean + + Displays the calendar in right-to-left mode. + default: false + + null + + + showWeekends + java.lang.Boolean + + Whether to include Saturday/Sunday columns in any of the calendar views. + default: true + + null + + + height + java.lang.Integer + + Will make the entire calendar (including header) a pixel height. + By default, this option is unset and the calendar's height is calculated by aspectRatio. + + null + + + contentHeight + java.lang.Integer + + Will make the calendar's content area a pixel height. + By default, this option is unset and the calendar's height is calculated by aspectRatio. + + null + + + aspectRatio + java.lang.Double + + Determines the width-to-height aspect ratio of the calendar. + A calendar is a block-level element that fills its entire avaiable width. The calendar’s height, + however, is determined by this ratio of width-to-height. (Hint: larger numbers make smaller heights). + default: 1.35 + + null + + + allDayByDefault + java.lang.Boolean + + Determines the default value for each Event Object's allDay property, when it is unspecified. + default: true + + null + + + + editable + java.lang.Boolean + + Determines whether the events on the calendar can be modified. + This determines if the events can be dragged and resized. + Enables/disables both at the same time. + If you don't want both, use editable in conjunction with disableDragging and disableResizing. + This option can be overridden on a per-event basis with the Event Object editable property. + default: false + + null + + + disableDragging + java.lang.Boolean + + Disables all event dragging, even when events are editable. + default: false + + null + + + disableResizing + java.lang.Boolean + + Disables all event resizing, even when events are editable. + default: false + + null + + + dragRevertDuration + java.lang.Integer + + Time in millisecond it takes for an event to revert to its original position after an unsuccessful drag. + default: 500 + + null + + + + date + java.util.Date + + The initial date when schedule loads. + default: + current date + + null + + + + onItemSelected + java.lang.String + + + + null + + + onItemDragStart + java.lang.String + + + + null + + + onItemDragStop + java.lang.String + + + + null + + + onItemResizeStart + java.lang.String + + + + null + + + onItemResizeStop + java.lang.String + + + + null + + + onItemDrop + java.lang.String + + + + null + + + onItemResized + java.lang.String + + + + null + + + onItemMouseover + java.lang.String + + + + null + + + onItemMouseout + java.lang.String + + + + null + + + onViewDisplay + java.lang.String + + + + null + + + onDateSelected + java.lang.String + + + + null + + + styleClass + java.lang.String + + + null + + + + + itemMoveListener + javax.faces.el.MethodBinding + + boolean + + + + itemResizeListener + javax.faces.el.MethodBinding + + + + itemSelectedListener + javax.faces.el.MethodBinding + + + + viewChangedListener + javax.faces.el.MethodBinding + + + + dateRangeChangedListener + javax.faces.el.MethodBinding + + + + dateSelectedListener + javax.faces.el.MethodBinding + + + +
+ &listeners; +
diff --git a/src/main/config/component/scheduleAgendaDayView.xml b/src/main/config/component/scheduleAgendaDayView.xml new file mode 100644 index 0000000..fe20347 --- /dev/null +++ b/src/main/config/component/scheduleAgendaDayView.xml @@ -0,0 +1,33 @@ + + +] +> + + + org.richfaces.ScheduleAgendaDayView + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleAgendaDayView + org.richfaces.component.UIScheduleAgendaDayView + + + + + scheduleAgendaDayView + org.richfaces.taglib.ScheduleAgendaDayViewTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + &commonViewAttributes; + + diff --git a/src/main/config/component/scheduleAgendaWeekView.xml b/src/main/config/component/scheduleAgendaWeekView.xml new file mode 100644 index 0000000..eb18633 --- /dev/null +++ b/src/main/config/component/scheduleAgendaWeekView.xml @@ -0,0 +1,33 @@ + + +] +> + + + org.richfaces.ScheduleAgendaWeekView + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleAgendaWeekView + org.richfaces.component.UIScheduleAgendaWeekView + + + + + scheduleAgendaWeekView + org.richfaces.taglib.ScheduleAgendaWeekViewTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + &commonViewAttributes; + + diff --git a/src/main/config/component/scheduleBasicDayView.xml b/src/main/config/component/scheduleBasicDayView.xml new file mode 100644 index 0000000..e9250fc --- /dev/null +++ b/src/main/config/component/scheduleBasicDayView.xml @@ -0,0 +1,33 @@ + + +] +> + + + org.richfaces.ScheduleBasicDayView + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleBasicDayView + org.richfaces.component.UIScheduleBasicDayView + + + + + scheduleBasicDayView + org.richfaces.taglib.ScheduleBasicDayViewTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + &commonViewAttributes; + + diff --git a/src/main/config/component/scheduleBasicWeekView.xml b/src/main/config/component/scheduleBasicWeekView.xml new file mode 100644 index 0000000..793b884 --- /dev/null +++ b/src/main/config/component/scheduleBasicWeekView.xml @@ -0,0 +1,33 @@ + + +] +> + + + org.richfaces.ScheduleBasicWeekView + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleBasicWeekView + org.richfaces.component.UIScheduleBasicWeekView + + + + + scheduleBasicWeekView + org.richfaces.taglib.ScheduleBasicWeekViewTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + &commonViewAttributes; + + diff --git a/src/main/config/component/scheduleItem.xml b/src/main/config/component/scheduleItem.xml new file mode 100644 index 0000000..936a8be --- /dev/null +++ b/src/main/config/component/scheduleItem.xml @@ -0,0 +1,85 @@ + + + + + + org.richfaces.ScheduleItem + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleItem + org.richfaces.component.UIScheduleItem + + + + + scheduleItem + org.richfaces.taglib.ScheduleItemTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + + + styleClass + java.lang.String + + + null + + + startDate + java.util.Date + + + + + endDate + java.util.Date + + + + + eventId + java.lang.String + + + + + title + java.lang.String + + + + + allDay + java.lang.Boolean + + + null + + + url + java.lang.String + null + + + editable + java.lang.Boolean + null + + + + data + java.lang.Object + null + + + diff --git a/src/main/config/component/scheduleMonthView.xml b/src/main/config/component/scheduleMonthView.xml new file mode 100644 index 0000000..85785fa --- /dev/null +++ b/src/main/config/component/scheduleMonthView.xml @@ -0,0 +1,63 @@ + + +] +> + + + org.richfaces.ScheduleMonthView + org.richfaces.Schedule + org.richfaces.component.html.HtmlScheduleMonthView + org.richfaces.component.UIScheduleMonthView + + + + + scheduleMonthView + org.richfaces.taglib.ScheduleMonthViewTag + + org.ajax4jsf.webapp.taglib.HtmlComponentTagBase + + + + &ui_component_attributes; + &commonViewAttributes; + + + weekMode + java.lang.String + + Determines the number of weeks displayed in a month view. Also determines each week's height. + Available options: +
    +
  • + fixed + - The calendar will always be 6 weeks tall. The height will always be the same, as determined by + height, contentHeight, or aspectRatio. +
  • +
  • + liquid + - The calendar will have either 4, 5, or 6 weeks, depending on the month. The height of the + weeks will stretch to fill the available height, as determined by height, contentHeight, or + aspectRatio. +
  • +
  • + variable + - The calendar will have either 4, 5, or 6 weeks, depending on the month. Each week will have + the same constant height, meaning the calendar’s height will change month-to-month. +
  • +
+ default: 'fixed' +
+ null +
+
+
diff --git a/src/main/config/resources/resources-config.xml b/src/main/config/resources/resources-config.xml new file mode 100644 index 0000000..482f01b --- /dev/null +++ b/src/main/config/resources/resources-config.xml @@ -0,0 +1,6 @@ + + + + org.richfaces.renderkit.html.scripts.ScheduleMessages + + diff --git a/src/main/java/org/richfaces/component/README b/src/main/java/org/richfaces/component/README new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/main/java/org/richfaces/component/README diff --git a/src/main/java/org/richfaces/component/ScheduleCommonViewAttributes.java b/src/main/java/org/richfaces/component/ScheduleCommonViewAttributes.java new file mode 100644 index 0000000..ff363e0 --- /dev/null +++ b/src/main/java/org/richfaces/component/ScheduleCommonViewAttributes.java @@ -0,0 +1,20 @@ +package org.richfaces.component; + +public interface ScheduleCommonViewAttributes { + + public String getTimeFormat(); + + public void setTimeFormat(String format); + + public String getColumnFormat(); + + public void setColumnFormat(String format); + + public String getTitleFormat(); + + public void setTitleFormat(String format); + + public Double getDragOpacity(); + + public void setDragOpacity(Double dragOpacity); +} diff --git a/src/main/java/org/richfaces/component/UISchedule.java b/src/main/java/org/richfaces/component/UISchedule.java new file mode 100644 index 0000000..98f6904 --- /dev/null +++ b/src/main/java/org/richfaces/component/UISchedule.java @@ -0,0 +1,609 @@ +package org.richfaces.component; + +import org.richfaces.component.event.ScheduleViewChangedEvent; +import org.richfaces.component.event.ScheduleDateRangeChangedEvent; +import org.richfaces.component.event.ScheduleItemResizeEvent; +import org.richfaces.component.event.ScheduleItemSelectedEvent; +import org.ajax4jsf.context.AjaxContext; +import org.ajax4jsf.model.DataVisitor; +import org.ajax4jsf.model.ExtendedDataModel; + +import javax.el.ELContext; +import javax.el.ValueExpression; +import javax.faces.component.UIComponent; +import javax.faces.component.UIComponentBase; +import javax.faces.context.FacesContext; +import javax.faces.el.MethodBinding; +import javax.faces.event.AbortProcessingException; +import javax.faces.event.FacesEvent; +import javax.faces.model.*; +import javax.servlet.jsp.jstl.sql.Result; +import java.io.IOException; +import java.sql.ResultSet; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +import org.richfaces.component.event.ScheduleDateRangeChangedListener; +import org.richfaces.component.event.ScheduleDateSelectedEvent; +import org.richfaces.component.event.ScheduleDateSelectedListener; +import org.richfaces.component.event.ScheduleItemSelectedListener; +import org.richfaces.component.event.ScheduleItemMoveEvent; +import org.richfaces.component.event.ScheduleItemMoveListener; +import org.richfaces.component.event.ScheduleItemResizeListener; +import org.richfaces.component.event.ScheduleListenerEventsProducer; +import org.richfaces.component.event.ScheduleViewChangedListener; +import org.richfaces.component.model.DateRange; + +public abstract class UISchedule extends UIComponentBase implements ScheduleCommonViewAttributes, ScheduleListenerEventsProducer { + + public static final String COMPONENT_TYPE = "org.richfaces.Schedule"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; + public static final String VIEW_MONTH = "month"; + public static final String VIEW_BASIC_WEEK = "basicWeek"; + public static final String VIEW_AGENDA_WEEK = "agendaWeek"; + public static final String VIEW_BASIC_DAY = "basicDay"; + public static final String VIEW_AGENDA_DAY = "agendaDay"; + public static final String WEEK_MODE_FIXED = "fixed"; + private DataModel model; + + public abstract Object getValue(); + + public abstract void setValue(Object value); + + public abstract String getVar(); + + public abstract void setVar(String value); + + public abstract Date getDate(); + + public abstract void setDate(Date date); + + public abstract String getSwitchType(); + + public abstract void setSwitchType(String switchType); + + public abstract String getView(); + + public abstract void setView(String defaultView); + + public abstract String getHeaderLeft(); + + public abstract void setHeaderLeft(String headerLeft); + + public abstract String getHeaderCenter(); + + public abstract void setHeaderCenter(String headerCenter); + + public abstract String getHeaderRight(); + + public abstract void setHeaderRight(String headerRight); + + public abstract Integer getFirstDay(); + + public abstract void setFirstDay(Integer firstDay); + + public abstract Boolean getIsRTL(); + + public abstract void setIsRTL(Boolean isrtl); + + public abstract Boolean getShowWeekends(); + + public abstract void setShowWeekends(Boolean showWeekends); + + public abstract String getWeekMode(); + + public abstract void setWeekMode(String weekMode); + + public abstract Integer getHeight(); + + public abstract void setHeight(Integer height); + + public abstract Integer getContentHeight(); + + public abstract void setContentHeight(Integer contentHeight); + + public abstract Double getAspectRatio(); + + public abstract void setAspectRatio(Double aspectRatio); + + public abstract Boolean getAllDayByDefault(); + + public abstract void setAllDayByDefault(Boolean allDayByDefault); + + public abstract Boolean getAllDaySlot(); + + public abstract void setAllDaySlot(Boolean allDaySlot); + + public abstract String getAllDayText(); + + public abstract void setAllDayText(String allDayText); + + public abstract String getAxisFormat(); + + public abstract void setAxisFormat(String axisFormat); + + public abstract Integer getSlotMinutes(); + + public abstract void setSlotMinutes(Integer slotMinutes); + + public abstract Integer getDefaultEventMinutes(); + + public abstract void setDefaultEventMinutes(Integer defaultEventMinutes); + + public abstract Integer getFirstHour(); + + public abstract void setFirstHour(Integer firstHour); + + public abstract String getMinTime(); + + public abstract void setMinTime(String minTime); + + public abstract String getMaxTime(); + + public abstract void setMaxTime(String maxTime); + + public abstract Boolean getEditable(); + + public abstract void setEditable(Boolean editable); + + public abstract Boolean getDisableDragging(); + + public abstract void setDisableDragging(Boolean disableDragging); + + public abstract Boolean getDisableResizing(); + + public abstract void setDisableResizing(Boolean disableResizing); + + public abstract Integer getDragRevertDuration(); + + public abstract void setDragRevertDuration(Integer dragRevertDuration); + + public abstract Double getDragOpacity(); + + public abstract void setDragOpacity(Double dragOpacity); + + public abstract String getStyleClass(); + + public abstract void setStyleClass(String styleClass); + + public abstract String getOnItemSelected(); + + public abstract void setOnItemSelected(String onItemSelect); + + public abstract String getOnItemDragStart(); + + public abstract void setOnItemDragStart(String onItemDragStart); + + public abstract String getOnItemDragStop(); + + public abstract void setOnItemDragStop(String onItemDragStop); + + public abstract String getOnItemDrop(); + + public abstract void setOnItemDrop(String onItemDrop); + + public abstract String getOnItemResizeStart(); + + public abstract void setOnItemResizeStart(String onItemResizeStart); + + public abstract String getOnItemResizeStop(); + + public abstract void setOnItemResizeStop(String onItemResizeStop); + + public abstract String getOnItemResized(); + + public abstract void setOnItemResized(String onItemMouseover); + + public abstract String getOnItemMouseover(); + + public abstract void setOnItemMouseover(String onItemMouseover); + + public abstract String getOnItemMouseout(); + + public abstract void setOnItemMouseout(String onItemMouseout); + + public abstract String getOnViewDisplay(); + + public abstract void setOnViewDisplay(String onViewDisplay); + + public abstract String getOnDateSelected(); + + public abstract void setOnDateSelected(String onDaySelected); + + // TODO do we use MethodBinding or MethodExpression? + public abstract MethodBinding getItemMoveListener(); + + public abstract void setItemMoveListener(MethodBinding listener); + + public abstract MethodBinding getItemSelectedListener(); + + public abstract void setItemSelectedListener(MethodBinding listener); + + public abstract MethodBinding getItemResizeListener(); + + public abstract void setItemResizeListener(MethodBinding listener); + + public abstract MethodBinding getViewChangedListener(); + + public abstract void setViewChangedListener(MethodBinding listener); + + public abstract MethodBinding getDateRangeChangedListener(); + + public abstract void setDateRangeChangedListener(MethodBinding listener); + + public abstract MethodBinding getDateSelectedListener(); + + public abstract void setDateSelectedListener(MethodBinding listener); + + @Override + public void broadcast(FacesEvent event) throws AbortProcessingException { + if (event instanceof ScheduleDateRangeChangedEvent) { + super.broadcast(event); + ScheduleDateRangeChangedEvent calendarAjaxEvent = (ScheduleDateRangeChangedEvent) event; + FacesContext facesContext = getFacesContext(); + AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext); + MethodBinding expression = getDateRangeChangedListener(); + if (expression != null) { + expression.invoke(facesContext, new Object[]{event}); + } + try { + ajaxContext.getResponseDataMap().put("_ajax:scheduleData", getScheduleData(calendarAjaxEvent.getStartDate(), calendarAjaxEvent.getEndDate())); + } catch (IOException ex) { + getFacesContext().getExternalContext().log("Cannot get schedule data", ex); + } + } else if (event instanceof ScheduleItemMoveEvent) { + FacesContext facesContext = getFacesContext(); + AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext); + MethodBinding expression = getItemMoveListener(); + boolean allow = true; + if (expression != null) { + Object result = expression.invoke(facesContext, new Object[]{event}); + allow = ((Boolean) result); + } + ajaxContext.setResponseData(allow); + super.broadcast(event); + } else if (event instanceof ScheduleItemResizeEvent) { + FacesContext facesContext = getFacesContext(); + AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext); + MethodBinding expression = getItemResizeListener(); + boolean allow = true; + if (expression != null) { + Object result = expression.invoke(facesContext, new Object[]{event}); + allow = ((Boolean) result); + } + ajaxContext.setResponseData(allow); + super.broadcast(event); + } else if (event instanceof ScheduleItemSelectedEvent) { + super.broadcast(event); + FacesContext facesContext = getFacesContext(); + MethodBinding expression = getItemSelectedListener(); + if (expression != null) { + expression.invoke(facesContext, new Object[]{event}); + } + } else if (event instanceof ScheduleViewChangedEvent) { + super.broadcast(event); + FacesContext facesContext = getFacesContext(); + MethodBinding expression = getViewChangedListener(); + if (expression != null) { + expression.invoke(facesContext, new Object[]{event}); + } + } else if (event instanceof ScheduleDateSelectedEvent) { + super.broadcast(event); + FacesContext facesContext = getFacesContext(); + MethodBinding expression = getDateSelectedListener(); + if (expression != null) { + expression.invoke(facesContext, new Object[]{event}); + } + } + } + + /** + * Range is [startDate;endDate), which means that start date is inclusive + * and end date is exclusive. + * + * @param startDate + * @param endDate + * @return + * @throws IOException + */ + public List> getScheduleData(Date startDate, Date endDate) throws IOException { + /** + * Locale must be US because this is the format the javascript widget supports + */ + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); + format.setLenient(false); + DataModel dataModel = getDataModel(); + if (dataModel instanceof ExtendedDataModel) { + DataVisitor visitor = new DataVisitor() { + //TODO Is this fine? or should we stack rowKeys and not use dataModel later on. I'don't know business rules of extendedDataModel, just used it to did once pagination with underlying EntityQuery from Seam + + public void process(FacesContext context, Object rowKey, Object argument) throws IOException { + } + }; + ((ExtendedDataModel) dataModel).walk(getFacesContext(), visitor, new DateRange(startDate, endDate), null); + } + ELContext elContext = getFacesContext().getELContext(); + ValueExpression valueExpression = getFacesContext().getApplication().getExpressionFactory().createValueExpression(getFacesContext().getELContext(), "#{" + getVar() + "}", Object.class); + List> data = new ArrayList>(); + for (int i = 0; i < dataModel.getRowCount(); i++) { + dataModel.setRowIndex(i); + valueExpression.setValue(elContext, dataModel.getRowData()); + Map firstDataElement = new HashMap(); +// TODO shouldn't we check earlier if there is at most one renderable UIScheduleItem child? + for (UIComponent child : getChildren()) { + if (child instanceof UIScheduleItem) { + UIScheduleItem item = (UIScheduleItem) child; + if (!item.isRendered()) { + continue; + } + firstDataElement.put("id", item.getEventId()); + firstDataElement.put("title", item.getTitle()); + if (item.getAllDay() != null) { + firstDataElement.put("allDay", item.getAllDay()); + } + firstDataElement.put("start", format.format(item.getStartDate())); + if (item.getEndDate() != null) { + firstDataElement.put("end", format.format(item.getEndDate())); + } + if (item.getUrl() != null) { + firstDataElement.put("url", item.getUrl()); + } + if (item.getStyleClass() != null) { + firstDataElement.put("className", item.getStyleClass()); + } + if (item.getEditable() != null) { + firstDataElement.put("editable", item.getEditable()); + } + if (item.getData() != null) { + firstDataElement.put("data", item.getData()); + } + data.add(firstDataElement); + } + } + } + valueExpression.setValue(elContext, null); + return data; + } + + public static Date getFirstDisplayedDay(UISchedule schedule) { + Calendar calendar = Calendar.getInstance(); + Date date = schedule.getDate(); + if (date != null) { + calendar.setTime(date); + } + int firstDayOfWeek = getFirstDay(schedule); + calendar.setFirstDayOfWeek(firstDayOfWeek); + String view = getView(schedule); + boolean showWeekends = isShowWeekends(schedule); + if (VIEW_MONTH.equals(view)) { + calendar.set(Calendar.DATE, 1); + if (!showWeekends) { + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); + if (dayOfWeek == Calendar.SUNDAY) { + calendar.add(Calendar.DATE, 1); + } else if (dayOfWeek == Calendar.SATURDAY) { + calendar.add(Calendar.DATE, 2); + } + } + if (!showWeekends && firstDayOfWeek == Calendar.SUNDAY) { + firstDayOfWeek++; + } + /** + * Following 1 line is a fix to what i believe is a bug of java.util.Calendar + */ + calendar.get(Calendar.DAY_OF_WEEK); + calendar.set(Calendar.DAY_OF_WEEK, firstDayOfWeek); + return calendar.getTime(); + } else if (VIEW_AGENDA_WEEK.equals(view) || VIEW_BASIC_WEEK.equals(view)) { + calendar.set(Calendar.DAY_OF_WEEK, firstDayOfWeek); + if (!showWeekends) { + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); + if (dayOfWeek == Calendar.SUNDAY) { + calendar.add(Calendar.DATE, 1); + } else if (dayOfWeek == Calendar.SATURDAY) { + calendar.add(Calendar.DATE, 2); + } + } + return calendar.getTime(); + } else if (VIEW_AGENDA_DAY.equals(view) || VIEW_BASIC_DAY.equals(view)) { + return calendar.getTime(); + } else { + throw new IllegalStateException("Invalid view attribute: " + view); + } + } + + public static Date getLastDisplayedDate(UISchedule schedule) { + Calendar calendar = Calendar.getInstance(); + int firstDayOfWeek = getFirstDay(schedule); + String view = getView(schedule); + boolean showWeekends = isShowWeekends(schedule); + if (VIEW_MONTH.equals(view)) { + if (WEEK_MODE_FIXED.equals(getWeekMode(schedule))) { + calendar.setTime(getFirstDisplayedDay(schedule)); + calendar.add(Calendar.DAY_OF_YEAR, 7 * 6); + } else { + Date date = schedule.getDate(); + if (date != null) { + calendar.setTime(date); + } + + if (!showWeekends && firstDayOfWeek == Calendar.SUNDAY) { + firstDayOfWeek++; + } + calendar.setFirstDayOfWeek(firstDayOfWeek); + calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE)); + int dayOfWeek = firstDayOfWeek + 6; + if (dayOfWeek > Calendar.SATURDAY) { + dayOfWeek -= 7; + } + /** + * Following 1 line is a fix to what i believe is a bug of java.util.Calendar + */ + calendar.get(Calendar.DAY_OF_WEEK); + calendar.set(Calendar.DAY_OF_WEEK, dayOfWeek); + calendar.add(Calendar.DATE, 1); + } + return calendar.getTime(); + } else if (VIEW_AGENDA_WEEK.equals(view) || VIEW_BASIC_WEEK.equals(view)) { + calendar.setFirstDayOfWeek(firstDayOfWeek); + calendar.setTime(getFirstDisplayedDay(schedule)); + calendar.add(Calendar.DATE, 7); + if (!showWeekends) { + int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); + if (dayOfWeek - 1 == Calendar.SUNDAY) { + calendar.add(Calendar.DATE, -2); + } else if (dayOfWeek - 1 == Calendar.SATURDAY) { + calendar.add(Calendar.DATE, -1); + } + } + return calendar.getTime(); + } else if (VIEW_AGENDA_DAY.equals(view) || VIEW_BASIC_DAY.equals(view)) { + calendar.setTime(getFirstDisplayedDay(schedule)); + calendar.add(Calendar.DATE, 1); + return calendar.getTime(); + } else { + throw new IllegalStateException("Invalid view attribute: " + view); + } + + } + + /** + * Here go static methods for getting value or default value of attributes. + */ + /** + * @param schedule + * @return + */ + public static boolean isShowWeekends(UISchedule schedule) { + Boolean showWeekends = schedule.getShowWeekends(); + return showWeekends == null || schedule.getShowWeekends(); + } + + public static int getFirstDay(UISchedule schedule) { + Integer firstDay = schedule.getFirstDay(); + if (firstDay == null) { + firstDay = Calendar.SUNDAY; + } + return firstDay; + } + + public static String getWeekMode(UISchedule schedule) { + String weekMode = schedule.getWeekMode(); + if (weekMode == null) { + weekMode = WEEK_MODE_FIXED; + } + return weekMode; + } + + public static String getView(UISchedule schedule) { + String view = schedule.getView(); + if (view == null) { + view = VIEW_MONTH; + } + return view; + } + + protected DataModel getDataModel() { + // Return any previously cached DataModel instance + if (this.model != null) { + return (model); + } + + // Synthesize a DataModel around our current value if possible + Object current = getValue(); + if (current == null) { + setDataModel(new ListDataModel(Collections.EMPTY_LIST)); + } else if (current instanceof DataModel) { + setDataModel((DataModel) current); + } else if (current instanceof List) { + setDataModel(new ListDataModel((List) current)); + } else if (Object[].class.isAssignableFrom(current.getClass())) { + setDataModel(new ArrayDataModel((Object[]) current)); + } else if (current instanceof ResultSet) { + setDataModel(new ResultSetDataModel((ResultSet) current)); + } else if (current instanceof Result) { + setDataModel(new ResultDataModel((Result) current)); + } else { + setDataModel(new ScalarDataModel(current)); + } + return (model); + + } + + public void setDataModel(DataModel model) { + this.model = model; + } + + public void addItemSelectedListener(ScheduleItemSelectedListener listener) { + addFacesListener(listener); + } + + public void removeItemSelectedListener(ScheduleItemSelectedListener listener) { + removeFacesListener(listener); + } + + public ScheduleItemSelectedListener[] getItemSelectedListeners() { + return (ScheduleItemSelectedListener[]) getFacesListeners(ScheduleItemSelectedListener.class); + } + + public void addItemMoveListener(ScheduleItemMoveListener listener) { + addFacesListener(listener); + } + + public void removeItemMoveListener(ScheduleItemMoveListener listener) { + removeFacesListener(listener); + } + + public ScheduleItemMoveListener[] getItemMoveListeners() { + return (ScheduleItemMoveListener[]) getFacesListeners(ScheduleItemMoveListener.class); + } + + public void addItemResizeListener(ScheduleItemResizeListener listener) { + addFacesListener(listener); + } + + public void removeItemResizeListener(ScheduleItemResizeListener listener) { + removeFacesListener(listener); + } + + public ScheduleItemResizeListener[] getItemResizeListeners() { + return (ScheduleItemResizeListener[]) getFacesListeners(ScheduleItemResizeListener.class); + } + + public void addViewChangedListener(ScheduleViewChangedListener listener) { + addFacesListener(listener); + } + + public void removeViewChangedListener(ScheduleViewChangedListener listener) { + removeFacesListener(listener); + } + + public ScheduleViewChangedListener[] getViewChangedListeners() { + return (ScheduleViewChangedListener[]) getFacesListeners(ScheduleViewChangedListener.class); + } + + public void addDateRangeChangedListener(ScheduleDateRangeChangedListener listener) { + addFacesListener(listener); + } + + public void removeDateRangeChangedListener(ScheduleDateRangeChangedListener listener) { + removeFacesListener(listener); + } + + public ScheduleDateRangeChangedListener[] getDateRangeChangedListeners() { + return (ScheduleDateRangeChangedListener[]) getFacesListeners(ScheduleDateRangeChangedListener.class); + } + + public void addDateSelectedListener(ScheduleDateSelectedListener listener) { + addFacesListener(listener); + } + + public void removeDateSelectedListener(ScheduleDateSelectedListener listener) { + removeFacesListener(listener); + } + + public ScheduleDateSelectedListener[] getDateSelectedListeners() { + return (ScheduleDateSelectedListener[]) getFacesListeners(ScheduleDateSelectedListener.class); + } +} diff --git a/src/main/java/org/richfaces/component/UIScheduleAgendaDayView.java b/src/main/java/org/richfaces/component/UIScheduleAgendaDayView.java new file mode 100644 index 0000000..f363b69 --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleAgendaDayView.java @@ -0,0 +1,9 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; + +public abstract class UIScheduleAgendaDayView extends UIComponentBase implements ScheduleCommonViewAttributes { + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleAgendaDayView"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; +} diff --git a/src/main/java/org/richfaces/component/UIScheduleAgendaWeekView.java b/src/main/java/org/richfaces/component/UIScheduleAgendaWeekView.java new file mode 100644 index 0000000..9577cc6 --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleAgendaWeekView.java @@ -0,0 +1,9 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; + +public abstract class UIScheduleAgendaWeekView extends UIComponentBase implements ScheduleCommonViewAttributes { + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleAgendaWeekView"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; +} diff --git a/src/main/java/org/richfaces/component/UIScheduleBasicDayView.java b/src/main/java/org/richfaces/component/UIScheduleBasicDayView.java new file mode 100644 index 0000000..f8a83bd --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleBasicDayView.java @@ -0,0 +1,9 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; + +public abstract class UIScheduleBasicDayView extends UIComponentBase implements ScheduleCommonViewAttributes{ + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleBasicDayView"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; +} diff --git a/src/main/java/org/richfaces/component/UIScheduleBasicWeekView.java b/src/main/java/org/richfaces/component/UIScheduleBasicWeekView.java new file mode 100644 index 0000000..cf23d91 --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleBasicWeekView.java @@ -0,0 +1,9 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; + +public abstract class UIScheduleBasicWeekView extends UIComponentBase implements ScheduleCommonViewAttributes { + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleBasicWeekView"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; +} diff --git a/src/main/java/org/richfaces/component/UIScheduleItem.java b/src/main/java/org/richfaces/component/UIScheduleItem.java new file mode 100644 index 0000000..3883043 --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleItem.java @@ -0,0 +1,49 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; +import java.util.Date; + +/** + * JSF component class + */ +public abstract class UIScheduleItem extends UIComponentBase { + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleItem"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; + + public abstract String getStyleClass(); + + public abstract void setStyleClass(String styleClass); + + public abstract String getTitle(); + + public abstract void setTitle(String title); + + public abstract Date getStartDate(); + + public abstract void setStartDate(Date date); + + public abstract Date getEndDate(); + + public abstract void setEndDate(Date date); + + public abstract String getEventId(); + + public abstract void setEventId(String eventId); + + public abstract Boolean getAllDay(); + + public abstract void setAllDay(Boolean isAllDay); + + public abstract String getUrl(); + + public abstract void setUrl(String url); + + public abstract Boolean getEditable(); + + public abstract void setEditable(Boolean editable); + + public abstract Object getData(); + + public abstract void setData(Object data); +} diff --git a/src/main/java/org/richfaces/component/UIScheduleMonthView.java b/src/main/java/org/richfaces/component/UIScheduleMonthView.java new file mode 100644 index 0000000..f779bf5 --- /dev/null +++ b/src/main/java/org/richfaces/component/UIScheduleMonthView.java @@ -0,0 +1,13 @@ +package org.richfaces.component; + +import javax.faces.component.UIComponentBase; + +public abstract class UIScheduleMonthView extends UIComponentBase implements ScheduleCommonViewAttributes { + + public static final String COMPONENT_TYPE = "org.richfaces.ScheduleMonthView"; + public static final String COMPONENT_FAMILY = "org.richfaces.Schedule"; + + public abstract String getWeekMode(); + + public abstract void setWeekMode(String mode); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedEvent.java b/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedEvent.java new file mode 100644 index 0000000..02fce6b --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedEvent.java @@ -0,0 +1,43 @@ +package org.richfaces.component.event; + +import java.util.Date; + +import javax.faces.component.UIComponent; + +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleDateRangeChangedEvent extends FacesEvent { + + private Date startDate; + private Date endDate; + + public ScheduleDateRangeChangedEvent(UIComponent component, Date startDate, Date endDate) { + super(component); + this.startDate = startDate; + this.endDate = endDate; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[startDate=" + startDate + ";endDate=" + endDate + "]"; + } + + @Override + public boolean isAppropriateListener(FacesListener listener) { + return listener instanceof ScheduleDateRangeChangedListener; + } + + @Override + public void processListener(FacesListener listener) { + ((ScheduleDateRangeChangedListener) listener).dateRangeChanged(this); + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedListener.java b/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedListener.java new file mode 100644 index 0000000..f11c0fd --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleDateRangeChangedListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleDateRangeChangedListener extends FacesListener { + + void dateRangeChanged(ScheduleDateRangeChangedEvent event); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleDateSelectedEvent.java b/src/main/java/org/richfaces/component/event/ScheduleDateSelectedEvent.java new file mode 100644 index 0000000..75a66e9 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleDateSelectedEvent.java @@ -0,0 +1,39 @@ +package org.richfaces.component.event; + +import java.util.Date; +import javax.faces.component.UIComponent; +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleDateSelectedEvent extends FacesEvent { + + private Date date; + private boolean allDay; + + public ScheduleDateSelectedEvent(UIComponent component, Date date, boolean allDay) { + super(component); + this.date = date; + this.allDay = allDay; + } + + public boolean isAppropriateListener(FacesListener facesListener) { + return facesListener instanceof ScheduleDateSelectedListener; + } + + public void processListener(FacesListener facesListener) { + ((ScheduleDateSelectedListener) facesListener).dateSelected(this); + } + + public Date getDate() { + return date; + } + + public boolean isAllDay() { + return allDay; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[date=" + date + ";allDay=" + allDay + "]"; + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleDateSelectedListener.java b/src/main/java/org/richfaces/component/event/ScheduleDateSelectedListener.java new file mode 100644 index 0000000..9e83fbe --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleDateSelectedListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleDateSelectedListener extends FacesListener { + + void dateSelected(ScheduleDateSelectedEvent event); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemMoveEvent.java b/src/main/java/org/richfaces/component/event/ScheduleItemMoveEvent.java new file mode 100644 index 0000000..f469277 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemMoveEvent.java @@ -0,0 +1,50 @@ +package org.richfaces.component.event; + +import javax.faces.component.UIComponent; +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleItemMoveEvent extends FacesEvent { + + private String eventId; + private int dayDelta; + private int minuteDelta; + private boolean allDay; + + public ScheduleItemMoveEvent(UIComponent component, String eventId, int dayDelta, int minuteDelta, boolean allDay) { + super(component); + this.eventId = eventId; + this.dayDelta = dayDelta; + this.minuteDelta = minuteDelta; + this.allDay = allDay; + } + + public boolean isAppropriateListener(FacesListener facesListener) { + return facesListener instanceof ScheduleItemMoveListener; + } + + public void processListener(FacesListener facesListener) { + ((ScheduleItemMoveListener) facesListener).itemMove(this); + } + + public String getEventId() { + return eventId; + } + + public int getDayDelta() { + return dayDelta; + } + + public int getMinuteDelta() { + return minuteDelta; + } + + public boolean isAllDay() { + return allDay; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[eventId=" + eventId + ";dayDelta=" + dayDelta + ";minuteDelta=" + minuteDelta + ";allDay=" + allDay + "]"; + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemMoveListener.java b/src/main/java/org/richfaces/component/event/ScheduleItemMoveListener.java new file mode 100644 index 0000000..d30484f --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemMoveListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleItemMoveListener extends FacesListener{ + + void itemMove(ScheduleItemMoveEvent event); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemResizeEvent.java b/src/main/java/org/richfaces/component/event/ScheduleItemResizeEvent.java new file mode 100644 index 0000000..5b8940a --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemResizeEvent.java @@ -0,0 +1,44 @@ +package org.richfaces.component.event; + +import javax.faces.component.UIComponent; +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleItemResizeEvent extends FacesEvent { + + private String eventId; + private int dayDelta; + private int minuteDelta; + + public ScheduleItemResizeEvent(UIComponent component, String eventId, int dayDelta, int minuteDelta) { + super(component); + this.eventId = eventId; + this.dayDelta = dayDelta; + this.minuteDelta = minuteDelta; + } + + public boolean isAppropriateListener(FacesListener facesListener) { + return facesListener instanceof ScheduleItemResizeListener; + } + + public void processListener(FacesListener facesListener) { + ((ScheduleItemResizeListener) facesListener).itemResize(this); + } + + public String getEventId() { + return eventId; + } + + public int getDayDelta() { + return dayDelta; + } + + public int getMinuteDelta() { + return minuteDelta; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[eventId=" + eventId + ";dayDelta=" + dayDelta + ";minuteDelta=" + minuteDelta + "]"; + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemResizeListener.java b/src/main/java/org/richfaces/component/event/ScheduleItemResizeListener.java new file mode 100644 index 0000000..032e16e --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemResizeListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleItemResizeListener extends FacesListener{ + + void itemResize(ScheduleItemResizeEvent event); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemSelectedEvent.java b/src/main/java/org/richfaces/component/event/ScheduleItemSelectedEvent.java new file mode 100644 index 0000000..57b8d63 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemSelectedEvent.java @@ -0,0 +1,32 @@ +package org.richfaces.component.event; + +import javax.faces.component.UIComponent; +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleItemSelectedEvent extends FacesEvent { + + private String eventId; + + public ScheduleItemSelectedEvent(UIComponent component, String eventId) { + super(component); + this.eventId = eventId; + } + + public boolean isAppropriateListener(FacesListener facesListener) { + return facesListener instanceof ScheduleItemSelectedListener; + } + + public void processListener(FacesListener facesListener) { + ((ScheduleItemSelectedListener) facesListener).itemSelected(this); + } + + public String getEventId() { + return eventId; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[eventId=" + eventId + "]"; + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleItemSelectedListener.java b/src/main/java/org/richfaces/component/event/ScheduleItemSelectedListener.java new file mode 100644 index 0000000..a163247 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleItemSelectedListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleItemSelectedListener extends FacesListener { + + public void itemSelected(ScheduleItemSelectedEvent event); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleListenerEventsProducer.java b/src/main/java/org/richfaces/component/event/ScheduleListenerEventsProducer.java new file mode 100644 index 0000000..0b1b43d --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleListenerEventsProducer.java @@ -0,0 +1,28 @@ +package org.richfaces.component.event; + +public interface ScheduleListenerEventsProducer { + + void addItemSelectedListener(ScheduleItemSelectedListener listener); + void removeItemSelectedListener(ScheduleItemSelectedListener listener); + ScheduleItemSelectedListener[] getItemSelectedListeners(); + + void addItemMoveListener(ScheduleItemMoveListener listener); + void removeItemMoveListener(ScheduleItemMoveListener listener); + ScheduleItemMoveListener[] getItemMoveListeners(); + + void addItemResizeListener(ScheduleItemResizeListener listener); + void removeItemResizeListener(ScheduleItemResizeListener listener); + ScheduleItemResizeListener[] getItemResizeListeners(); + + void addViewChangedListener(ScheduleViewChangedListener listener); + void removeViewChangedListener(ScheduleViewChangedListener listener); + ScheduleViewChangedListener[] getViewChangedListeners(); + + void addDateRangeChangedListener(ScheduleDateRangeChangedListener listener); + void removeDateRangeChangedListener(ScheduleDateRangeChangedListener listener); + ScheduleDateRangeChangedListener[] getDateRangeChangedListeners(); + + void addDateSelectedListener(ScheduleDateSelectedListener listener); + void removeDateSelectedListener(ScheduleDateSelectedListener listener); + ScheduleDateSelectedListener[] getDateSelectedListeners(); +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleViewChangedEvent.java b/src/main/java/org/richfaces/component/event/ScheduleViewChangedEvent.java new file mode 100644 index 0000000..13e0ef8 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleViewChangedEvent.java @@ -0,0 +1,32 @@ +package org.richfaces.component.event; + +import javax.faces.component.UIComponent; +import javax.faces.event.FacesEvent; +import javax.faces.event.FacesListener; + +public class ScheduleViewChangedEvent extends FacesEvent { + + private String view; + + public ScheduleViewChangedEvent(UIComponent component, String view) { + super(component); + this.view = view; + } + + public boolean isAppropriateListener(FacesListener facesListener) { + return facesListener instanceof ScheduleViewChangedListener; + } + + public void processListener(FacesListener facesListener) { + ((ScheduleViewChangedListener) facesListener).viewChanged(this); + } + + public String getView() { + return view; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[view=" + view + "]"; + } +} diff --git a/src/main/java/org/richfaces/component/event/ScheduleViewChangedListener.java b/src/main/java/org/richfaces/component/event/ScheduleViewChangedListener.java new file mode 100644 index 0000000..2da6982 --- /dev/null +++ b/src/main/java/org/richfaces/component/event/ScheduleViewChangedListener.java @@ -0,0 +1,8 @@ +package org.richfaces.component.event; + +import javax.faces.event.FacesListener; + +public interface ScheduleViewChangedListener extends FacesListener{ + + void viewChanged(ScheduleViewChangedEvent event); +} diff --git a/src/main/java/org/richfaces/component/model/DateRange.java b/src/main/java/org/richfaces/component/model/DateRange.java new file mode 100644 index 0000000..d47b583 --- /dev/null +++ b/src/main/java/org/richfaces/component/model/DateRange.java @@ -0,0 +1,34 @@ +package org.richfaces.component.model; + +import java.util.Date; +import org.ajax4jsf.model.Range; + +public class DateRange implements Range { + + private Date startDate; + private Date endDate; + + public DateRange() { + } + + public DateRange(Date startDate, Date endDate) { + setStartDate(startDate); + setEndDate(endDate); + } + + public Date getStartDate() { + return startDate; + } + + public void setStartDate(Date startDate) { + this.startDate = startDate; + } + + public Date getEndDate() { + return endDate; + } + + public void setEndDate(Date endDate) { + this.endDate = endDate; + } +} diff --git a/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java b/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java new file mode 100644 index 0000000..6663043 --- /dev/null +++ b/src/main/java/org/richfaces/renderkit/ScheduleRendererBase.java @@ -0,0 +1,291 @@ +package org.richfaces.renderkit; + +import java.util.Locale; +import org.richfaces.component.event.ScheduleViewChangedEvent; +import org.richfaces.component.event.ScheduleDateRangeChangedEvent; +import org.richfaces.component.event.ScheduleItemResizeEvent; +import org.richfaces.component.event.ScheduleItemSelectedEvent; +import org.ajax4jsf.javascript.JSFunction; +import org.ajax4jsf.javascript.JSFunctionDefinition; +import org.ajax4jsf.javascript.JSObject; +import org.ajax4jsf.javascript.JSReference; +import org.ajax4jsf.renderkit.AjaxComponentRendererBase; +import org.ajax4jsf.renderkit.AjaxRendererUtils; +import org.richfaces.component.*; + +import javax.faces.component.NamingContainer; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.faces.FacesException; + +import org.richfaces.component.event.ScheduleDateSelectedEvent; +import org.richfaces.component.event.ScheduleItemMoveEvent; + +public abstract class ScheduleRendererBase extends AjaxComponentRendererBase { + + public static final String ITEM_MOVE_EVENT = "itemMove"; + public static final String ITEM_RESIZE_EVENT = "itemResize"; + public static final String ITEM_SELECTED_EVENT = "itemSelected"; + public static final String DATE_RANGE_CHANGED_EVENT = "dateRangeChanged"; + public static final String VIEW_CHANGED_EVENT = "viewChanged"; + public static final String DATE_SELECTED_EVENT = "dateSelected"; + public static final String AJAX_MODE = "ajax"; + public static final String SERVER_MODE = "server"; + public static final String CLIENT_MODE = "client"; + private static final String CALLBACK = "callback"; + private static final String END_DATE_PARAM = "endDate"; + private static final String START_DATE_PARAM = "startDate"; + private static final String ITEM_ID_PARAM = "itemId"; + private static final String DAY_DELTA_PARAM = "dayDelta"; + private static final String MINUTE_DELTA_PARAM = "minuteDelta"; + private static final String ALL_DAY_PARAM = "allDay"; + private static final String EVENT_TYPE_PARAM = "eventType"; + private static final String VIEW_PARAM = "view"; + + protected Object createSubmitEventFunction(FacesContext context, UISchedule component) { + JSFunction jsFunction = null; + Map params = new HashMap(); + 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)); + if (isAjaxMode(component)) { + jsFunction = AjaxRendererUtils.buildAjaxFunction(component, context); + Map eventOptions = AjaxRendererUtils.buildEventOptions(context, component, params); + eventOptions.put("oncomplete", new JSReference(CALLBACK)); + jsFunction.addParameter(eventOptions); + } else if (SERVER_MODE.equals(component.getSwitchType())) { + jsFunction = new JSFunction("Richfaces.jsFormSubmit", + component.getClientId(context), + getUtils().getNestingForm(context, component).getClientId(context), + null, + 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); + } + + @Override + public void decode(FacesContext context, UIComponent component) { + if (!component.isRendered()) { + return; + } + Map requestParameterMap = context.getExternalContext().getRequestParameterMap(); + if (requestParameterMap.get(component.getClientId(context)) != null) { + String startDateParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, START_DATE_PARAM)); + String endDateParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, END_DATE_PARAM)); + String itemIdParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, ITEM_ID_PARAM)); + String dayDeltaParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, DAY_DELTA_PARAM)); + String minuteDeltaParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, MINUTE_DELTA_PARAM)); + String allDayParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, ALL_DAY_PARAM)); + String eventTypeParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, EVENT_TYPE_PARAM)); + String viewParam = requestParameterMap.get(getFieldId(context, (UISchedule) component, VIEW_PARAM)); + + try { + if (DATE_RANGE_CHANGED_EVENT.equals(eventTypeParam)) { + Date startDate = new Date(Long.parseLong(startDateParam) * 1000); + Date endDate = new Date(Long.parseLong(endDateParam) * 1000); + new ScheduleDateRangeChangedEvent(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 ScheduleItemSelectedEvent(component, itemIdParam).queue(); + } else if (VIEW_CHANGED_EVENT.equals(eventTypeParam)) { + new ScheduleViewChangedEvent(component, viewParam).queue(); + } else if (DATE_SELECTED_EVENT.equals(eventTypeParam)) { + Date startDate = new Date(Long.parseLong(startDateParam) * 1000); + boolean allDay = Boolean.parseBoolean(allDayParam); + new ScheduleDateSelectedEvent(component, startDate, allDay).queue(); + } + } catch (NumberFormatException ex) { + throw new FacesException("Cannot convert request parmeters", ex); + } + } + } + + public void writeInitFunction(FacesContext context, UISchedule component) throws IOException { + ResponseWriter writer = context.getResponseWriter(); + String clientId = component.getClientId(context); + Locale locale = context.getViewRoot().getLocale(); + writer.writeText(new JSObject("RichFaces.Schedule", clientId, locale.getLanguage(), + getOptions(component), + DATE_RANGE_CHANGED_EVENT, + ITEM_SELECTED_EVENT, + ITEM_MOVE_EVENT, + ITEM_RESIZE_EVENT, + VIEW_CHANGED_EVENT, + DATE_SELECTED_EVENT, + createSubmitEventFunction(context, component)).toScript(), + null); + } + + public Map getOptions(UISchedule schedule) throws IOException { + /** + * Copy attributes from child view components + */ + for (UIComponent child : schedule.getChildren()) { + if (!child.isRendered()) { + continue; + } + String suffix = ""; + if (child instanceof UIScheduleMonthView) { + copyAttribute("weekMode", "", child, schedule); + suffix = "Month"; + } else if (child instanceof UIScheduleAgendaDayView) { + suffix = "AgendaDay"; + } else if (child instanceof UIScheduleAgendaWeekView) { + suffix = "AgendaWeek"; + } else if (child instanceof UIScheduleBasicDayView) { + suffix = "BasicDay"; + } else if (child instanceof UIScheduleBasicWeekView) { + 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 options = new HashMap(); + addOptionIfSet("defaultView", schedule.getView(), options); +// TODO shall we numerate firstDayOfWeek as in Calendar (sunday=1,monday=2,etc.) or like in widget(sunday=0,monday=1,etc.)? + if (schedule.getFirstDay() != null) { + options.put("firstDay", schedule.getFirstDay() - 1); + } + addOptionIfSet("isRTL", schedule.getIsRTL(), options); + addOptionIfSet("weekends", schedule.getShowWeekends(), options); + addOptionIfSet("weekMode", schedule.getWeekMode(), options); + addOptionIfSet("height", schedule.getHeight(), options); + addOptionIfSet("contentHeight", schedule.getContentHeight(), options); + addOptionIfSet("aspectRatio", schedule.getAspectRatio(), options); + addOptionIfSet("allDaySlot", schedule.getAllDaySlot(), options); + addOptionIfSet("allDayText", schedule.getAllDayText(), options); + addOptionIfSet("axisFormat", schedule.getAxisFormat(), options); + addOptionIfSet("slotMinutes", schedule.getSlotMinutes(), options); + addOptionIfSet("defaultEventMinutes", schedule.getDefaultEventMinutes(), options); + addOptionIfSet("firstHour", schedule.getFirstHour(), options); + addOptionIfSet("minTime", schedule.getMinTime(), options); + addOptionIfSet("maxTime", schedule.getMaxTime(), options); + addOptionIfSet("editable", schedule.getEditable(), options); + addOptionIfSet("disableDragging", schedule.getDisableDragging(), options); + addOptionIfSet("disableResizing", schedule.getDisableResizing(), options); + addOptionIfSet("dragRevertDuration", schedule.getDragRevertDuration(), options); + addOptionHash("dragOpacity", schedule, options); + addOptionHash("titleFormat", schedule, options); + addOptionHash("timeFormat", schedule, options); + addOptionHash("columnFormat", schedule, options); + Map headerOptions = new HashMap(3); + addOptionIfSet("left", schedule.getHeaderLeft(), headerOptions); + addOptionIfSet("center", schedule.getHeaderCenter(), headerOptions); + addOptionIfSet("right", schedule.getHeaderRight(), headerOptions); + if (headerOptions.size() > 0) { + options.put("header", headerOptions); + } + addOptionIfSet("allDayDefault", schedule.getAllDayByDefault(), options); + addOptionIfSet("onItemSelected", schedule.getOnItemSelected(), options); + addOptionIfSet("onItemDrop", schedule.getOnItemDrop(), options); + addOptionIfSet("onItemResized", schedule.getOnItemResized(), options); + addOptionIfSet("onItemResizeStart", schedule.getOnItemResizeStart(), options); + addOptionIfSet("onItemResizeStop", schedule.getOnItemResizeStop(), options); + addOptionIfSet("onItemDragStart", schedule.getOnItemDragStart(), options); + addOptionIfSet("onItemDragStop", schedule.getOnItemDragStop(), options); + addOptionIfSet("onItemMouseover", schedule.getOnItemMouseover(), options); + addOptionIfSet("onItemMouseout", schedule.getOnItemMouseout(), options); + addOptionIfSet("onViewDisplay", schedule.getOnViewDisplay(), options); + addOptionIfSet("onDateSelected", schedule.getOnDateSelected(), 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 initialItems = new HashMap(); + Date startDate = UISchedule.getFirstDisplayedDay(schedule); + Date endDate = UISchedule.getLastDisplayedDate(schedule); + initialItems.put("items", schedule.getScheduleData(startDate, endDate)); + Map date = new HashMap(); + 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(); + 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 void addOptionIfSet(String optionName, Object value, Map options) { + if (value != null && value != "") { + options.put(optionName, value); + } + } + + public String getFieldId(FacesContext context, UISchedule component, String attribute) { + return getUtils().clientId(context, component) + NamingContainer.SEPARATOR_CHAR + attribute; + } + + private void addOptionHash(String attribute, UIComponent source, Map options) { + Map hash = new HashMap(3); + Map attributes = source.getAttributes(); + addOptionIfSet("month", attributes.get(attribute + "Month"), hash); + addOptionIfSet("basicWeek", attributes.get(attribute + "BasicWeek"), hash); + addOptionIfSet("agendaWeek", attributes.get(attribute + "AgendaWeek"), hash); + addOptionIfSet("basicDay", attributes.get(attribute + "BasicDay"), hash); + addOptionIfSet("agendaDay", attributes.get(attribute + "AgendaDay"), hash); + addOptionIfSet("", attributes.get(attribute), hash); + if (hash.size() > 0) { + options.put(attribute, hash); + } + } + + private boolean isAjaxMode(UISchedule component) { + String mode = component.getSwitchType(); + return AJAX_MODE.equals(mode) || "".equals(mode) || null == mode; + } + + private boolean isClientMode(UISchedule component) { + String mode = component.getSwitchType(); + return CLIENT_MODE.equals(mode); + } + + 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); + } + } +} diff --git a/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java b/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java new file mode 100644 index 0000000..7719455 --- /dev/null +++ b/src/main/java/org/richfaces/renderkit/html/scripts/ScheduleMessages.java @@ -0,0 +1,49 @@ +package org.richfaces.renderkit.html.scripts; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import javax.faces.FactoryFinder; +import javax.faces.context.ExternalContext; +import javax.faces.context.FacesContext; +import javax.faces.context.FacesContextFactory; +import org.ajax4jsf.resource.InternetResourceBase; +import org.ajax4jsf.resource.ResourceContext; + +public class ScheduleMessages extends InternetResourceBase { + + @Override + public InputStream getResourceAsStream(ResourceContext context) { +//TODO how to access messages from here if user keeps them somewhere else? + String language = "pl"; + Locale locale = new Locale(language); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); +//TODO load default locale if specific is null + ResourceBundle bundle = ResourceBundle.getBundle("org.richfaces.component.scheduleMessages", locale, loader); + StringBuilder out = new StringBuilder(); + out.append("RichFaces.Schedule.prototype.messages=jQuery.extend(RichFaces.Schedule.prototype.messages,{").append("'").append(locale).append("':{").append("monthNames:["); + String[] months = new String[]{"JANUARY","FEBRUARY"}; + for (int i = 0; i < months.length; i++) { + out.append("'"); +//TODO Handle MissingResourceException +//TODO Escape message strings + try { + out.append(escape(bundle.getString("org.richfaces.component.UISchedule.monthNames." + months[i]))); + } catch (MissingResourceException e) { + out.append(escape(months[i])); + } + out.append("'"); + if (i + 1 < months.length) { + out.append(","); + } + } + out.append("]").append("}").append("})"); + return new ByteArrayInputStream(out.toString().getBytes()); + } + + private String escape(String message) { + return message.replaceAll("'", "\\'"); + } +} diff --git a/src/main/java/org/richfaces/taglib/ScheduleTagHandlerBase.java b/src/main/java/org/richfaces/taglib/ScheduleTagHandlerBase.java new file mode 100644 index 0000000..1964387 --- /dev/null +++ b/src/main/java/org/richfaces/taglib/ScheduleTagHandlerBase.java @@ -0,0 +1,92 @@ +package org.richfaces.taglib; + +import com.sun.facelets.FaceletContext; +import com.sun.facelets.tag.MetaRule; +import com.sun.facelets.tag.MetaRuleset; +import com.sun.facelets.tag.Metadata; +import com.sun.facelets.tag.MetadataTarget; +import com.sun.facelets.tag.TagAttribute; +import com.sun.facelets.tag.jsf.ComponentConfig; +import com.sun.facelets.tag.jsf.ComponentHandler; +import javax.faces.component.UIComponent; +import javax.faces.el.MethodBinding; +import org.richfaces.event.NodeExpandedEvent; +import org.richfaces.event.NodeSelectedEvent; + +public class ScheduleTagHandlerBase extends ComponentHandler { + + private final static String DATE_RANGE_CHANGED_LISTENER = "dateRangeChangedListener"; + private final static String DATE_SELECTED_LISTENER = "dateSelectedListener"; + private final static String ITEM_RESIZE_LISTENER = "itemResizeListener"; + private final static String ITEM_MOVE_LISTENER = "itemMoveListener"; + private final static String ITEM_SELECTED_LISTENER = "itemSelectedListener"; + private final static String VIEW_CHANGED_LISTENER = "viewChangedListener"; + + public ScheduleTagHandlerBase(ComponentConfig config) { + super(config); + } + + @Override + protected MetaRuleset createMetaRuleset(Class clazz) { + MetaRuleset ruleset = super.createMetaRuleset(clazz); + + ruleset.addRule(new MetaRule() { + + public Metadata applyRule(String name, final TagAttribute attribute, MetadataTarget metadataTarget) { + if (DATE_RANGE_CHANGED_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeExpandedEvent.class}); + ((UIComponent) object).getAttributes().put(DATE_RANGE_CHANGED_LISTENER, binding); + } + }; + } else if (DATE_SELECTED_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeSelectedEvent.class}); + ((UIComponent) object).getAttributes().put(DATE_SELECTED_LISTENER, binding); + } + }; + } else if (ITEM_RESIZE_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeSelectedEvent.class}); + ((UIComponent) object).getAttributes().put(ITEM_RESIZE_LISTENER, binding); + } + }; + } else if (ITEM_MOVE_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeSelectedEvent.class}); + ((UIComponent) object).getAttributes().put(ITEM_MOVE_LISTENER, binding); + } + }; + } else if (ITEM_SELECTED_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeSelectedEvent.class}); + ((UIComponent) object).getAttributes().put(ITEM_SELECTED_LISTENER, binding); + } + }; + } else if (VIEW_CHANGED_LISTENER.equals(name)) { + return new Metadata() { + + public void applyMetadata(FaceletContext context, Object object) { + MethodBinding binding = context.getFacesContext().getApplication().createMethodBinding(attribute.getValue(), new Class[]{NodeSelectedEvent.class}); + ((UIComponent) object).getAttributes().put(VIEW_CHANGED_LISTENER, binding); + } + }; + } + + return null; + } + }); + + return ruleset; + } +} diff --git a/src/main/resources/org/richfaces/component/scheduleMessages.properties b/src/main/resources/org/richfaces/component/scheduleMessages.properties new file mode 100644 index 0000000..47bed51 --- /dev/null +++ b/src/main/resources/org/richfaces/component/scheduleMessages.properties @@ -0,0 +1 @@ +org.richfaces.component.UISchedule.monthNames.JANUARY=January \ No newline at end of file diff --git a/src/main/resources/org/richfaces/renderkit/html/css/fullcalendar.css b/src/main/resources/org/richfaces/renderkit/html/css/fullcalendar.css new file mode 100644 index 0000000..888f6ea --- /dev/null +++ b/src/main/resources/org/richfaces/renderkit/html/css/fullcalendar.css @@ -0,0 +1,575 @@ +/* + * FullCalendar v1.4.5 Stylesheet + * + * Feel free to edit this file to customize the look of FullCalendar. + * When upgrading to newer versions, please upgrade this file as well, + * porting over any customizations afterwards. + * + * Date: Sun Feb 21 20:30:11 2010 -0800 + * + * TODO adjust class names to richfaces naming conventions + */ + + +.fc, +.fc .fc-header, +.fc .fc-content { + font-size: 1em; + } + +.fc { + direction: ltr; + text-align: left; + } + +.fc table { + border-collapse: collapse; + border-spacing: 0; + } + +.fc td, .fc th { + padding: 0; + vertical-align: top; + } + + + +/* Header +------------------------------------------------------------------------*/ + +table.fc-header { + width: 100%; + } + +.fc-header-left { + width: 25%; + } + +.fc-header-left table { + float: left; + } + +.fc-header-center { + width: 50%; + text-align: center; + } + +.fc-header-center table { + margin: 0 auto; + } + +.fc-header-right { + width: 25%; + } + +.fc-header-right table { + float: right; + } + +.fc-header-title { + margin-top: 0; + white-space: nowrap; + } + +.fc-header-space { + padding-left: 10px; + } + +/* right-to-left */ + +.fc-rtl .fc-header-title { + direction: rtl; + } + + + +/* Buttons +------------------------------------------------------------------------*/ + +.fc-header .fc-state-default, +.fc-header .ui-state-default { + margin-bottom: 1em; + cursor: pointer; + } + +.fc-header .fc-state-default { + border-width: 1px 0; + padding: 0 1px; + } + +.fc-header .fc-state-default, +.fc-header .fc-state-default a { + border-style: solid; + } + +.fc-header .fc-state-default a { + display: block; + border-width: 0 1px; + margin: 0 -1px; + width: 100%; + text-decoration: none; + } + +.fc-header .fc-state-default span { + display: block; + border-style: solid; + border-width: 1px 0 1px 1px; + padding: 3px 5px; + } + +.fc-header .ui-state-default { + padding: 4px 6px; + } + +.fc-header .fc-state-default span, +.fc-header .ui-state-default span { + white-space: nowrap; + } + +/* for adjacent buttons */ + +.fc-header .fc-no-right { + padding-right: 0; + } + +.fc-header .fc-no-right a { + margin-right: 0; + border-right: 0; + } + +.fc-header .ui-no-right { + border-right: 0; + } + +/* for fake rounded corners */ + +.fc-header .fc-corner-left { + margin-left: 1px; + padding-left: 0; + } + +.fc-header .fc-corner-right { + margin-right: 1px; + padding-right: 0; + } + +/* DEFAULT button COLORS */ + +.fc-header .fc-state-default, +.fc-header .fc-state-default a { + border-color: #777; /* outer border */ + color: #333; + } + +.fc-header .fc-state-default span { + border-color: #fff #fff #d1d1d1; /* inner border */ + background: #e8e8e8; + } + +/* PRESSED button COLORS (down and active) */ + +.fc-header .fc-state-active a { + color: #fff; + } + +.fc-header .fc-state-down span, +.fc-header .fc-state-active span { + background: #888; + border-color: #808080 #808080 #909090; /* inner border */ + } + +/* DISABLED button COLORS */ + +.fc-header .fc-state-disabled a { + color: #999; + } + +.fc-header .fc-state-disabled, +.fc-header .fc-state-disabled a { + border-color: #ccc; /* outer border */ + } + +.fc-header .fc-state-disabled span { + border-color: #fff #fff #f0f0f0; /* inner border */ + background: #f0f0f0; + } + + + +/* Content Area & Global Cell Styles +------------------------------------------------------------------------*/ + +.fc-widget-content { + border: 1px solid #ccc; /* outer border color */ + } + +.fc-content { + clear: both; + } + +.fc-content .fc-state-default { + border-style: solid; + border-color: #ccc; /* inner border color */ + } + +.fc-content .fc-state-highlight { /* today */ + background: #ffc; + } + +.fc-content .fc-not-today { + background: none; + } + +.fc-cell-overlay { /* semi-transparent rectangle while dragging */ + background: #9cf; + opacity: .2; + filter: alpha(opacity=20); /* for IE */ + } + +.fc-view { /* prevents dragging outside of widget */ + width: 100%; + overflow: hidden; + } + + + +/* Global Event Styles +------------------------------------------------------------------------*/ + +.fc-event, +.fc-agenda .fc-event-time, +.fc-event a { + border-style: solid; + border-color: #36c; /* default BORDER color (probably the same as background-color) */ + background-color: #36c; /* default BACKGROUND color */ + color: #fff; /* default TEXT color */ + } + + /* Use the 'className' CalEvent property and the following + * example CSS to change event color on a per-event basis: + * + * .myclass, + * .fc-agenda .myclass .fc-event-time, + * .myclass a { + * background-color: black; + * border-color: black; + * color: red; + * } + */ + +.fc-event { + text-align: left; + } + +.fc-event a { + overflow: hidden; + font-size: .85em; + text-decoration: none; + cursor: pointer; + } + +.fc-event-editable { + cursor: pointer; + } + +.fc-event-time, +.fc-event-title { + padding: 0 1px; + } + +/* for fake rounded corners */ + +.fc-event a { + display: block; + position: relative; + width: 100%; + height: 100%; + } + +/* right-to-left */ + +.fc-rtl .fc-event a { + text-align: right; + } + +/* resizable */ + +.fc .ui-resizable-handle { + display: block; + position: absolute; + z-index: 99999; + border: 0 !important; /* important overrides pre jquery ui 1.7 styles */ + background: url(data:image/gif;base64,AAAA) !important; /* hover fix for IE */ + } + + + +/* Horizontal Events +------------------------------------------------------------------------*/ + +.fc-event-hori { + border-width: 1px 0; + margin-bottom: 1px; + } + +.fc-event-hori a { + border-width: 0; + } + +/* for fake rounded corners */ + +.fc-content .fc-corner-left { + margin-left: 1px; + } + +.fc-content .fc-corner-left a { + margin-left: -1px; + border-left-width: 1px; + } + +.fc-content .fc-corner-right { + margin-right: 1px; + } + +.fc-content .fc-corner-right a { + margin-right: -1px; + border-right-width: 1px; + } + +/* resizable */ + +.fc-event-hori .ui-resizable-e { + top: 0 !important; /* importants override pre jquery ui 1.7 styles */ + right: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: e-resize; + } + +.fc-event-hori .ui-resizable-w { + top: 0 !important; + left: -3px !important; + width: 7px !important; + height: 100% !important; + cursor: w-resize; + } + +.fc-event-hori .ui-resizable-handle { + _padding-bottom: 14px; /* IE6 had 0 height */ + } + + + +/* Month View, Basic Week View, Basic Day View +------------------------------------------------------------------------*/ + +.fc-grid table { + width: 100%; + } + +.fc .fc-grid th { + border-width: 0 0 0 1px; + text-align: center; + } + +.fc .fc-grid td { + border-width: 1px 0 0 1px; + } + +.fc-grid th.fc-leftmost, +.fc-grid td.fc-leftmost { + border-left: 0; + } + +.fc-grid .fc-day-number { + float: right; + padding: 0 2px; + } + +.fc-grid .fc-other-month .fc-day-number { + opacity: 0.3; + filter: alpha(opacity=30); /* for IE */ + /* opacity with small font can sometimes look too faded + might want to set the 'color' property instead + making day-numbers bold also fixes the problem */ + } + +.fc-grid .fc-day-content { + clear: both; + padding: 2px 2px 0; /* distance between events and day edges */ + } + +/* event styles */ + +.fc-grid .fc-event-time { + font-weight: bold; + } + +/* right-to-left */ + +.fc-rtl .fc-grid { + direction: rtl; + } + +.fc-rtl .fc-grid .fc-day-number { + float: left; + } + +.fc-rtl .fc-grid .fc-event-time { + float: right; + } + +/* Agenda Week View, Agenda Day View +------------------------------------------------------------------------*/ + +.fc .fc-agenda th, +.fc .fc-agenda td { + border-width: 1px 0 0 1px; + } + +.fc .fc-agenda .fc-leftmost { + border-left: 0; + } + +.fc-agenda tr.fc-first th, +.fc-agenda tr.fc-first td { + border-top: 0; + } + +.fc-agenda-head tr.fc-last th { + border-bottom-width: 1px; + } + +.fc .fc-agenda-head td, +.fc .fc-agenda-body td { + background: none; + } + +.fc-agenda-head th { + text-align: center; + } + +/* the time axis running down the left side */ + +.fc-agenda .fc-axis { + width: 50px; + padding: 0 4px; + vertical-align: middle; + white-space: nowrap; + text-align: right; + font-weight: normal; + } + +/* all-day event cells at top */ + +.fc-agenda-head tr.fc-all-day th { + height: 35px; + } + +.fc-agenda-head td { + padding-bottom: 10px; + } + +.fc .fc-divider div { + font-size: 1px; /* for IE6/7 */ + height: 2px; + } + +.fc .fc-divider .fc-state-default { + background: #eee; /* color for divider between all-day and time-slot events */ + } + +/* body styles */ + +.fc .fc-agenda-body td div { + height: 20px; /* slot height */ + } + +.fc .fc-agenda-body tr.fc-minor th, +.fc .fc-agenda-body tr.fc-minor td { + border-top-style: dotted; + } + +.fc-agenda .fc-day-content { + padding: 2px 2px 0; /* distance between events and day edges */ + } + + + +/* Vertical Events +------------------------------------------------------------------------*/ + +.fc-event-vert { + border-width: 0 1px; + } + +.fc-event-vert a { + border-width: 0; + } + +/* for fake rounded corners */ + +.fc-content .fc-corner-top { + margin-top: 1px; + } + +.fc-content .fc-corner-top a { + margin-top: -1px; + border-top-width: 1px; + } + +.fc-content .fc-corner-bottom { + margin-bottom: 1px; + } + +.fc-content .fc-corner-bottom a { + margin-bottom: -1px; + border-bottom-width: 1px; + } + +/* event content */ + +.fc-event-vert span { + display: block; + position: relative; + z-index: 2; + } + +.fc-event-vert span.fc-event-time { + white-space: nowrap; + _white-space: normal; + overflow: hidden; + border: 0; + font-size: 10px; + } + +.fc-event-vert span.fc-event-title { + line-height: 13px; + } + +.fc-event-vert span.fc-event-bg { /* makes the event lighter w/ a semi-transparent overlay */ + position: absolute; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: #fff; + opacity: .3; + filter: alpha(opacity=30); /* for IE */ + } + +/* resizable */ + +.fc-event-vert .ui-resizable-s { + bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */ + width: 100% !important; + height: 8px !important; + line-height: 8px !important; + font-size: 11px !important; + font-family: monospace; + text-align: center; + cursor: s-resize; + } + + diff --git a/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js b/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js new file mode 100644 index 0000000..644f4ad --- /dev/null +++ b/src/main/resources/org/richfaces/renderkit/html/scripts/fullcalendar.js @@ -0,0 +1,3418 @@ +/*! + * FullCalendar v1.4.5 + * http://arshaw.com/fullcalendar/ + * + * Use fullcalendar.css for basic styling. + * For event drag & drop, required jQuery UI draggable. + * For event resizing, requires jQuery UI resizable. + * + * Copyright (c) 2009 Adam Shaw + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Date: Sun Feb 21 20:30:11 2010 -0800 + * + */ + +(function($) { + + +var fc = $.fullCalendar = {}; +var views = fc.views = {}; + + +/* Defaults +-----------------------------------------------------------------------------*/ + +var defaults = { + + // display + defaultView: 'month', + aspectRatio: 1.35, + header: { + left: 'title', + center: '', + right: 'today prev,next' + }, + weekends: true, + + // editing + //editable: false, + //disableDragging: false, + //disableResizing: false, + + allDayDefault: true, + + // event ajax + lazyFetching: true, + startParam: 'start', + endParam: 'end', + + // time formats + titleFormat: { + month: 'MMMM yyyy', + week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", + day: 'dddd, MMM d, yyyy' + }, + columnFormat: { + month: 'ddd', + week: 'ddd M/d', + day: 'dddd M/d' + }, + timeFormat: { // for event elements + '': 'h(:mm)t' // default + }, + + // locale + isRTL: false, + firstDay: 0, + monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], + monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'], + dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], + dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], + buttonText: { + prev: ' ◄ ', + next: ' ► ', + prevYear: ' << ', + nextYear: ' >> ', + today: 'today', + month: 'month', + week: 'week', + day: 'day' + }, + + // jquery-ui theming + theme: false, + buttonIcons: { + prev: 'circle-triangle-w', + next: 'circle-triangle-e' + } + +}; + +// right-to-left defaults +var rtlDefaults = { + header: { + left: 'next,prev today', + center: '', + right: 'title' + }, + buttonText: { + prev: ' ► ', + next: ' ◄ ', + prevYear: ' >> ', + nextYear: ' << ' + }, + buttonIcons: { + prev: 'circle-triangle-e', + next: 'circle-triangle-w' + } +}; + +// function for adding/overriding defaults +var setDefaults = fc.setDefaults = function(d) { + $.extend(true, defaults, d); +} + + + +/* .fullCalendar jQuery function +-----------------------------------------------------------------------------*/ + +$.fn.fullCalendar = function(options) { + + // method calling + if (typeof options == 'string') { + var args = Array.prototype.slice.call(arguments, 1), + res; + this.each(function() { + var data = $.data(this, 'fullCalendar'); + if (data) { + var r = data[options].apply(this, args); + if (res == undefined) { + res = r; + } + } + }); + if (res != undefined) { + return res; + } + return this; + } + + // pluck the 'events' and 'eventSources' options + var eventSources = options.eventSources || []; + delete options.eventSources; + if (options.events) { + eventSources.push(options.events); + delete options.events; + } + + // first event source reserved for 'sticky' events + eventSources.unshift([]); + + // initialize options + options = $.extend(true, {}, + defaults, + (options.isRTL || options.isRTL==undefined && defaults.isRTL) ? rtlDefaults : {}, + options + ); + var tm = options.theme ? 'ui' : 'fc'; // for making theme classes + + + this.each(function() { + + + /* Instance Initialization + -----------------------------------------------------------------------------*/ + + // element + var _element = this, + element = $(_element).addClass('fc'), + elementOuterWidth, + content = $("
").prependTo(_element), + suggestedViewHeight, + resizeUID = 0, + ignoreWindowResize = 0, + date = new Date(), + viewName, // the current view name (TODO: look into getting rid of) + view, // the current view + viewInstances = {}, + absoluteViewElement; + + + + if (options.isRTL) { + element.addClass('fc-rtl'); + } + if (options.theme) { + element.addClass('ui-widget'); + } + + if (options.year != undefined && options.year != date.getFullYear()) { + date.setDate(1); + date.setMonth(0); + date.setFullYear(options.year); + } + if (options.month != undefined && options.month != date.getMonth()) { + date.setDate(1); + date.setMonth(options.month); + } + if (options.date != undefined) { + date.setDate(options.date); + } + + + + /* View Rendering + -----------------------------------------------------------------------------*/ + + function changeView(v) { + if (v != viewName) { + ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached + + var oldView = view, + newViewElement; + + if (oldView) { + if (oldView.eventsChanged) { + eventsDirty(); + oldView.eventDirty = oldView.eventsChanged = false; + } + if (oldView.beforeHide) { + oldView.beforeHide(); // called before changing min-height. if called after, scroll state is reset (in Opera) + } + setMinHeight(content, content.height()); + oldView.element.hide(); + }else{ + setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated + } + content.css('overflow', 'hidden'); + + if (viewInstances[v]) { + (view = viewInstances[v]).element.show(); + }else{ + view = viewInstances[v] = $.fullCalendar.views[v]( + newViewElement = absoluteViewElement = + $("
") + .appendTo(content), + options + ); + } + + if (header) { + // update 'active' view button + header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active'); + header.find('div.fc-button-' + v).addClass(tm + '-state-active'); + } + + view.name = viewName = v; + render(); // after height has been set, will make absoluteViewElement's position=relative, then set to null + content.css('overflow', ''); + if (oldView) { + setMinHeight(content, 1); + } + if (!newViewElement && view.afterShow) { + view.afterShow(); // called after setting min-height/overflow, so in final scroll state (for Opera) + } + + ignoreWindowResize--; + } + } + + + function render(inc) { + if (elementVisible()) { + ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached + + if (suggestedViewHeight == undefined) { + calcSize(); + } + + if (!view.start || inc || date < view.start || date >= view.end) { + view.render(date, inc || 0); // responsible for clearing events + setSize(true); + if (!eventStart || !options.lazyFetching || view.visStart < eventStart || view.visEnd > eventEnd) { + fetchAndRenderEvents(); + }else{ + view.renderEvents(events); // don't refetch + } + } + else if (view.sizeDirty || view.eventsDirty || !options.lazyFetching) { + view.clearEvents(); + if (view.sizeDirty) { + setSize(); + } + if (options.lazyFetching) { + view.renderEvents(events); // don't refetch + }else{ + fetchAndRenderEvents(); + } + } + elementOuterWidth = element.outerWidth(); + view.sizeDirty = false; + view.eventsDirty = false; + + if (header) { + // update title text + header.find('h2.fc-header-title').html(view.title); + // enable/disable 'today' button + var today = new Date(); + if (today >= view.start && today < view.end) { + header.find('div.fc-button-today').addClass(tm + '-state-disabled'); + }else{ + header.find('div.fc-button-today').removeClass(tm + '-state-disabled'); + } + } + + ignoreWindowResize--; + view.trigger('viewDisplay', _element); + } + } + + + function elementVisible() { + return _element.offsetWidth !== 0; + } + + function bodyVisible() { + return $('body')[0].offsetWidth !== 0; + } + + + // called when any event objects have been added/removed/changed, rerenders + function eventsChanged() { + eventsDirty(); + if (elementVisible()) { + view.clearEvents(); + view.renderEvents(events); + view.eventsDirty = false; + } + } + + // marks other views' events as dirty + function eventsDirty() { + $.each(viewInstances, function() { + this.eventsDirty = true; + }); + } + + // called when we know the element size has changed + function sizeChanged() { + sizesDirty(); + if (elementVisible()) { + calcSize(); + setSize(); + view.rerenderEvents(); + view.sizeDirty = false; + } + } + + // marks other views' sizes as dirty + function sizesDirty() { + $.each(viewInstances, function() { + this.sizeDirty = true; + }); + } + + + + + /* Event Sources and Fetching + -----------------------------------------------------------------------------*/ + + var events = [], + eventStart, eventEnd; + + // Fetch from ALL sources. Clear 'events' array and populate + function fetchEvents(callback) { + events = []; + eventStart = cloneDate(view.visStart); + eventEnd = cloneDate(view.visEnd); + var queued = eventSources.length, + sourceDone = function() { + if (--queued == 0) { + if (callback) { + callback(events); + } + } + }, i=0; + for (; i") + .append($("") + .append($("").append(buildSection(sections.left))) + .append($("").append(buildSection(sections.center))) + .append($("").append(buildSection(sections.right)))) + .prependTo(element); + } + function buildSection(buttonStr) { + if (buttonStr) { + var tr = $(""); + $.each(buttonStr.split(' '), function(i) { + if (i > 0) { + tr.append(""); + } + var prevButton; + $.each(this.split(','), function(j, buttonName) { + if (buttonName == 'title') { + tr.append("

 

"); + if (prevButton) { + prevButton.addClass(tm + '-corner-right'); + } + prevButton = null; + }else{ + var buttonClick; + if (publicMethods[buttonName]) { + buttonClick = publicMethods[buttonName]; + } + else if (views[buttonName]) { + buttonClick = function() { + button.removeClass(tm + '-state-hover'); + changeView(buttonName) + }; + } + if (buttonClick) { + if (prevButton) { + prevButton.addClass(tm + '-no-right'); + } + var button, + icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null, + text = smartProperty(options.buttonText, buttonName); + if (icon) { + button = $("
" + + "
"); + } + else if (text) { + button = $(""); + } + if (button) { + button + .click(function() { + if (!button.hasClass(tm + '-state-disabled')) { + buttonClick(); + } + }) + .mousedown(function() { + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-down'); + }) + .mouseup(function() { + button.removeClass(tm + '-state-down'); + }) + .hover( + function() { + button + .not('.' + tm + '-state-active') + .not('.' + tm + '-state-disabled') + .addClass(tm + '-state-hover'); + }, + function() { + button + .removeClass(tm + '-state-hover') + .removeClass(tm + '-state-down'); + } + ) + .appendTo($("").appendTo(tr)); + if (prevButton) { + prevButton.addClass(tm + '-no-right'); + }else{ + button.addClass(tm + '-corner-left'); + } + prevButton = button; + } + } + } + }); + if (prevButton) { + prevButton.addClass(tm + '-corner-right'); + } + }); + return $("").append(tr); + } + } + + + + /* Resizing + -----------------------------------------------------------------------------*/ + + + function calcSize() { + if (options.contentHeight) { + suggestedViewHeight = options.contentHeight; + } + else if (options.height) { + suggestedViewHeight = options.height - (header ? header.height() : 0) - vsides(content[0]); + } + else { + suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5)); + } + } + + + function setSize(dateChanged) { + ignoreWindowResize++; + view.setHeight(suggestedViewHeight, dateChanged); + if (absoluteViewElement) { + absoluteViewElement.css('position', 'relative'); + absoluteViewElement = null; + } + view.setWidth(content.width(), dateChanged); + ignoreWindowResize--; + } + + + function windowResize() { + if (!ignoreWindowResize) { + if (view.start) { // view has already been rendered + var uid = ++resizeUID; + setTimeout(function() { // add a delay + if (uid == resizeUID && !ignoreWindowResize && elementVisible()) { + if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) { + ignoreWindowResize++; // in case the windowResize callback changes the height + sizeChanged(); + view.trigger('windowResize', _element); + ignoreWindowResize--; + } + } + }, 200); + }else{ + // calendar must have been initialized in a 0x0 iframe that has just been resized + lateRender(); + } + } + }; + $(window).resize(windowResize); + + + // let's begin... + changeView(options.defaultView); + + + // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize + if (!bodyVisible()) { + lateRender(); + } + + + // called when we know the calendar couldn't be rendered when it was initialized, + // but we think it's ready now + function lateRender() { + setTimeout(function() { // IE7 needs this so dimensions are calculated correctly + if (!view.start && bodyVisible()) { // !view.start makes sure this never happens more than once + render(); + } + },0); + } + + + }); + + return this; + +}; + + + +/* Important Event Utilities +-----------------------------------------------------------------------------*/ + +var fakeID = 0; + +function normalizeEvent(event, options) { + event._id = event._id || (event.id == undefined ? '_fc' + fakeID++ : event.id + ''); + if (event.date) { + if (!event.start) { + event.start = event.date; + } + delete event.date; + } + event._start = cloneDate(event.start = parseDate(event.start)); + event.end = parseDate(event.end); + if (event.end && event.end <= event.start) { + event.end = null; + } + event._end = event.end ? cloneDate(event.end) : null; + if (event.allDay == undefined) { + event.allDay = options.allDayDefault; + } + if (event.className) { + if (typeof event.className == 'string') { + event.className = event.className.split(/\s+/); + } + }else{ + event.className = []; + } +} +// TODO: if there is no title or start date, return false to indicate an invalid event + + +/* Grid-based Views: month, basicWeek, basicDay +-----------------------------------------------------------------------------*/ + +setDefaults({ + weekMode: 'fixed' +}); + +views.month = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addMonths(date, delta); + date.setDate(1); + } + // start/end + var start = this.start = cloneDate(date, true); + start.setDate(1); + this.end = addMonths(cloneDate(start), 1); + // visStart/visEnd + var visStart = this.visStart = cloneDate(start), + visEnd = this.visEnd = cloneDate(this.end), + nwe = options.weekends ? 0 : 1; + if (nwe) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7)); + addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7); + // row count + var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7)); + if (options.weekMode == 'fixed') { + addDays(visEnd, (6 - rowCnt) * 7); + rowCnt = 6; + } + // title + this.title = formatDate( + start, + this.option('titleFormat'), + options + ); + // render + this.renderGrid( + rowCnt, options.weekends ? 7 : 5, + this.option('columnFormat'), + true + ); + } + }); +} + +views.basicWeek = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta * 7); + } + var visStart = this.visStart = cloneDate( + this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) + ), + visEnd = this.visEnd = cloneDate( + this.end = addDays(cloneDate(visStart), 7) + ); + if (!options.weekends) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + this.title = formatDates( + visStart, + addDays(cloneDate(visEnd), -1), + this.option('titleFormat'), + options + ); + this.renderGrid( + 1, options.weekends ? 7 : 5, + this.option('columnFormat'), + false + ); + } + }); +}; + +views.basicDay = function(element, options) { + return new Grid(element, options, { + render: function(date, delta) { + if (delta) { + addDays(date, delta); + if (!options.weekends) { + skipWeekend(date, delta < 0 ? -1 : 1); + } + } + this.title = formatDate(date, this.option('titleFormat'), options); + this.start = this.visStart = cloneDate(date, true); + this.end = this.visEnd = addDays(cloneDate(this.start), 1); + this.renderGrid( + 1, 1, + this.option('columnFormat'), + false + ); + } + }); +} + + +// rendering bugs + +var tdHeightBug; + + +function Grid(element, options, methods) { + + var tm, firstDay, + nwe, // no weekends (int) + rtl, dis, dit, // day index sign / translate + viewWidth, viewHeight, + rowCnt, colCnt, + colWidth, + thead, tbody, + cachedEvents=[], + segmentContainer, + dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) { + return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div') + }), + // ... + + // initialize superclass + view = $.extend(this, viewMethods, methods, { + renderGrid: renderGrid, + renderEvents: renderEvents, + rerenderEvents: rerenderEvents, + clearEvents: clearEvents, + setHeight: setHeight, + setWidth: setWidth, + defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing + return cloneDate(event.start); + } + }); + view.init(element, options); + + + + /* Grid Rendering + -----------------------------------------------------------------------------*/ + + + element.addClass('fc-grid'); + if (element.disableSelection) { + element.disableSelection(); + } + + function renderGrid(r, c, colFormat, showNumbers) { + rowCnt = r; + colCnt = c; + + // update option-derived variables + tm = options.theme ? 'ui' : 'fc'; + nwe = options.weekends ? 0 : 1; + firstDay = options.firstDay; + if (rtl = options.isRTL) { + dis = -1; + dit = colCnt - 1; + }else{ + dis = 1; + dit = 0; + } + + var month = view.start.getMonth(), + today = clearTime(new Date()), + s, i, j, d = cloneDate(view.visStart); + + if (!tbody) { // first time, build all cells from scratch + + var table = $("
").appendTo(element); + + s = ""; + for (i=0; i" + formatDate(d, colFormat, options) + ""; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + thead = $(s + "").appendTo(table); + + s = ""; + d = cloneDate(view.visStart); + for (i=0; i"; + for (j=0; j1 && d.getMonth() != month ? ' fc-other-month' : '') + + (+d == +today ? + ' fc-today '+tm+'-state-highlight' : + ' fc-not-today') + "'>" + + (showNumbers ? "
" + d.getDate() + "
" : '') + + "
 
"; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + s += ""; + } + tbody = $(s + "
").appendTo(table); + tbody.find('td').click(dayClick); + + segmentContainer = $("
").appendTo(element); + + }else{ // NOT first time, reuse as many cells as possible + + clearEvents(); + + var prevRowCnt = tbody.find('tr').length; + if (rowCnt < prevRowCnt) { + tbody.find('tr:gt(' + (rowCnt-1) + ')').remove(); // remove extra rows + } + else if (rowCnt > prevRowCnt) { // needs to create new rows... + s = ''; + for (i=prevRowCnt; i"; + for (j=0; j" + + (showNumbers ? "
" : '') + + "
 
" + + ""; + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + } + s += ""; + } + tbody.append(s); + } + tbody.find('td.fc-new').removeClass('fc-new').click(dayClick); + + // re-label and re-class existing cells + d = cloneDate(view.visStart); + tbody.find('td').each(function() { + var td = $(this); + if (rowCnt > 1) { + if (d.getMonth() == month) { + td.removeClass('fc-other-month'); + }else{ + td.addClass('fc-other-month'); + } + } + if (+d == +today) { + td.removeClass('fc-not-today') + .addClass('fc-today') + .addClass(tm + '-state-highlight'); + }else{ + td.addClass('fc-not-today') + .removeClass('fc-today') + .removeClass(tm + '-state-highlight'); + } + td.find('div.fc-day-number').text(d.getDate()); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + if (rowCnt == 1) { // more changes likely (week or day view) + + // redo column header text and class + d = cloneDate(view.visStart); + thead.find('th').each(function() { + $(this).text(formatDate(d, colFormat, options)); + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + // redo cell day-of-weeks + d = cloneDate(view.visStart); + tbody.find('td').each(function() { + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, 1); + if (nwe) { + skipWeekend(d); + } + }); + + } + + } + + }; + + + function dayClick(ev) { + var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]), + date = addDays( + cloneDate(view.visStart), + Math.floor(n/colCnt) * 7 + n % colCnt + ); + view.trigger('dayClick', this, date, true, ev); + } + + + + function setHeight(height) { + viewHeight = height; + var leftTDs = tbody.find('tr td:first-child'), + tbodyHeight = viewHeight - thead.height(), + rowHeight1, rowHeight2; + if (options.weekMode == 'variable') { + rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6)); + }else{ + rowHeight1 = Math.floor(tbodyHeight / rowCnt); + rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1); + } + if (tdHeightBug == undefined) { + // bug in firefox where cell height includes padding + var tr = tbody.find('tr:first'), + td = tr.find('td:first'); + td.height(rowHeight1); + tdHeightBug = rowHeight1 != td.height(); + } + if (tdHeightBug) { + leftTDs.slice(0, -1).height(rowHeight1); + leftTDs.slice(-1).height(rowHeight2); + }else{ + setOuterHeight(leftTDs.slice(0, -1), rowHeight1); + setOuterHeight(leftTDs.slice(-1), rowHeight2); + } + } + + + function setWidth(width) { + viewWidth = width; + dayContentPositions.clear(); + setOuterWidth( + thead.find('th').slice(0, -1), + colWidth = Math.floor(viewWidth / colCnt) + ); + } + + + + /* Event Rendering + -----------------------------------------------------------------------------*/ + + + function renderEvents(events) { + view.reportEvents(cachedEvents = events); + renderSegs(compileSegs(events)); + } + + + function rerenderEvents(modifiedEventId) { + clearEvents(); + renderSegs(compileSegs(cachedEvents), modifiedEventId); + } + + + function clearEvents() { + view._clearEvents(); // only clears the hashes + segmentContainer.empty(); + } + + + function compileSegs(events) { + var d1 = cloneDate(view.visStart), + d2 = addDays(cloneDate(d1), colCnt), + visEventsEnds = $.map(events, visEventEnd), + i, row, + j, level, + k, seg, + segs=[]; + for (i=0; i" + + "" + + (!event.allDay && seg.isStart ? + "" + + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'), options)) + + "" + :'') + + "" + htmlEscape(event.title) + "" + + "" + + ((event.editable || event.editable == undefined && options.editable) && !options.disableResizing && $.fn.resizable ? + "
" + : '') + + "
"; + seg.left = left; + seg.outerWidth = right - left; + } + segmentContainer[0].innerHTML = html; // faster than html() + eventElements = segmentContainer.children(); + + // retrieve elements, run through eventRender callback, bind handlers + for (i=0; i div') // optimal selector? + .height(top + levelHeight); + } + + // calculate row tops + for (rowI=0; rowI" + + "" + + ""; + for (i=0; i" + formatDate(d, colFormat, options) + ""; + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + } + s += ""; + if (options.allDaySlot) { + s += "" + + "" + + "" + + "" + + ""; + } + s+= "
  
" + options.allDayText + "" + + "
 
 
"; + head = $(s).appendTo(element); + head.find('td').click(slotClick); + + // all-day event container + daySegmentContainer = $("
").appendTo(head); + + // body + d = zeroDate(); + var maxd = addMinutes(cloneDate(d), maxMinute); + addMinutes(d, minMinute); + s = ""; + for (i=0; d < maxd; i++) { + minutes = d.getMinutes(); + s += ""; + addMinutes(d, options.slotMinutes); + } + s += "
" + + ((!slotNormal || minutes==0) ? formatDate(d, options.axisFormat) : ' ') + + "
 
"; + body = $("
") + .append(bodyContent = $("
") + .append(bodyTable = $(s))) + .appendTo(element); + body.find('td').click(slotClick); + + // slot event container + slotSegmentContainer = $("
").appendTo(bodyContent); + + // background stripes + d = cloneDate(d0); + s = "
" + + ""; + for (i=0; i
 
"; + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + } + s += "
"; + bg = $(s).appendTo(element); + + }else{ // skeleton already built, just modify it + + clearEvents(); + + // redo column header text and class + head.find('tr:first th').slice(1, -1).each(function() { + $(this).text(formatDate(d, colFormat, options)); + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + }); + + // change classes of background stripes + d = cloneDate(d0); + bg.find('td').each(function() { + this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); + if (+d == +today) { + $(this) + .removeClass('fc-not-today') + .addClass('fc-today') + .addClass(tm + '-state-highlight'); + }else{ + $(this) + .addClass('fc-not-today') + .removeClass('fc-today') + .removeClass(tm + '-state-highlight'); + } + addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } + }); + + } + + }; + + + function resetScroll() { + var d0 = zeroDate(), + scrollDate = cloneDate(d0); + scrollDate.setHours(options.firstHour); + var top = timePosition(d0, scrollDate) + 1, // +1 for the border + scroll = function() { + body.scrollTop(top); + }; + scroll(); + setTimeout(scroll, 0); // overrides any previous scroll state made by the browser + } + + + function setHeight(height, dateChanged) { + viewHeight = height; + slotTopCache = {}; + + body.height(height - head.height()); + + slotHeight = body.find('tr:first div').height() + 1; + + bg.css({ + top: head.find('tr').height(), + height: height + }); + + if (dateChanged) { + resetScroll(); + } + } + + + function setWidth(width) { + viewWidth = width; + colContentPositions.clear(); + + body.width(width); + bodyTable.width(''); + + var topTDs = head.find('tr:first th'), + stripeTDs = bg.find('td'), + clientWidth = body[0].clientWidth; + + bodyTable.width(clientWidth); + + // time-axis width + axisWidth = 0; + setOuterWidth( + head.find('tr:lt(2) th:first').add(body.find('tr:first th')) + .width('') + .each(function() { + axisWidth = Math.max(axisWidth, $(this).outerWidth()); + }), + axisWidth + ); + + // column width + colWidth = Math.floor((clientWidth - axisWidth) / colCnt); + setOuterWidth(stripeTDs.slice(0, -1), colWidth); + setOuterWidth(topTDs.slice(1, -2), colWidth); + setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth*(colCnt-1)); + + bg.css({ + left: axisWidth, + width: clientWidth - axisWidth + }); + } + + + + + function slotClick(ev) { + var col = Math.floor((ev.pageX - bg.offset().left) / colWidth), + date = addDays(cloneDate(view.visStart), dit + dis*col), + rowMatch = this.className.match(/fc-slot(\d+)/); + if (rowMatch) { + var mins = parseInt(rowMatch[1]) * options.slotMinutes, + hours = Math.floor(mins/60); + date.setHours(hours); + date.setMinutes(mins%60 + minMinute); + view.trigger('dayClick', this, date, false, ev); + }else{ + view.trigger('dayClick', this, date, true, ev); + } + } + + + + /* Event Rendering + -----------------------------------------------------------------------------*/ + + function renderEvents(events, modifiedEventId) { + view.reportEvents(cachedEvents = events); + var i, len=events.length, + dayEvents=[], + slotEvents=[]; + for (i=0; i" + + "" + + "" + + "" + htmlEscape(formatDates(event.start, event.end, view.option('timeFormat'))) + "" + + "" + htmlEscape(event.title) + "" + + "" + + ((event.editable || event.editable == undefined && options.editable) && !options.disableResizing && $.fn.resizable ? + "
=
" + : '') + + "
"; + } + slotSegmentContainer[0].innerHTML = html; // faster than html() + eventElements = slotSegmentContainer.children(); + + // retrieve elements, run through eventRender callback, bind event handlers + for (i=0; i= addMinutes(cloneDate(day), maxMinute)) { + return bodyContent.height(); + } + var slotMinutes = options.slotMinutes, + minutes = time.getHours()*60 + time.getMinutes() - minMinute, + slotI = Math.floor(minutes / slotMinutes), + slotTop = slotTopCache[slotI]; + if (slotTop == undefined) { + slotTop = slotTopCache[slotI] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop; + } + return Math.max(0, Math.round( + slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) + )); + } + + + + + function day2col(dayOfWeek) { + return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit; + } + + +} + + +// count the number of colliding, higher-level segments (for event squishing) + +function countForwardSegs(levels) { + var i, j, k, level, segForward, segBack; + for (i=levels.length-1; i>0; i--) { + level = levels[i]; + for (j=0; j