This commit is contained in:
Adam Shaw 2009-09-21 04:57:20 +00:00
parent fc27aedcca
commit 363ad2c67f
23 changed files with 1255 additions and 1122 deletions

View file

@ -5,7 +5,8 @@ version 1.3 (9/15/09)
- themable by jQuery UI themes - themable by jQuery UI themes
- resizable events (require jQuery UI resizable plugin) - resizable events (require jQuery UI resizable plugin)
- rescoped & rewritten CSS, enhanced default look - rescoped & rewritten CSS, enhanced default look
- reworked options & API to support multiple views / be consistent with jQuery UI datepicker - cleaner css & rendering techniques for right-to-left
- reworked options & API to support multiple views / be consistent with jQuery UI
- refactoring of entire codebase - refactoring of entire codebase
- broken into different JS & CSS files, assembled w/ build scripts - broken into different JS & CSS files, assembled w/ build scripts
- new test suite for new features, uses firebug-lite - new test suite for new features, uses firebug-lite
@ -37,10 +38,12 @@ version 1.3 (9/15/09)
+ eventResize + eventResize
x monthDisplay -> viewDisplay x monthDisplay -> viewDisplay
x resize -> windowResize x resize -> windowResize
'eventDrop' params changed, can revert if ajax cuts out
- CalEvent Properties - CalEvent Properties
x showTime -> allDay x showTime -> allDay
x draggable -> editable x draggable -> editable
'end' is now INCLUSIVE when allDay=true 'end' is now INCLUSIVE when allDay=true
'url' now produces a real <a> tag, more native clicking/tab behavior
- Methods: - Methods:
+ renderEvent + renderEvent
x prevMonth -> prev x prevMonth -> prev
@ -54,6 +57,8 @@ version 1.3 (9/15/09)
'formatDates' added to support date-ranges 'formatDates' added to support date-ranges
- Google Calendar Options: - Google Calendar Options:
x draggable -> editable x draggable -> editable
- Bugfixes
- gcal extension fetched 25 results max, now fetches all
version 1.2.1 (6/29/09) version 1.2.1 (6/29/09)
- bugfixes - bugfixes

View file

@ -27,8 +27,8 @@ Locale Options
Text that will be displayed on buttons of the header. Default:: Text that will be displayed on buttons of the header. Default::
{ {
prev: '&#9668;', // left triangle prev: '&nbsp;&#9668;&nbsp;', // left triangle
next: '&#9658;', // right triangle next: '&nbsp;&#9658;&nbsp;', // right triangle
today: 'today', today: 'today',
month: 'month', month: 'month',
week: 'week', week: 'week',

View file

@ -19,12 +19,12 @@ jQuery object:
``month`` is 0-based, meaning January=0, February=1, etc. ``month`` is 0-based, meaning January=0, February=1, etc.
**moveDate** - .fullCalendar('moveDate', *years, [months, [days]]*) **incrementDate** - .fullCalendar('incrementDate', *years, [months, [days]]*)
Moves the calendar forward/backward an arbitrary amount of time. Moves the calendar forward/backward an arbitrary amount of time.
**updateEvent** - .fullCalendar('updateEvent', *calEvent*) **updateEvent** - .fullCalendar('updateEvent', *calEvent*)
Reports changes about a modified :ref:`CalEvent <CalEvent>`. This will cause the event Reports changes to a :ref:`CalEvent's <CalEvent>` standard properties.
to be rerendered on the calendar. This will cause the event to be rerendered on the calendar.
If there are repeating events on the calendar with the If there are repeating events on the calendar with the
same ID, these events will be changed as well. same ID, these events will be changed as well.

View file

@ -57,7 +57,7 @@ always available (:ref:`more below <view-object>`).
``this`` is set to the event's element ``this`` is set to the event's element
**eventDrop**: function(*calEvent, dayDelta, minuteDelta, jsEvent, ui, view*) **eventDrop**: function(*calEvent, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view*)
Triggered when dragging stops and the event has moved to a *different* day. Triggered when dragging stops and the event has moved to a *different* day.
``dayDelta`` holds the number of days the event was moved forward (a positive number) ``dayDelta`` holds the number of days the event was moved forward (a positive number)
@ -70,6 +70,9 @@ always available (:ref:`more below <view-object>`).
repeating events. If updating a remote database, just add these values to the repeating events. If updating a remote database, just add these values to the
start and end times of all events with the given ``calEvent.id`` start and end times of all events with the given ``calEvent.id``
``revertFunc`` is a function that, if called, reverts the event's start/end date to
the values *before* the drag. This is useful if an ajax call should fail.
**eventResizeStart**, **eventResizeStop**: function(*calEvent, jsEvent, ui, view*) **eventResizeStart**, **eventResizeStop**: function(*calEvent, jsEvent, ui, view*)
Triggered before/after an event is resized (but not necessarily changed). Triggered before/after an event is resized (but not necessarily changed).
``calEvent`` holds that event's information (date, title, etc). ``calEvent`` holds that event's information (date, title, etc).
@ -78,7 +81,7 @@ always available (:ref:`more below <view-object>`).
``this`` is set to the event's element ``this`` is set to the event's element
**eventResize**: function(*calEvent, dayDelta, minuteDelta, jsEvent, ui, view*) **eventResize**: function(*calEvent, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view*)
Triggered when an event is resized and *changed in duration*. Triggered when an event is resized and *changed in duration*.
``dayDelta`` holds the number of days the event's end time was moved ``dayDelta`` holds the number of days the event's end time was moved
@ -87,6 +90,9 @@ always available (:ref:`more below <view-object>`).
``minuteDelta`` will always be ``0`` and is reserved for a future release ``minuteDelta`` will always be ``0`` and is reserved for a future release
of FullCalendar where there will be an agenda view. of FullCalendar where there will be an agenda view.
``revertFunc`` is a function that, if called, reverts the event's start/end date to
the values *before* the drag. This is useful if an ajax call should fail.
.. _view-object: .. _view-object:

View file

@ -4,7 +4,6 @@
<!--<src>--> <!--<src>-->
<link rel='stylesheet' type='text/css' href='../src/css/main.css' /> <link rel='stylesheet' type='text/css' href='../src/css/main.css' />
<link rel='stylesheet' type='text/css' href='../src/css/grid.css' /> <link rel='stylesheet' type='text/css' href='../src/css/grid.css' />
<link rel='stylesheet' type='text/css' href='../src/css/agenda.css' />
<script type='text/javascript' src='../src/jquery/jquery.js'></script> <script type='text/javascript' src='../src/jquery/jquery.js'></script>
<script type='text/javascript' src='../src/jquery/ui.core.js'></script> <script type='text/javascript' src='../src/jquery/ui.core.js'></script>
<script type='text/javascript' src='../src/jquery/ui.draggable.js'></script> <script type='text/javascript' src='../src/jquery/ui.draggable.js'></script>

View file

@ -4,7 +4,6 @@
<!--<src>--> <!--<src>-->
<link rel='stylesheet' type='text/css' href='../src/css/main.css' /> <link rel='stylesheet' type='text/css' href='../src/css/main.css' />
<link rel='stylesheet' type='text/css' href='../src/css/grid.css' /> <link rel='stylesheet' type='text/css' href='../src/css/grid.css' />
<link rel='stylesheet' type='text/css' href='../src/css/agenda.css' />
<script type='text/javascript' src='../src/jquery/jquery.js'></script> <script type='text/javascript' src='../src/jquery/jquery.js'></script>
<script type='text/javascript' src='../src/main.js'></script> <script type='text/javascript' src='../src/main.js'></script>
<script type='text/javascript' src='../src/grid.js'></script> <script type='text/javascript' src='../src/grid.js'></script>

View file

@ -4,7 +4,6 @@
<!--<src>--> <!--<src>-->
<link rel='stylesheet' type='text/css' href='../src/css/main.css' /> <link rel='stylesheet' type='text/css' href='../src/css/main.css' />
<link rel='stylesheet' type='text/css' href='../src/css/grid.css' /> <link rel='stylesheet' type='text/css' href='../src/css/grid.css' />
<link rel='stylesheet' type='text/css' href='../src/css/agenda.css' />
<script type='text/javascript' src='../src/jquery/jquery.js'></script> <script type='text/javascript' src='../src/jquery/jquery.js'></script>
<script type='text/javascript' src='../src/jquery/ui.core.js'></script> <script type='text/javascript' src='../src/jquery/ui.core.js'></script>
<script type='text/javascript' src='../src/jquery/ui.draggable.js'></script> <script type='text/javascript' src='../src/jquery/ui.draggable.js'></script>

View file

@ -5,7 +5,6 @@
<!--<src>--> <!--<src>-->
<link rel='stylesheet' type='text/css' href='../src/css/main.css' /> <link rel='stylesheet' type='text/css' href='../src/css/main.css' />
<link rel='stylesheet' type='text/css' href='../src/css/grid.css' /> <link rel='stylesheet' type='text/css' href='../src/css/grid.css' />
<link rel='stylesheet' type='text/css' href='../src/css/agenda.css' />
<script type='text/javascript' src='../src/jquery/jquery.js'></script> <script type='text/javascript' src='../src/jquery/jquery.js'></script>
<script type='text/javascript' src='../src/jquery/ui.core.js'></script> <script type='text/javascript' src='../src/jquery/ui.core.js'></script>
<script type='text/javascript' src='../src/jquery/ui.draggable.js'></script> <script type='text/javascript' src='../src/jquery/ui.draggable.js'></script>
@ -36,6 +35,11 @@
$('#calendar').fullCalendar({ $('#calendar').fullCalendar({
theme: true, theme: true,
editable: true, editable: true,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,basicWeek,basicDay'
},
events: [ events: [
{ {
id: 1, id: 1,

View file

@ -4,7 +4,6 @@
<!--<src>--> <!--<src>-->
<link rel='stylesheet' type='text/css' href='../src/css/main.css' /> <link rel='stylesheet' type='text/css' href='../src/css/main.css' />
<link rel='stylesheet' type='text/css' href='../src/css/grid.css' /> <link rel='stylesheet' type='text/css' href='../src/css/grid.css' />
<link rel='stylesheet' type='text/css' href='../src/css/agenda.css' />
<script type='text/javascript' src='../src/jquery/jquery.js'></script> <script type='text/javascript' src='../src/jquery/jquery.js'></script>
<script type='text/javascript' src='../src/jquery/ui.core.js'></script> <script type='text/javascript' src='../src/jquery/ui.core.js'></script>
<script type='text/javascript' src='../src/jquery/ui.draggable.js'></script> <script type='text/javascript' src='../src/jquery/ui.draggable.js'></script>
@ -86,6 +85,7 @@
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif; font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
text-align: left;
} }
#calendar { #calendar {

View file

@ -1,4 +1,15 @@
.fc-event,
.fc-event a,
.fc-agenda .fc-event-time {
color: #fff;
border-style: solid;
border-color: blue;
background-color: blue;
}
/* header styles */ /* header styles */

View file

@ -15,8 +15,8 @@
border-width: 1px 0 0 1px; border-width: 1px 0 0 1px;
} }
.fc-grid th.fc-left, .fc-grid th.fc-leftmost,
.fc-grid td.fc-left { .fc-grid td.fc-leftmost {
border-left: 0; border-left: 0;
} }
@ -27,7 +27,10 @@
.fc-grid .fc-other-month .fc-day-number { .fc-grid .fc-other-month .fc-day-number {
opacity: 0.3; opacity: 0.3;
filter: alpha(opacity=30); 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 { .fc-grid .fc-day-content {
@ -54,3 +57,4 @@
.fc-rtl .fc-grid .fc-event-time { .fc-rtl .fc-grid .fc-event-time {
float: right; float: right;
} }

View file

@ -1,3 +1,12 @@
/*
* FullCalendar 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.
*
*/
.fc, .fc,
.fc .fc-header, .fc .fc-header,
@ -63,25 +72,60 @@ table.fc-header {
direction: rtl; direction: rtl;
} }
/* button rounded corners */
/* Buttons
------------------------------------------------------------------------*/
.fc-header .fc-state-default,
.fc-header .ui-state-default {
margin-bottom: 1em;
cursor: pointer;
}
.fc-header .fc-state-default { .fc-header .fc-state-default {
border-width: 1px 0; border-width: 1px 0;
padding: 0 1px; padding: 0 1px;
} }
.fc-header .fc-state-default,
.fc-header .fc-state-default a {
border-style: solid;
}
.fc-header .fc-state-default a { .fc-header .fc-state-default a {
display: block; display: block;
position: relative; position: relative;
margin: 0 -1px;
border-width: 0 1px; border-width: 0 1px;
margin: 0 -1px;
width: 100%; width: 100%;
text-decoration: none;
} }
.fc-header .fc-state-default span { .fc-header .fc-state-default span {
display: block; display: block;
border-style: solid;
border-width: 1px 0 1px 1px;
padding: 3px 5px;
} }
.fc-header .ui-state-default {
padding: 4px 6px;
}
/* for adjacent buttons */
.fc-header .fc-no-right {
padding-right: 0;
border-right: 0;
}
.fc-header .ui-no-right {
border-right: 0;
}
/* for fake rounded corners */
.fc-header .fc-corner-left { .fc-header .fc-corner-left {
margin-left: 1px; margin-left: 1px;
padding-left: 0; padding-left: 0;
@ -92,73 +136,45 @@ table.fc-header {
padding-right: 0; padding-right: 0;
} }
.fc-header .fc-no-left { /* DEFAULT button COLORS */
padding-left: 0;
}
.fc-header .ui-no-left {
border-left: 0;
}
/* default button state */
.fc-header .fc-state-default,
.fc-header .ui-state-default {
margin-bottom: 10px;
cursor: pointer;
}
.fc-header .fc-state-default, .fc-header .fc-state-default,
.fc-header .fc-state-default a { .fc-header .fc-state-default a {
border-style: solid; border-color: #777; /* outer border */
border-color: #6E6E6E;
color: #333; color: #333;
} }
.fc-header .fc-state-default span { .fc-header .fc-state-default span {
border-width: 1px 0 0 1px; border-color: #fff #fff #cecece; /* inner border */
border-style: solid; background: #e8e8e8;
border-color: #fff;
background: #F0F0F0;
} }
.fc-header .fc-state-default span, /* PRESSED button COLORS (down and active) */
.fc-header .ui-state-default {
padding: 4px 6px;
}
/* active button state */
.fc-header .fc-state-active a { .fc-header .fc-state-active a {
color: #fff; color: #fff;
} }
.fc-header .fc-state-down span,
.fc-header .fc-state-active span { .fc-header .fc-state-active span {
background: #787878; background: #888;
border-color: #777; border-color: #808080 #808080 #909090; /* inner border */
} }
/* down button state */ /* DISABLED button COLORS */
.fc-header .fc-state-down span {
background: #787878;
border-color: #777;
}
/* disabled button state */
.fc-header .fc-state-disabled,
.fc-header .fc-state-disabled a {
border-color: #ccc;
}
.fc-header .fc-state-disabled a { .fc-header .fc-state-disabled a {
color: #999; color: #999;
} }
.fc-header .fc-state-disabled,
.fc-header .fc-state-disabled a {
border-color: #ccc; /* outer border */
}
.fc-header .fc-state-disabled span { .fc-header .fc-state-disabled span {
border-color: #fff; border-color: #fff #fff #f0f0f0; /* inner border */
background: #F0F0F0; background: #f0f0f0;
} }
@ -180,15 +196,15 @@ table.fc-header {
} }
.fc-content .fc-state-highlight { /* today */ .fc-content .fc-state-highlight { /* today */
background: #FFFFCC; background: #ffc;
} }
.fc-content td.fc-not-today { .fc-content .fc-not-today {
background: none; background: none;
} }
.fc-cell-overlay { /* semi-transparent rectangle while dragging */ .fc-cell-overlay { /* semi-transparent rectangle while dragging */
background: #ADDBFF; background: #9cf;
opacity: .2; opacity: .2;
filter: alpha(opacity=20); /* for IE */ filter: alpha(opacity=20); /* for IE */
} }
@ -204,40 +220,63 @@ table.fc-header {
------------------------------------------------------------------------*/ ------------------------------------------------------------------------*/
.fc-event, .fc-event,
.fc-event a, .fc-event a {
.fc-agenda .fc-event-time {
color: #fff; color: #fff;
border-style: solid; border-style: solid;
border-color: blue; border-color: #36c; /* DEFAULT EVENT COLOR */
background-color: blue; background-color: #36c; /* */
} }
/* Use the 'className' CalEvent property and the following
* example CSS to change event color on a per-event basis:
*
* .my-event-class,
* .my-event-class a {
* border-color: red;
* background-color: red;
* }
*/
.fc-event a { .fc-event a {
overflow: hidden; overflow: hidden;
font-size: 11px; font-size: .85em;
text-decoration: none; text-decoration: none;
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
} }
.fc-event-editable {
cursor: pointer;
}
.fc-event-time, .fc-event-time,
.fc-event-title { .fc-event-title {
padding: 0 1px; padding: 0 1px;
} }
.fc-event a { /* prep for rounded corners */ /* for fake rounded corners */
.fc-event a {
display: block; display: block;
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
/* right-to-left */
.fc-rtl .fc-event a {
text-align: right;
}
/* resizable */ /* resizable */
.fc .ui-resizable-handle { .fc .ui-resizable-handle {
display: block; display: block;
position: absolute; position: absolute;
z-index: 99999; 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 */
} }
@ -254,6 +293,8 @@ table.fc-header {
border-width: 0; border-width: 0;
} }
/* for fake rounded corners */
.fc-content .fc-corner-left { .fc-content .fc-corner-left {
margin-left: 1px; margin-left: 1px;
} }
@ -279,22 +320,19 @@ table.fc-header {
} }
.fc-event-hori .ui-resizable-e { .fc-event-hori .ui-resizable-e {
top: 0 !important; top: 0 !important; /* importants override pre jquery ui 1.7 styles */
right: -5px !important; right: -3px !important;
width: 7px !important; width: 7px !important;
height: 100% !important; height: 100% !important;
border: 0 !important;
background: none !important;
cursor: e-resize; cursor: e-resize;
} }
.fc-event-hori .ui-resizable-w { .fc-event-hori .ui-resizable-w {
top: 0 !important; top: 0 !important;
left: -5px !important; left: -3px !important;
width: 7px !important; width: 7px !important;
height: 100% !important; height: 100% !important;
border: 0 !important;
background: none !important;
cursor: w-resize; cursor: w-resize;
} }

View file

@ -1,3 +1,13 @@
/*
* FullCalendar Google Calendar Extension
*
* 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
*
*/
(function($) { (function($) {
$.fullCalendar.gcalFeed = function(feedUrl, options) { $.fullCalendar.gcalFeed = function(feedUrl, options) {
@ -11,48 +21,40 @@
'start-min': $.fullCalendar.formatDate(start, 'u'), 'start-min': $.fullCalendar.formatDate(start, 'u'),
'start-max': $.fullCalendar.formatDate(end, 'u'), 'start-max': $.fullCalendar.formatDate(end, 'u'),
'singleevents': true, 'singleevents': true,
'max-results': 1000 'max-results': 9999
}, },
function(data) { function(data) {
var events = []; var events = [];
if (data.feed.entry) if (data.feed.entry) {
$.each(data.feed.entry, function(i, entry) { $.each(data.feed.entry, function(i, entry) {
var url; var startStr = entry['gd$when'][0]['startTime'],
$.each(entry['link'], function(j, link) { start = $.fullCalendar.parseDate(startStr),
if (link.type == 'text/html') { end = $.fullCalendar.parseDate(entry['gd$when'][0]['endTime']),
url = link.href; allDay = startStr.indexOf('T') == -1,
classNames = [],
url;
$.each(entry.link, function() {
if (this.type == 'text/html') {
url = this.href;
} }
}); });
var startStr = entry['gd$when'][0]['startTime'];
var start = $.fullCalendar.parseDate(startStr);
var end = $.fullCalendar.parseDate(entry['gd$when'][0]['endTime']);
var allDay = startStr.indexOf('T') == -1;
var classNames = [];
if (allDay) { if (allDay) {
end = new Date(end - 1); // make in inclusive end = new Date(end - 1); // make inclusive
}else{
classNames.push('fc-event-nobg');
}
if (options.className) {
if (typeof options.className == 'string') {
classNames.push(options.className);
}else{
classNames = classNames.concat(options.className);
}
} }
events.push({ events.push({
id: entry['gCal$uid']['value'], id: entry['gCal$uid']['value'],
url: url,
title: entry['title']['$t'], title: entry['title']['$t'],
url: url,
start: $.fullCalendar.parseDate(entry['gd$when'][0]['startTime']), start: $.fullCalendar.parseDate(entry['gd$when'][0]['startTime']),
end: end, end: end,
allDay: allDay,
location: entry['gd$where'][0]['valueString'], location: entry['gd$where'][0]['valueString'],
description: entry['content']['$t'], description: entry['content']['$t'],
allDay: allDay, className: options.className,
className: classNames,
editable: options.editable || false editable: options.editable || false
}); });
}); });
}
callback(events); callback(events);
}); });
} }

View file

@ -1,4 +1,7 @@
/* Grid-based Views: month, basicWeek, basicDay
-----------------------------------------------------------------------------*/
setDefaults({ setDefaults({
weekMode: 'fixed' weekMode: 'fixed'
}); });
@ -17,8 +20,8 @@ views.month = function(element, options) {
strProp(options.titleFormat, 'month'), strProp(options.titleFormat, 'month'),
options options
); );
addDays(this.visStart = cloneDate(start), -((start.getDay() - options.weekStart + 7) % 7)); addDays(this.visStart = cloneDate(start), -((start.getDay() - options.firstDay + 7) % 7));
addDays(this.visEnd = cloneDate(this.end), (7 - this.visEnd.getDay() + options.weekStart) % 7); addDays(this.visEnd = cloneDate(this.end), (7 - this.visEnd.getDay() + options.firstDay) % 7);
var rowCnt = Math.round((this.visEnd - this.visStart) / (DAY_MS * 7)); var rowCnt = Math.round((this.visEnd - this.visStart) / (DAY_MS * 7));
if (options.weekMode == 'fixed') { if (options.weekMode == 'fixed') {
addDays(this.visEnd, (6 - rowCnt) * 7); addDays(this.visEnd, (6 - rowCnt) * 7);
@ -36,7 +39,7 @@ views.basicWeek = function(element, options) {
addDays(date, delta * 7); addDays(date, delta * 7);
} }
this.title = formatDates( this.title = formatDates(
this.start = this.visStart = addDays(cloneDate(date), -((date.getDay() - options.weekStart + 7) % 7)), this.start = this.visStart = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)),
addDays(cloneDate(this.end = this.visEnd = addDays(cloneDate(this.start), 7)), -1), addDays(cloneDate(this.end = this.visEnd = addDays(cloneDate(this.start), 7)), -1),
strProp(options.titleFormat, 'week'), strProp(options.titleFormat, 'week'),
options options
@ -61,18 +64,16 @@ views.basicDay = function(element, options) {
} }
// flags for [Opera] rendering bugs // rendering bugs
var tdTopBug, trTopBug, tbodyTopBug, sniffBugs = true; var tdTopBug, trTopBug, tbodyTopBug,
tdHeightBug,
var tdHeightBug; rtlLeftDiff;
var sniffedEventLeftBug, eventLeftDiff=0;
function Grid(element, options, methods) { function Grid(element, options, methods) {
var tm, weekStart, var tm, firstDay,
rtl, dis, dit, // day index sign / translate rtl, dis, dit, // day index sign / translate
rowCnt, colCnt, rowCnt, colCnt,
colWidth, colWidth,
@ -85,10 +86,10 @@ function Grid(element, options, methods) {
renderEvents: renderEvents, renderEvents: renderEvents,
rerenderEvents: rerenderEvents, rerenderEvents: rerenderEvents,
updateSize: updateSize, updateSize: updateSize,
defaultEventEnd: function(event) { defaultEventEnd: function(event) { // calculates an end if event doesnt have one, mostly for resizing
return cloneDate(event.start); return cloneDate(event.start);
}, },
visEventEnd: function(event) { visEventEnd: function(event) { // returns exclusive 'visible' end, for rendering
if (event.end) { if (event.end) {
var end = cloneDate(event.end); var end = cloneDate(event.end);
return (event.allDay || end.getHours() || end.getMinutes()) ? addDays(end, 1) : end; return (event.allDay || end.getHours() || end.getMinutes()) ? addDays(end, 1) : end;
@ -120,7 +121,7 @@ function Grid(element, options, methods) {
// update option-derived variables // update option-derived variables
tm = options.theme ? 'ui' : 'fc'; tm = options.theme ? 'ui' : 'fc';
weekStart = options.weekStart; firstDay = options.firstDay;
if (rtl = options.isRTL) { if (rtl = options.isRTL) {
dis = -1; dis = -1;
dit = colCnt - 1; dit = colCnt - 1;
@ -138,7 +139,7 @@ function Grid(element, options, methods) {
s += "<th class='fc-" + s += "<th class='fc-" +
dayIDs[d.getDay()] + ' ' + // needs to be first dayIDs[d.getDay()] + ' ' + // needs to be first
tm + '-state-default' + tm + '-state-default' +
(i==dit ? ' fc-left' : '') + (i==dit ? ' fc-leftmost' : '') +
"'>" + formatDate(d, colFormat, options) + "</th>"; "'>" + formatDate(d, colFormat, options) + "</th>";
addDays(d, 1); addDays(d, 1);
} }
@ -152,7 +153,7 @@ function Grid(element, options, methods) {
s += "<td class='fc-" + s += "<td class='fc-" +
dayIDs[d.getDay()] + ' ' + // needs to be first dayIDs[d.getDay()] + ' ' + // needs to be first
tm + '-state-default fc-day' + (i*colCnt+j) + tm + '-state-default fc-day' + (i*colCnt+j) +
(j==dit ? ' fc-left' : '') + (j==dit ? ' fc-leftmost' : '') +
(rowCnt>1 && d.getMonth() != month ? ' fc-other-month' : '') + (rowCnt>1 && d.getMonth() != month ? ' fc-other-month' : '') +
(+d == +today ? (+d == +today ?
' fc-today '+tm+'-state-highlight' : ' fc-today '+tm+'-state-highlight' :
@ -182,7 +183,7 @@ function Grid(element, options, methods) {
s += "<td class='fc-" + s += "<td class='fc-" +
dayIDs[d.getDay()] + ' ' + // needs to be first dayIDs[d.getDay()] + ' ' + // needs to be first
tm + '-state-default fc-new fc-day' + (i*colCnt+j) + tm + '-state-default fc-new fc-day' + (i*colCnt+j) +
(j==dit ? ' fc-left' : '') + "'>" + (j==dit ? ' fc-leftmost' : '') + "'>" +
(showNumbers ? "<div class='fc-day-number'></div>" : '') + (showNumbers ? "<div class='fc-day-number'></div>" : '') +
"<div class='fc-day-content'><div>&nbsp;</div></div>" + "<div class='fc-day-content'><div>&nbsp;</div></div>" +
"</td>"; "</td>";
@ -218,7 +219,7 @@ function Grid(element, options, methods) {
addDays(d, 1); addDays(d, 1);
}); });
if (rowCnt == 1) { // more likely changed (week or day view) if (rowCnt == 1) { // more changes likely (week or day view)
// redo column header text and class // redo column header text and class
d = cloneDate(view.visStart); d = cloneDate(view.visStart);
@ -245,16 +246,25 @@ function Grid(element, options, methods) {
}; };
function dayClick() {
var date = addDays(
cloneDate(view.visStart),
parseInt(this.className.match(/fc\-day(\d+)/)[1])
);
view.trigger('dayClick', this, date);
}
function updateSize() { function updateSize() {
var width = element.width(); var width = element.width(),
var height = Math.round(width / options.aspectRatio); height = Math.round(width / options.aspectRatio),
leftTDs = tbody.find('tr td:first-child'),
tbodyHeight = height - thead.height(),
rowHeight1, rowHeight2;
setOuterWidth( setOuterWidth(
thead.find('th').slice(0, -1), thead.find('th').slice(0, -1),
colWidth = Math.floor(width / colCnt) colWidth = Math.floor(width / colCnt)
); );
var leftTDs = tbody.find('tr td:first-child');
var tbodyHeight = height - thead.height();
var rowHeight1, rowHeight2;
if (options.weekMode == 'variable') { if (options.weekMode == 'variable') {
rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6)); rowHeight1 = rowHeight2 = Math.floor(tbodyHeight / (rowCnt==1 ? 2 : 6));
}else{ }else{
@ -262,18 +272,19 @@ function Grid(element, options, methods) {
rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1); rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1);
} }
if (sniffBugs) { if (tdTopBug == undefined) {
// nasty bugs in opera 9.25 // nasty bugs in opera 9.25
// position() returning relative to direct parent // position() returning relative to direct parent
var tr = tbody.find('tr:first'); var tr = tbody.find('tr:first'),
var td = tr.find('td:first'); td = tr.find('td:first'),
var trTop = tr.position().top; trTop = tr.position().top,
var tdTop = td.position().top; tdTop = td.position().top;
tdTopBug = tdTop < 0; tdTopBug = tdTop < 0;
trTopBug = trTop != tdTop; trTopBug = trTop != tdTop;
tbodyTopBug = tbody.position().top != trTop; tbodyTopBug = tbody.position().top != trTop;
sniffBugs = false; }
// if (tdHeightBug == undefined) {
// bug in firefox where cell height includes padding
td.height(rowHeight1); td.height(rowHeight1);
tdHeightBug = rowHeight1 != td.height(); tdHeightBug = rowHeight1 != td.height();
} }
@ -285,8 +296,6 @@ function Grid(element, options, methods) {
setOuterHeight(leftTDs.slice(0, -1), rowHeight1); setOuterHeight(leftTDs.slice(0, -1), rowHeight1);
setOuterHeight(leftTDs.slice(-1), rowHeight2); setOuterHeight(leftTDs.slice(-1), rowHeight2);
} }
//alert(tbodyHeight + ' === ' + tbody.height());
} }
@ -302,7 +311,6 @@ function Grid(element, options, methods) {
function rerenderEvents(skipCompile) { function rerenderEvents(skipCompile) {
//console.log('rerender events');
view.clearEvents(); view.clearEvents();
if (skipCompile) { if (skipCompile) {
renderSegs(cachedSegs); renderSegs(cachedSegs);
@ -336,7 +344,7 @@ function Grid(element, options, methods) {
k, seg, k, seg,
event, event,
eventClasses, eventClasses,
startE, endE, startElm, endElm,
left1, left2, left1, left2,
eventElement, eventAnchor, eventElement, eventAnchor,
triggerRes; triggerRes;
@ -346,9 +354,15 @@ function Grid(element, options, methods) {
td = tr.find('td:first'); td = tr.find('td:first');
innerDiv = td.find('div.fc-day-content div').css('position', 'relative'); innerDiv = td.find('div.fc-day-content div').css('position', 'relative');
top = innerDiv.position().top; top = innerDiv.position().top;
if (tdTopBug) top -= td.position().top; if (tdTopBug) {
if (trTopBug) top += tr.position().top; top -= td.position().top;
if (tbodyTopBug) top += tbody.position().top; }
if (trTopBug) {
top += tr.position().top;
}
if (tbodyTopBug) {
top += tbody.position().top;
}
weekHeight = 0; weekHeight = 0;
for (j=0; j<levels.length; j++) { for (j=0; j<levels.length; j++) {
segs = levels[j]; segs = levels[j];
@ -361,15 +375,15 @@ function Grid(element, options, methods) {
eventClasses = eventClasses.split(' '); eventClasses = eventClasses.split(' ');
} }
eventClasses.push('fc-event', 'fc-event-hori'); eventClasses.push('fc-event', 'fc-event-hori');
startE = seg.isStart ? startElm = seg.isStart ?
tr.find('td:eq('+((seg.start.getDay()-weekStart+colCnt)%colCnt)+') div.fc-day-content div') : tr.find('td:eq('+((seg.start.getDay()-firstDay+colCnt)%colCnt)+') div.fc-day-content div') :
tbody; tbody;
endE = seg.isEnd ? endElm = seg.isEnd ?
tr.find('td:eq('+((seg.end.getDay()-weekStart+colCnt-1)%colCnt)+') div.fc-day-content div') : tr.find('td:eq('+((seg.end.getDay()-firstDay+colCnt-1)%colCnt)+') div.fc-day-content div') :
tbody; tbody;
if (rtl) { if (rtl) {
left1 = endE.position().left; left1 = endElm.position().left;
left2 = startE.position().left + startE.width(); left2 = startElm.position().left + startElm.width();
if (seg.isStart) { if (seg.isStart) {
eventClasses.push('fc-corner-right'); eventClasses.push('fc-corner-right');
} }
@ -377,8 +391,8 @@ function Grid(element, options, methods) {
eventClasses.push('fc-corner-left'); eventClasses.push('fc-corner-left');
} }
}else{ }else{
left1 = startE.position().left; left1 = startElm.position().left;
left2 = endE.position().left + endE.width(); left2 = endElm.position().left + endElm.width();
if (seg.isStart) { if (seg.isStart) {
eventClasses.push('fc-corner-left'); eventClasses.push('fc-corner-left');
} }
@ -388,7 +402,7 @@ function Grid(element, options, methods) {
} }
eventElement = $("<div class='" + eventClasses.join(' ') + "'/>") eventElement = $("<div class='" + eventClasses.join(' ') + "'/>")
.append(eventAnchor = $("<a/>") .append(eventAnchor = $("<a/>")
.append(event.allDay ? null : .append(event.allDay || !seg.isStart ? null :
$("<span class='fc-event-time'/>") $("<span class='fc-event-time'/>")
.html(formatDates(event.start, event.end, options.timeFormat, options))) .html(formatDates(event.start, event.end, options.timeFormat, options)))
.append($("<span class='fc-event-title'/>") .append($("<span class='fc-event-title'/>")
@ -405,22 +419,20 @@ function Grid(element, options, methods) {
.css({ .css({
position: 'absolute', position: 'absolute',
top: top, top: top,
left: left1 + eventLeftDiff, left: left1 + (rtlLeftDiff||0),
zIndex: 3 zIndex: 2
}) })
.appendTo(element); .appendTo(element);
setOuterWidth(eventElement, left2-left1, true); setOuterWidth(eventElement, left2-left1, true);
if (!sniffedEventLeftBug) { if (rtl && rtlLeftDiff == undefined) {
if (rtl) { // bug in IE6 where offsets are miscalculated with direction:rtl
eventLeftDiff = left1 - eventElement.position().left; rtlLeftDiff = left1 - eventElement.position().left;
if (eventLeftDiff) { if (rtlLeftDiff) {
eventElement.css('left', left1 + eventLeftDiff); eventElement.css('left', left1 + rtlLeftDiff);
}
} }
sniffedEventLeftBug = true;
} }
eventElementHandlers(event, eventElement); eventElementHandlers(event, eventElement);
if (event.editable || typeof event.editable == 'undefined' && options.editable) { if (event.editable || event.editable == undefined && options.editable) {
draggableEvent(event, eventElement); draggableEvent(event, eventElement);
resizableEvent(event, eventElement); resizableEvent(event, eventElement);
} }
@ -447,7 +459,7 @@ function Grid(element, options, methods) {
view.trigger('eventMouseover', this, event, ev); view.trigger('eventMouseover', this, event, ev);
}, },
function(ev) { function(ev) {
view.trigger('eventMouseover', this, event, ev); view.trigger('eventMouseout', this, event, ev);
} }
); );
} }
@ -462,8 +474,8 @@ function Grid(element, options, methods) {
if (!options.disableDragging && eventElement.draggable) { if (!options.disableDragging && eventElement.draggable) {
var matrix; var matrix;
eventElement.draggable({ eventElement.draggable({
zIndex: 4, zIndex: 3,
delay: 50, delay: 100,
opacity: options.dragOpacity, opacity: options.dragOpacity,
revertDuration: options.dragRevertDuration, revertDuration: options.dragRevertDuration,
start: function(ev, ui) { start: function(ev, ui) {
@ -476,7 +488,7 @@ function Grid(element, options, methods) {
} }
}); });
tbody.find('tr').each(function() { tbody.find('tr').each(function() {
matrix.row(this); matrix.row(this, tbodyTopBug);
}); });
var tds = tbody.find('tr:first td'); var tds = tbody.find('tr:first td');
if (rtl) { if (rtl) {
@ -485,9 +497,9 @@ function Grid(element, options, methods) {
tds.each(function() { tds.each(function() {
matrix.col(this); matrix.col(this);
}); });
matrix.start();
view.hideEvents(event, eventElement); view.hideEvents(event, eventElement);
view.trigger('eventDragStart', eventElement, event, ev, ui); view.trigger('eventDragStart', eventElement, event, ev, ui);
matrix.mouse(ev.pageX, ev.pageY);
}, },
drag: function(ev) { drag: function(ev) {
matrix.mouse(ev.pageX, ev.pageY); matrix.mouse(ev.pageX, ev.pageY);
@ -501,7 +513,11 @@ function Grid(element, options, methods) {
}else{ }else{
var dayDelta = cell.rowDelta*7 + cell.colDelta*dis; var dayDelta = cell.rowDelta*7 + cell.colDelta*dis;
view.moveEvent(event, dayDelta); view.moveEvent(event, dayDelta);
view.trigger('eventDrop', this, event, dayDelta, 0, ev, ui); view.trigger('eventDrop', this, event, dayDelta, 0, function() {
view.moveEvent(event, -dayDelta);
rerenderEvents();
}, ev, ui);
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
rerenderEvents(); rerenderEvents();
} }
} }
@ -522,36 +538,29 @@ function Grid(element, options, methods) {
grid: [colWidth, 0], grid: [colWidth, 0],
containment: element, containment: element,
start: function(ev, ui) { start: function(ev, ui) {
eventElement.css('z-index', 4); eventElement.css('z-index', 3);
view.hideEvents(event, eventElement); view.hideEvents(event, eventElement);
view.trigger('eventResizeStart', this, event, ev, ui); view.trigger('eventResizeStart', this, event, ev, ui);
}, },
stop: function(ev, ui) { stop: function(ev, ui) {
view.trigger('eventResizeStop', this, event, ev, ui); view.trigger('eventResizeStop', this, event, ev, ui);
var dayDelta = Math.round((Math.max(colWidth, ui.size.width) - ui.originalSize.width) / colWidth); // ui.size.width wasn't working with grid correctly, use .width()
var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth);
if (dayDelta) { if (dayDelta) {
view.resizeEvent(event, dayDelta); view.resizeEvent(event, dayDelta);
view.trigger('eventResize', this, event, dayDelta, 0, ev, ui); view.trigger('eventResize', this, event, dayDelta, 0, function() {
view.resizeEvent(event, -dayDelta);
rerenderEvents();
}, ev, ui);
rerenderEvents(); rerenderEvents();
}else{ }else{
view.showEvents(event, eventElement); view.showEvents(event, eventElement);
} }
eventElement.css('z-index', 3); eventElement.css('z-index', 2);
} }
}); });
} }
} }
//
function dayClick() {
var dayIndex = parseInt(this.className.match(/fc\-day(\d+)/)[1]);
var date = addDays(cloneDate(view.visStart), dayIndex);
view.trigger('dayClick', this, date);
}
}; };

View file

@ -17,6 +17,11 @@ var defaults = {
right: 'today prev,next' right: 'today prev,next'
}, },
// editing
//editable: false,
//disableDragging: false,
//disableResizing: false,
// event ajax // event ajax
startParam: 'start', startParam: 'start',
endParam: 'end', endParam: 'end',
@ -35,16 +40,16 @@ var defaults = {
day: 'dddd M/d' day: 'dddd M/d'
}, },
// regional // locale
isRTL: false, isRTL: false,
weekStart: 0, firstDay: 0,
monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'], 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'], monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'], dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
buttonText: { buttonText: {
prev: '&#9668;', prev: '&nbsp;&#9668;&nbsp;',
next: '&#9658;', next: '&nbsp;&#9658;&nbsp;',
today: 'today', today: 'today',
month: 'month', month: 'month',
week: 'week', week: 'week',
@ -57,6 +62,7 @@ var defaults = {
prev: 'circle-triangle-w', prev: 'circle-triangle-w',
next: 'circle-triangle-e' next: 'circle-triangle-e'
} }
}; };
// right-to-left defaults // right-to-left defaults
@ -67,8 +73,8 @@ var rtlDefaults = {
right: 'title' right: 'title'
}, },
buttonText: { buttonText: {
prev: '&#9658;', prev: '&nbsp;&#9658;&nbsp;',
next: '&#9668;' next: '&nbsp;&#9668;&nbsp;'
} }
}; };
@ -86,12 +92,15 @@ $.fn.fullCalendar = function(options) {
// method calling // method calling
if (typeof options == 'string') { if (typeof options == 'string') {
var args = Array.prototype.slice.call(arguments, 1), res; var args = Array.prototype.slice.call(arguments, 1),
res;
this.each(function() { this.each(function() {
var r = $.data(this, 'fullCalendar')[options].apply(this, args); var r = $.data(this, 'fullCalendar')[options].apply(this, args);
if (typeof res == 'undefined') res = r; if (res == undefined) {
res = r;
}
}); });
if (typeof res != 'undefined') { if (res != undefined) {
return res; return res;
} }
return this; return this;
@ -111,10 +120,10 @@ $.fn.fullCalendar = function(options) {
// initialize options // initialize options
options = $.extend(true, {}, options = $.extend(true, {},
defaults, defaults,
(options.isRTL || typeof options.isRTL == 'undefined' && defaults.isRTL) ? rtlDefaults : {}, (options.isRTL || options.isRTL==undefined && defaults.isRTL) ? rtlDefaults : {},
options options
); );
var tm = options.theme ? 'ui' : 'fc'; var tm = options.theme ? 'ui' : 'fc'; // for making theme classes
this.each(function() { this.each(function() {
@ -139,13 +148,13 @@ $.fn.fullCalendar = function(options) {
viewName, view, // the current view viewName, view, // the current view
prevView, prevView,
viewInstances = {}; viewInstances = {};
if (options.year) { if (options.year != undefined) {
date.setYear(options.year); date.setYear(options.year);
} }
if (options.month) { if (options.month != undefined) {
date.setMonth(options.month); date.setMonth(options.month);
} }
if (options.date) { if (options.date != undefined) {
date.setDate(options.date); date.setDate(options.date);
} }
@ -164,35 +173,40 @@ $.fn.fullCalendar = function(options) {
$("<div class='fc-view fc-view-" + v + "'/>").appendTo(content), $("<div class='fc-view fc-view-" + v + "'/>").appendTo(content),
options); options);
} }
if (prevView) { if (prevView && prevView.eventsChanged) {
prevView.element.hide(); // if previous view's events have been changed, mark future views' events as dirty
if (prevView.eventsChanged) { eventsDirtyExcept(prevView);
eventsDirtyExcept(prevView); prevView.eventsChanged = false;
prevView.eventsChanged = false;
}
} }
if (header) { if (header) {
// update 'active' view button
header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active'); header.find('div.fc-button-' + viewName).removeClass(tm + '-state-active');
header.find('div.fc-button-' + v).addClass(tm + '-state-active'); header.find('div.fc-button-' + v).addClass(tm + '-state-active');
} }
view.name = viewName = v; view.name = viewName = v;
render(); render();
if (prevView) {
// hide the old element AFTER the new has been rendered, preserves scrollbars
prevView.element.hide();
}
} }
} }
function render(inc) { function render(inc) {
if (inc || !view.date || +view.date != +date) { if (inc || !view.date || +view.date != +date) { // !view.date means it hasn't been rendered yet
ignoreResizes = true; ignoreWindowResizes = true;
view.render(date, inc || 0, function(callback) { view.render(date, inc || 0, function(callback) {
// dont refetch if new view contains the same events (or a subset)
if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) { if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) {
fetchEvents(callback); fetchEvents(callback);
}else{ }else{
callback(events); callback(events); // no refetching
} }
}); });
ignoreResizes = false; ignoreWindowResizes = false;
view.date = cloneDate(date); view.date = cloneDate(date);
if (header) { if (header) {
// enable/disable 'today' button
var today = new Date(); var today = new Date();
if (today >= view.start && today < view.end) { if (today >= view.start && today < view.end) {
header.find('div.fc-button-today').addClass(tm + '-state-disabled'); header.find('div.fc-button-today').addClass(tm + '-state-disabled');
@ -202,15 +216,18 @@ $.fn.fullCalendar = function(options) {
} }
} }
else if (view.eventsDirty) { else if (view.eventsDirty) {
// ensure events are rerendered if another view messed with them
view.rerenderEvents(); view.rerenderEvents();
} }
if (header) { if (header) {
// update title text
header.find('h2.fc-header-title').html(view.title); header.find('h2.fc-header-title').html(view.title);
} }
view.eventsDirty = false; view.eventsDirty = false;
view.trigger('viewDisplay', _element); view.trigger('viewDisplay', _element);
} }
// marks other views' events as dirty
function eventsDirtyExcept(exceptView) { function eventsDirtyExcept(exceptView) {
$.each(viewInstances, function() { $.each(viewInstances, function() {
if (this != exceptView) { if (this != exceptView) {
@ -219,6 +236,7 @@ $.fn.fullCalendar = function(options) {
}); });
} }
// called when any event objects have been added/removed/changed, rerenders
function eventsChanged() { function eventsChanged() {
view.clearEvents(); view.clearEvents();
view.renderEvents(events); view.renderEvents(events);
@ -253,20 +271,21 @@ $.fn.fullCalendar = function(options) {
// Fetch from a particular source. Append to the 'events' array // Fetch from a particular source. Append to the 'events' array
function fetchEventSource(src, callback) { function fetchEventSource(src, callback) {
var prevDate = cloneDate(date), var prevViewName = view.name,
prevDate = cloneDate(date),
reportEvents = function(a) { reportEvents = function(a) {
if (+date == +prevDate) { if (prevViewName == view.name && +prevDate == +date) { // protects from fast switching
for (var i=0; i<a.length; i++) { for (var i=0; i<a.length; i++) {
normalizeEvent(a[i]); normalizeEvent(a[i]);
a[i].source = src; a[i].source = src;
} }
events = events.concat(a); events = events.concat(a);
} if (callback) {
if (callback) { callback(a);
callback(a); }
} }
}, },
reportPopEvents = function(a) { reportEventsAndPop = function(a) {
reportEvents(a); reportEvents(a);
popLoading(); popLoading();
}; };
@ -276,11 +295,11 @@ $.fn.fullCalendar = function(options) {
params[options.endParam] = Math.round(eventEnd.getTime() / 1000); params[options.endParam] = Math.round(eventEnd.getTime() / 1000);
params[options.cacheParam] = (new Date()).getTime(); params[options.cacheParam] = (new Date()).getTime();
pushLoading(); pushLoading();
$.getJSON(src, params, reportPopEvents); $.getJSON(src, params, reportEventsAndPop);
} }
else if ($.isFunction(src)) { else if ($.isFunction(src)) {
pushLoading(); pushLoading();
src(cloneDate(eventStart), cloneDate(eventEnd), reportPopEvents); src(cloneDate(eventStart), cloneDate(eventEnd), reportEventsAndPop);
} }
else { else {
reportEvents(src); // src is an array reportEvents(src); // src is an array
@ -331,39 +350,41 @@ $.fn.fullCalendar = function(options) {
}, },
gotoDate: function(year, month, dateNum) { gotoDate: function(year, month, dateNum) {
if (typeof year != 'undefined') { if (year != undefined) {
date.setYear(year); date.setYear(year);
} }
if (typeof month != 'undefined') { if (month != undefined) {
date.setMonth(month); date.setMonth(month);
} }
if (typeof dateNum != 'undefined') { if (dateNum != undefined) {
date.setDate(dateNum); date.setDate(dateNum);
} }
render(); render();
}, },
moveDate: function(years, months, days) { incrementDate: function(years, months, days) {
if (typeof years != 'undefined') { if (years != undefined) {
addYears(date, years); addYears(date, years);
} }
if (typeof months != 'undefined') { if (months != undefined) {
addMonths(date, months); addMonths(date, months);
} }
if (typeof days != 'undefined') { if (days != undefined) {
addDays(date, days); addDays(date, days);
} }
render(); render();
}, },
// //
// Event Rendering // Event Manipulation
// //
updateEvent: function(event) { updateEvent: function(event) { // update an existing event
var startDelta = event.start - event._start, var i, len = events.length, e,
endDelta = event.end ? (event.end - (event._end || view.defaultEventEnd(event))) : 0, startDelta = event.start - event._start,
i, len = events.length, e; endDelta = event.end ?
(event.end - (event._end || view.defaultEventEnd(event))) // event._end would be null if event.end
: 0; // was null and event was just resized
for (i=0; i<len; i++) { for (i=0; i<len; i++) {
e = events[i]; e = events[i];
if (e._id == event._id && e != event) { if (e._id == event._id && e != event) {
@ -379,6 +400,8 @@ $.fn.fullCalendar = function(options) {
} }
e.title = event.title; e.title = event.title;
e.allDay = event.allDay; e.allDay = event.allDay;
e.className = event.className;
e.editable = event.editable;
normalizeEvent(e); normalizeEvent(e);
} }
} }
@ -386,7 +409,7 @@ $.fn.fullCalendar = function(options) {
eventsChanged(); eventsChanged();
}, },
renderEvent: function(event, stick) { renderEvent: function(event, stick) { // render a new event
normalizeEvent(event); normalizeEvent(event);
if (!event.source) { if (!event.source) {
if (stick) { if (stick) {
@ -434,9 +457,7 @@ $.fn.fullCalendar = function(options) {
return e._id == filter; return e._id == filter;
}); });
} }
else { return events; // else, return all
return events;
}
}, },
rerenderEvents: function() { rerenderEvents: function() {
@ -447,9 +468,9 @@ $.fn.fullCalendar = function(options) {
// Event Source // Event Source
// //
addEventSource: function(src) { addEventSource: function(source) {
eventSources.push(src); eventSources.push(source);
fetchEventSource(src, function() { fetchEventSource(source, function() {
eventsChanged(); eventsChanged();
}); });
}, },
@ -490,19 +511,21 @@ $.fn.fullCalendar = function(options) {
} }
function buildSection(buttonStr) { function buildSection(buttonStr) {
if (buttonStr) { if (buttonStr) {
var tr = $("<tr/>"), var tr = $("<tr/>");
prevTitle = false;
$.each(buttonStr.split(' '), function(i) { $.each(buttonStr.split(' '), function(i) {
if (i > 0) { if (i > 0) {
tr.append("<td><span class='fc-header-space'/></td>"); tr.append("<td><span class='fc-header-space'/></td>");
} }
var prevButton;
$.each(this.split(','), function(j) { $.each(this.split(','), function(j) {
var buttonName = this, var buttonName = this,
buttonNameShort = this.replace(/^(basic|agenda)/, '').toLowerCase(); buttonNameShort = this.replace(/^(basic|agenda)/, '').toLowerCase();
if (buttonName == 'title') { if (buttonName == 'title') {
tr.find('> :last div').addClass(tm + '-corner-right');
tr.append("<td><h2 class='fc-header-title'/></td>"); tr.append("<td><h2 class='fc-header-title'/></td>");
prevTitle = true; if (prevButton) {
prevButton.addClass(tm + '-corner-right');
}
prevButton = null;
}else{ }else{
var buttonClick; var buttonClick;
if (publicMethods[buttonNameShort]) { if (publicMethods[buttonNameShort]) {
@ -512,6 +535,9 @@ $.fn.fullCalendar = function(options) {
buttonClick = function() { switchView(buttonName) }; buttonClick = function() { switchView(buttonName) };
} }
if (buttonClick) { if (buttonClick) {
if (prevButton) {
prevButton.addClass(tm + '-no-right');
}
var button, var button,
icon = options.theme ? options.buttonIcons[buttonNameShort] : null, icon = options.theme ? options.buttonIcons[buttonNameShort] : null,
text = options.buttonText[buttonNameShort]; text = options.buttonText[buttonNameShort];
@ -549,17 +575,19 @@ $.fn.fullCalendar = function(options) {
switchView(buttonName); switchView(buttonName);
}); });
} }
if (j == 0 || prevTitle) { if (prevButton) {
button.addClass(tm + '-corner-left'); prevButton.addClass(tm + '-no-right');
}else{ }else{
button.addClass(tm + '-no-left'); button.addClass(tm + '-corner-left');
} }
prevTitle = false; prevButton = button;
} }
} }
} }
}); });
tr.find('> :last div').addClass(tm + '-corner-right'); if (prevButton) {
prevButton.addClass(tm + '-corner-right');
}
}); });
return $("<table/>").append(tr); return $("<table/>").append(tr);
} }
@ -571,11 +599,11 @@ $.fn.fullCalendar = function(options) {
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var elementWidth, var elementWidth,
ignoreResizes = false, ignoreWindowResizes = false,
resizeCnt = 0; resizeCnt = 0;
$(window).resize(function() { $(window).resize(function() {
if (!ignoreResizes) { if (!ignoreWindowResizes) {
var rcnt = ++resizeCnt; // add a delay var rcnt = ++resizeCnt; // add a delay
setTimeout(function() { setTimeout(function() {
if (rcnt == resizeCnt) { if (rcnt == resizeCnt) {
@ -610,7 +638,7 @@ $.fn.fullCalendar = function(options) {
var fakeID = 0; var fakeID = 0;
function normalizeEvent(event) { function normalizeEvent(event) {
event._id = event._id || (typeof event.id == 'undefined' ? '_fc' + fakeID++ : event.id + ''); event._id = event._id || (event.id == undefined ? '_fc' + fakeID++ : event.id + '');
if (event.date) { if (event.date) {
if (!event.start) { if (!event.start) {
event.start = event.date; event.start = event.date;
@ -620,10 +648,10 @@ function normalizeEvent(event) {
event._start = cloneDate(event.start = parseDate(event.start)); event._start = cloneDate(event.start = parseDate(event.start));
event.end = parseDate(event.end); event.end = parseDate(event.end);
if (event.end && event.end < event.start) { if (event.end && event.end < event.start) {
event.end = cloneDate(event.start); event.end = null;
} }
event._end = event.end ? cloneDate(event.end) : null; event._end = event.end ? cloneDate(event.end) : null;
if (typeof event.allDay == 'undefined') { if (event.allDay == undefined) {
event.allDay = true; event.allDay = true;
} }
} }

View file

@ -1 +1,2 @@
})(jQuery); })(jQuery);

View file

@ -2,8 +2,9 @@
* FullCalendar * FullCalendar
* http://arshaw.com/fullcalendar/ * http://arshaw.com/fullcalendar/
* *
* use fullcalendar.css for basic styling * Use fullcalendar.css for basic styling.
* requires jQuery UI core and draggables ONLY if you plan to do drag & drop * For event drag & drop, required jQuery UI draggable.
* For event resizing, requires jQuery UI resizable.
* *
* Copyright (c) 2009 Adam Shaw * Copyright (c) 2009 Adam Shaw
* Dual licensed under the MIT and GPL licenses: * Dual licensed under the MIT and GPL licenses:
@ -15,3 +16,4 @@
*/ */
(function($) { (function($) {

View file

@ -38,9 +38,8 @@ function clearTime(d) {
function cloneDate(d, dontKeepTime) { function cloneDate(d, dontKeepTime) {
if (dontKeepTime) { if (dontKeepTime) {
return clearTime(new Date(+d)); return clearTime(new Date(+d));
}else{
return new Date(+d);
} }
return new Date(+d);
} }
@ -49,23 +48,24 @@ function cloneDate(d, dontKeepTime) {
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var parseDate = fc.parseDate = function(s) { var parseDate = fc.parseDate = function(s) {
if (typeof s == 'object') if (typeof s == 'object') { // already a Date object
return s; // already a Date object return s;
if (typeof s == 'undefined') }
return null; if (typeof s == 'number') { // a UNIX timestamp
if (typeof s == 'number')
return new Date(s * 1000); return new Date(s * 1000);
return parseISO8601(s, true) || }
Date.parse(s) || if (typeof s == 'string') {
new Date(parseInt(s) * 1000); if (s.match(/^\d+$/)) { // a UNIX timestamp
return new Date(parseInt(s) * 1000);
}
return parseISO8601(s, true) || Date.parse(s) || null;
}
return null;
} }
var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) { var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) {
// derived from http://delete.me.uk/2005/03/iso8601.html // derived from http://delete.me.uk/2005/03/iso8601.html
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" + var d = s.match(parseISO8601Regex);
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
var d = s.match(new RegExp(regexp));
if (!d) return null; if (!d) return null;
var offset = 0; var offset = 0;
var date = new Date(d[1], 0, 1); var date = new Date(d[1], 0, 1);
@ -85,6 +85,11 @@ var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) {
return new Date(Number(date) + (offset * 60 * 1000)); return new Date(Number(date) + (offset * 60 * 1000));
} }
var parseISO8601Regex = new RegExp(
"([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?");
/* Date Formatting /* Date Formatting
@ -253,11 +258,9 @@ function HoverMatrix(changeCallback) {
origRow, origCol, origRow, origCol,
currRow, currCol; currRow, currCol;
// this.cell = null; this.row = function(e, topBug) {
this.row = function(e) {
prevRowE = $(e); prevRowE = $(e);
tops.push(prevRowE.offset().top); tops.push(prevRowE.offset().top + (topBug ? prevRowE.parent().position().top : 0));
}; };
this.col = function(e) { this.col = function(e) {
@ -265,13 +268,12 @@ function HoverMatrix(changeCallback) {
lefts.push(prevColE.offset().left); lefts.push(prevColE.offset().left);
}; };
this.start = function() {
tops.push(tops[tops.length-1] + prevRowE.outerHeight());
lefts.push(lefts[lefts.length-1] + prevColE.outerWidth());
origRow = currRow = currCol = -1;
};
this.mouse = function(x, y) { this.mouse = function(x, y) {
if (origRow == undefined) {
tops.push(tops[tops.length-1] + prevRowE.outerHeight());
lefts.push(lefts[lefts.length-1] + prevColE.outerWidth());
currRow = currCol = -1;
}
var r, c; var r, c;
for (r=0; r<tops.length && y>=tops[r]; r++) ; for (r=0; r<tops.length && y>=tops[r]; r++) ;
for (c=0; c<lefts.length && x>=lefts[c]; c++) ; for (c=0; c<lefts.length && x>=lefts[c]; c++) ;
@ -283,7 +285,7 @@ function HoverMatrix(changeCallback) {
if (r == -1 || c == -1) { if (r == -1 || c == -1) {
this.cell = null; this.cell = null;
}else{ }else{
if (origRow == -1) { if (origRow == undefined) {
origRow = r; origRow = r;
origCol = c; origCol = c;
} }
@ -310,7 +312,8 @@ function HoverMatrix(changeCallback) {
/* Misc Utils /* Misc Utils
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; var undefined,
dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
function zeroPad(n) { function zeroPad(n) {
return (n < 10 ? '0' : '') + n; return (n < 10 ? '0' : '') + n;
@ -319,3 +322,4 @@ function zeroPad(n) {
function strProp(s, prop) { function strProp(s, prop) {
return typeof s == 'string' ? s : s[prop]; return typeof s == 'string' ? s : s[prop];
} }

View file

@ -1,19 +1,30 @@
/* Methods & Utilities for All Views
-----------------------------------------------------------------------------*/
var viewMethods = { var viewMethods = {
// /*
// Objects inheriting these methods must implement the following properties/methods: * Objects inheriting these methods must implement the following properties/methods:
// - title * - title
// - start * - start
// - end * - end
// - visStart * - visStart
// - visEnd * - visEnd
// - defaultEventEnd(event) * - defaultEventEnd(event)
// - visEventEnd(event) * - visEventEnd(event)
// * - render(events)
// - render * - rerenderEvents()
// - rerenderEvents *
// *
* z-index reservations:
* 1. day-overlay
* 2. events
* 3. dragging/resizing events
*
*/
init: function(element, options) { init: function(element, options) {
this.element = element; this.element = element;
@ -26,7 +37,7 @@ var viewMethods = {
// trigger event handlers, always append view as last arg // triggers an event handler, always append view as last arg
trigger: function(name, thisObj) { trigger: function(name, thisObj) {
if (this.options[name]) { if (this.options[name]) {
@ -36,7 +47,7 @@ var viewMethods = {
// // returns a Date object for an event's end
eventEnd: function(event) { eventEnd: function(event) {
return event.end || this.defaultEventEnd(event); return event.end || this.defaultEventEnd(event);
@ -44,14 +55,13 @@ var viewMethods = {
// event/element creation reporting // report when view receives new events
reportEvents: function(events) { reportEvents: function(events) { // events are already normalized at this point
var i, len=events.length, event, var i, len=events.length, event,
fakeID = 0,
eventsByID = this.eventsByID = {}, eventsByID = this.eventsByID = {},
cachedEvents = this.cachedEvents = []; cachedEvents = this.cachedEvents = [];
for (i=0; i<len; i++) { // TODO: move _id creation to more global 'cleanEvents' for (i=0; i<len; i++) {
event = events[i]; event = events[i];
if (eventsByID[event._id]) { if (eventsByID[event._id]) {
eventsByID[event._id].push(event); eventsByID[event._id].push(event);
@ -62,6 +72,10 @@ var viewMethods = {
} }
}, },
// report when view creates an element for an event
reportEventElement: function(event, element) { reportEventElement: function(event, element) {
this.eventElements.push(element); this.eventElements.push(element);
var eventElementsByID = this.eventElementsByID; var eventElementsByID = this.eventElementsByID;
@ -76,7 +90,7 @@ var viewMethods = {
// event element manipulation // event element manipulation
clearEvents: function() { // just remove ELEMENTS clearEvents: function() { // only remove ELEMENTS
$.each(this.eventElements, function() { $.each(this.eventElements, function() {
this.remove(); this.remove();
}); });
@ -89,12 +103,13 @@ var viewMethods = {
}, },
hideEvents: function(event, exceptElement) { hideEvents: function(event, exceptElement) {
this._eee(event, exceptElement, 'hide'); // fadeOut this._eee(event, exceptElement, 'hide');
}, },
_eee: function(event, exceptElement, funcName) { // event-element-each _eee: function(event, exceptElement, funcName) { // event-element-each
var elements = this.eventElementsByID[event._id]; var elements = this.eventElementsByID[event._id],
for (var i=0; i<elements.length; i++) { i, len = elements.length;
for (i=0; i<len; i++) {
if (elements[i] != exceptElement) { if (elements[i] != exceptElement) {
elements[i][funcName](); elements[i][funcName]();
} }
@ -105,41 +120,41 @@ var viewMethods = {
// event modification reporting // event modification reporting
moveEvent: function(event, days, minutes) { // and actually DO the date change too moveEvent: function(event, days, minutes) { // actually DO the date changes
minutes = minutes || 0; minutes = minutes || 0;
var i, event2, events = this.eventsByID[event._id]; var events = this.eventsByID[event._id],
for (i=0; i<events.length; i++) { i, len=events.length, e;
event2 = events[i]; for (i=0; i<len; i++) {
event2.allDay = event.allDay; e = events[i];
addMinutes(addDays(event2.start, days, true), minutes); e.allDay = event.allDay;
if (event.end) { addMinutes(addDays(e.start, days, true), minutes);
event2.end = addMinutes(addDays(this.eventEnd(event2), days, true), minutes); if (e.end) {
}else{ e.end = addMinutes(addDays(e.end, days, true), minutes);
event2.end = null;
} }
normalizeEvent(event2); normalizeEvent(e);
} }
this.eventsChanged = true; this.eventsChanged = true;
}, },
resizeEvent: function(event, days, minutes) { // and actually DO the date change too resizeEvent: function(event, days, minutes) { // actually DO the date changes
minutes = minutes || 0; minutes = minutes || 0;
var i, event2, events = this.eventsByID[event._id]; var events = this.eventsByID[event._id],
for (i=0; i<events.length; i++) { i, len=events.length, e;
event2 = events[i]; for (i=0; i<len; i++) {
event2.end = addMinutes(addDays(this.eventEnd(event2), days, true), minutes); e = events[i];
normalizeEvent(event2); e.end = addMinutes(addDays(this.eventEnd(e), days, true), minutes);
normalizeEvent(e);
} }
this.eventsChanged = true; this.eventsChanged = true;
}, },
// semi-transparent overlay (for days while dragging) // semi-transparent overlay (while dragging)
showOverlay: function(props) { showOverlay: function(props) {
if (!this.dayOverlay) { if (!this.dayOverlay) {
this.dayOverlay = $("<div class='fc-cell-overlay' style='position:absolute;display:none'/>") this.dayOverlay = $("<div class='fc-cell-overlay' style='position:absolute;z-index:1;display:none'/>")
.appendTo(this.element); .appendTo(this.element);
} }
var o = this.element.offset(); var o = this.element.offset();
@ -217,7 +232,7 @@ function stackSegs(segs) {
collide = false; collide = false;
if (levels[j]) { if (levels[j]) {
for (k=0; k<levels[j].length; k++) { for (k=0; k<levels[j].length; k++) {
if (seg.end > levels[j][k].start && seg.start < levels[j][k].end) { if (segsCollide(levels[j][k], seg)) {
collide = true; collide = true;
break; break;
} }
@ -246,3 +261,4 @@ function segCmp(a, b) {
function segsCollide(seg1, seg2) { function segsCollide(seg1, seg2) {
return seg1.end > seg2.start && seg1.start < seg2.end; return seg1.end > seg2.start && seg1.start < seg2.end;
} }

View file

@ -76,7 +76,7 @@
</script> </script>
</head> </head>
<body style='font-size:12px'> <body style='font-size:14px'>
<div id='calendar' style='width:900px;margin:20px auto 0;font-family:arial'></div> <div id='calendar' style='width:900px;margin:20px auto 0;font-family:arial'></div>
</body> </body>
</html> </html>

View file

@ -33,7 +33,7 @@
dragOpacity: .5, dragOpacity: .5,
dragRevertDuration: 100, dragRevertDuration: 100,
weekMode: 'liquid', //'variable' //weekMode: 'liquid', //'variable'
/* /*
titleFormat: { titleFormat: {

View file

@ -11,7 +11,7 @@
var gcalFeed = $.fullCalendar.gcalFeed("http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic"); var gcalFeed = $.fullCalendar.gcalFeed("http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic");
var jsonFeed = "../examples/json_events.php"; var jsonFeed = "../examples/json-events.php";
var staticEvents = [ var staticEvents = [
{ {

View file

@ -20,20 +20,20 @@
viewDisplay: function(view) { viewDisplay: function(view) {
console.log('viewDisplay'); console.log('viewDisplay');
console.log(view); //console.log(view);
console.log(this); //console.log(this);
}, },
//loading: // see sources.html //loading: // see sources.html
windowResize: function(view) { windowResize: function(view) {
console.log('windowResize - ' + view.title); console.log('windowResize - ' + view.title);
console.log(this); //console.log(this);
}, },
dayClick: function(dayDate, view) { dayClick: function(dayDate, view) {
console.log('dayClick - ' + dayDate + ' - ' + view.title); console.log('dayClick - ' + dayDate + ' - ' + view.title);
console.log(this); //console.log(this);
}, },
eventRender: function(event, element, view) { eventRender: function(event, element, view) {
@ -45,16 +45,16 @@
} }
else if (event.id == 1) { else if (event.id == 1) {
element.css('border-color', 'red'); element.css('border-color', 'red');
console.log('renderEvent (' + event.title + ') - ' + view.title); //console.log('renderEvent (' + event.title + ') - ' + view.title);
} }
}, },
eventClick: function(event, jsEvent, view) { eventClick: function(event, jsEvent, view) {
console.log('EVENT CLICK ' + event.title); console.log('EVENT CLICK ' + event.title);
console.log(jsEvent); //console.log(jsEvent);
console.log(view); //console.log(view);
console.log(this); //console.log(this);
return false; //return false;
}, },
/* /*
eventMouseover: function(event, jsEvent, view) { eventMouseover: function(event, jsEvent, view) {
@ -73,20 +73,23 @@
eventDragStart: function(event, jsEvent, ui, view) { eventDragStart: function(event, jsEvent, ui, view) {
console.log('DRAG START ' + event.title); console.log('DRAG START ' + event.title);
console.log(this); //console.log(this);
}, },
eventDragStop: function(event, jsEvent, ui, view) { eventDragStop: function(event, jsEvent, ui, view) {
console.log('DRAG STOP ' + event.title); console.log('DRAG STOP ' + event.title);
console.log(this); //console.log(this);
}, },
eventDrop: function(event, dayDelta, minuteDelta, jsEvent, ui, view) { eventDrop: function(event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
console.log('DROP ' + event.title); console.log('DROP ' + event.title);
console.log(dayDelta + ' days'); console.log(dayDelta + ' days');
console.log(minuteDelta + ' minutes'); console.log(minuteDelta + ' minutes');
console.log(jsEvent); /*setTimeout(function() {
console.log(ui); revertFunc();
console.log(view.title); }, 2000);*/
console.log(this); //console.log(jsEvent);
//console.log(ui);
//console.log(view.title);
//console.log(this);
}, },
eventResizeStart: function(event, jsEvent, ui, view) { eventResizeStart: function(event, jsEvent, ui, view) {
@ -97,14 +100,17 @@
console.log('RESIZE STOP ' + event.title); console.log('RESIZE STOP ' + event.title);
console.log(this); console.log(this);
}, },
eventResize: function(event, dayDelta, minuteDelta, jsEvent, ui, view) { eventResize: function(event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view) {
console.log('RESIZE!! ' + event.title); console.log('RESIZE!! ' + event.title);
console.log(dayDelta + ' days'); console.log(dayDelta + ' days');
console.log(minuteDelta + ' minutes'); console.log(minuteDelta + ' minutes');
console.log(jsEvent); /*setTimeout(function() {
console.log(ui); revertFunc();
console.log(view.title); }, 2000);*/
console.log(this); //console.log(jsEvent);
//console.log(ui);
//console.log(view.title);
//console.log(this);
}, },
events: [ events: [
@ -144,10 +150,10 @@
}, },
{ {
id: 4, id: 4,
title: "Click for Facebook", title: "Click for Google",
start: new Date(y, m, 27), start: new Date(y, m, 27),
end: new Date(y, m, 28), end: new Date(y, m, 28),
url: "http://facebook.com/" url: "http://google.com/"
}, },
{ {
id: 5, id: 5,