This commit is contained in:
Adam Shaw 2009-09-21 04:57:20 +00:00
parent b3b84dca9a
commit 44526dbe3d
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)
@ -69,6 +69,9 @@ always available (:ref:`more below <view-object>`).
``dayDelta`` and ``minuteDelta`` are elegant for dealing with multi-day and ``dayDelta`` and ``minuteDelta`` are elegant for dealing with multi-day and
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).
@ -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
@ -86,6 +89,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 {
@ -53,4 +56,5 @@
.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,
@ -62,26 +71,61 @@ table.fc-header {
.fc-rtl .fc-header-title { .fc-rtl .fc-header-title {
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;
} }
/* for fake rounded corners */
.fc-event a { /* prep for 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,52 +21,44 @@
'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);
}); });
} }
} }
})(jQuery); })(jQuery);

File diff suppressed because it is too large Load diff

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,22 +457,20 @@ $.fn.fullCalendar = function(options) {
return e._id == filter; return e._id == filter;
}); });
} }
else { return events; // else, return all
return events;
}
}, },
rerenderEvents: function() { rerenderEvents: function() {
view.rerenderEvents(); view.rerenderEvents();
}, },
// //
// 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:
@ -14,4 +15,5 @@
* Revision: * Revision:
*/ */
(function($) { (function($) {

View file

@ -1,321 +1,325 @@
/* Date Math /* Date Math
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var DAY_MS = 86400000; var DAY_MS = 86400000;
function addYears(d, n, keepTime) { function addYears(d, n, keepTime) {
d.setFullYear(d.getFullYear() + n); d.setFullYear(d.getFullYear() + n);
if (keepTime) return d; if (keepTime) return d;
return clearTime(d); return clearTime(d);
} }
function addMonths(d, n, keepTime) { function addMonths(d, n, keepTime) {
d.setMonth(d.getMonth() + n); d.setMonth(d.getMonth() + n);
if (keepTime) return d; if (keepTime) return d;
return clearTime(d); return clearTime(d);
} }
function addDays(d, n, keepTime) { function addDays(d, n, keepTime) {
d.setDate(d.getDate() + n); d.setDate(d.getDate() + n);
if (keepTime) return d; if (keepTime) return d;
return clearTime(d); return clearTime(d);
} }
function addMinutes(d, n) { function addMinutes(d, n) {
d.setMinutes(d.getMinutes() + n); d.setMinutes(d.getMinutes() + n);
return d; return d;
} }
function clearTime(d) { function clearTime(d) {
d.setHours(0); d.setHours(0);
d.setMinutes(0); d.setMinutes(0);
d.setSeconds(0); d.setSeconds(0);
d.setMilliseconds(0); d.setMilliseconds(0);
return d; return 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);
} }
}
/* Date Parsing
/* Date Parsing -----------------------------------------------------------------------------*/
-----------------------------------------------------------------------------*/
var parseDate = fc.parseDate = function(s) {
var parseDate = fc.parseDate = function(s) { if (typeof s == 'object') { // already a Date object
if (typeof s == 'object') return s;
return s; // already a Date object }
if (typeof s == 'undefined') if (typeof s == 'number') { // a UNIX timestamp
return null; return new Date(s * 1000);
if (typeof s == 'number') }
return new Date(s * 1000); if (typeof s == 'string') {
return parseISO8601(s, true) || if (s.match(/^\d+$/)) { // a UNIX timestamp
Date.parse(s) || return new Date(parseInt(s) * 1000);
new Date(parseInt(s) * 1000); }
} return parseISO8601(s, true) || Date.parse(s) || null;
}
var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) { return null;
// derived from http://delete.me.uk/2005/03/iso8601.html }
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" + var parseISO8601 = fc.parseISO8601 = function(s, ignoreTimezone) {
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"; // derived from http://delete.me.uk/2005/03/iso8601.html
var d = s.match(new RegExp(regexp)); var d = s.match(parseISO8601Regex);
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);
if (d[3]) { date.setMonth(d[3] - 1); } if (d[3]) { date.setMonth(d[3] - 1); }
if (d[5]) { date.setDate(d[5]); } if (d[5]) { date.setDate(d[5]); }
if (d[7]) { date.setHours(d[7]); } if (d[7]) { date.setHours(d[7]); }
if (d[8]) { date.setMinutes(d[8]); } if (d[8]) { date.setMinutes(d[8]); }
if (d[10]) { date.setSeconds(d[10]); } if (d[10]) { date.setSeconds(d[10]); }
if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); } if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
if (!ignoreTimezone) { if (!ignoreTimezone) {
if (d[14]) { if (d[14]) {
offset = (Number(d[16]) * 60) + Number(d[17]); offset = (Number(d[16]) * 60) + Number(d[17]);
offset *= ((d[15] == '-') ? 1 : -1); offset *= ((d[15] == '-') ? 1 : -1);
} }
offset -= date.getTimezoneOffset(); offset -= date.getTimezoneOffset();
} }
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})" +
/* Date Formatting "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
-----------------------------------------------------------------------------*/ "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?");
var formatDate = fc.formatDate = function(date, format, options) {
return formatDates(date, null, format, options);
} /* Date Formatting
-----------------------------------------------------------------------------*/
var formatDates = fc.formatDates = function(date1, date2, format, options) {
options = options || defaults; var formatDate = fc.formatDate = function(date, format, options) {
var date = date1, return formatDates(date, null, format, options);
otherDate = date2, }
i, len = format.length, c,
i2, formatter, var formatDates = fc.formatDates = function(date1, date2, format, options) {
res = ''; options = options || defaults;
for (i=0; i<len; i++) { var date = date1,
c = format.charAt(i); otherDate = date2,
if (c == "'") { i, len = format.length, c,
for (i2=i+1; i2<len; i2++) { i2, formatter,
if (format.charAt(i2) == "'") { res = '';
if (date) { for (i=0; i<len; i++) {
if (i2 == i+1) { c = format.charAt(i);
res += "'"; if (c == "'") {
}else{ for (i2=i+1; i2<len; i2++) {
res += format.substring(i+1, i2); if (format.charAt(i2) == "'") {
} if (date) {
i = i2; if (i2 == i+1) {
} res += "'";
break; }else{
} res += format.substring(i+1, i2);
} }
} i = i2;
else if (c == '(') { }
for (i2=i+1; i2<len; i2++) { break;
if (format.charAt(i2) == ')') { }
var subres = formatDate(date, format.substring(i+1, i2), options); }
if (parseInt(subres.replace(/\D/, ''))) { }
res += subres; else if (c == '(') {
} for (i2=i+1; i2<len; i2++) {
i = i2; if (format.charAt(i2) == ')') {
break; var subres = formatDate(date, format.substring(i+1, i2), options);
} if (parseInt(subres.replace(/\D/, ''))) {
} res += subres;
} }
else if (c == '[') { i = i2;
for (i2=i+1; i2<len; i2++) { break;
if (format.charAt(i2) == ']') { }
var subformat = format.substring(i+1, i2); }
var subres = formatDate(date, subformat, options); }
if (subres != formatDate(otherDate, subformat, options)) { else if (c == '[') {
res += subres; for (i2=i+1; i2<len; i2++) {
} if (format.charAt(i2) == ']') {
i = i2; var subformat = format.substring(i+1, i2);
break; var subres = formatDate(date, subformat, options);
} if (subres != formatDate(otherDate, subformat, options)) {
} res += subres;
} }
else if (c == '{') { i = i2;
date = date2; break;
otherDate = date1; }
} }
else if (c == '}') { }
date = date1; else if (c == '{') {
otherDate = date2; date = date2;
} otherDate = date1;
else { }
for (i2=len; i2>i; i2--) { else if (c == '}') {
if (formatter = dateFormatters[format.substring(i, i2)]) { date = date1;
if (date) { otherDate = date2;
res += formatter(date, options); }
} else {
i = i2 - 1; for (i2=len; i2>i; i2--) {
break; if (formatter = dateFormatters[format.substring(i, i2)]) {
} if (date) {
} res += formatter(date, options);
if (i2 == i) { }
if (date) { i = i2 - 1;
res += c; break;
} }
} }
} if (i2 == i) {
} if (date) {
return res; res += c;
} }
}
var dateFormatters = { }
s : function(d) { return d.getSeconds() }, }
ss : function(d) { return zeroPad(d.getSeconds()) }, return res;
m : function(d) { return d.getMinutes() }, }
mm : function(d) { return zeroPad(d.getMinutes()) },
h : function(d) { return d.getHours() % 12 || 12 }, var dateFormatters = {
hh : function(d) { return zeroPad(d.getHours() % 12 || 12) }, s : function(d) { return d.getSeconds() },
H : function(d) { return d.getHours() }, ss : function(d) { return zeroPad(d.getSeconds()) },
HH : function(d) { return zeroPad(d.getHours()) }, m : function(d) { return d.getMinutes() },
d : function(d) { return d.getDate() }, mm : function(d) { return zeroPad(d.getMinutes()) },
dd : function(d) { return zeroPad(d.getDate()) }, h : function(d) { return d.getHours() % 12 || 12 },
ddd : function(d,o) { return o.dayNamesShort[d.getDay()] }, hh : function(d) { return zeroPad(d.getHours() % 12 || 12) },
dddd: function(d,o) { return o.dayNames[d.getDay()] }, H : function(d) { return d.getHours() },
M : function(d) { return d.getMonth() + 1 }, HH : function(d) { return zeroPad(d.getHours()) },
MM : function(d) { return zeroPad(d.getMonth() + 1) }, d : function(d) { return d.getDate() },
MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] }, dd : function(d) { return zeroPad(d.getDate()) },
MMMM: function(d,o) { return o.monthNames[d.getMonth()] }, ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
yy : function(d) { return (d.getFullYear()+'').substring(2) }, dddd: function(d,o) { return o.dayNames[d.getDay()] },
yyyy: function(d) { return d.getFullYear() }, M : function(d) { return d.getMonth() + 1 },
t : function(d) { return d.getHours() < 12 ? 'a' : 'p' }, MM : function(d) { return zeroPad(d.getMonth() + 1) },
tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' }, MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
T : function(d) { return d.getHours() < 12 ? 'A' : 'P' }, MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' }, yy : function(d) { return (d.getFullYear()+'').substring(2) },
u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") }, yyyy: function(d) { return d.getFullYear() },
S : function(d) { t : function(d) { return d.getHours() < 12 ? 'a' : 'p' },
var date = d.getDate(); tt : function(d) { return d.getHours() < 12 ? 'am' : 'pm' },
if (date > 10 && date < 20) return 'th'; T : function(d) { return d.getHours() < 12 ? 'A' : 'P' },
return ['st', 'nd', 'rd'][date%10-1] || 'th'; TT : function(d) { return d.getHours() < 12 ? 'AM' : 'PM' },
} u : function(d) { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
}; S : function(d) {
var date = d.getDate();
if (date > 10 && date < 20) return 'th';
return ['st', 'nd', 'rd'][date%10-1] || 'th';
/* Element Dimensions }
-----------------------------------------------------------------------------*/ };
function setOuterWidth(element, width, includeMargins) {
element.each(function() {
var e = $(this); /* Element Dimensions
var w = width - ( -----------------------------------------------------------------------------*/
(parseInt(e.css('border-left-width')) || 0) +
(parseInt(e.css('padding-left')) || 0) + function setOuterWidth(element, width, includeMargins) {
(parseInt(e.css('padding-right')) || 0) + element.each(function() {
(parseInt(e.css('border-right-width')) || 0)); var e = $(this);
if (includeMargins) { var w = width - (
w -= (parseInt(e.css('border-left-width')) || 0) +
(parseInt(e.css('margin-left')) || 0) + (parseInt(e.css('padding-left')) || 0) +
(parseInt(e.css('margin-right')) || 0); (parseInt(e.css('padding-right')) || 0) +
} (parseInt(e.css('border-right-width')) || 0));
e.width(w); if (includeMargins) {
}); w -=
} (parseInt(e.css('margin-left')) || 0) +
(parseInt(e.css('margin-right')) || 0);
function setOuterHeight(element, height, includeMargins) { }
element.each(function() { e.width(w);
var e = $(this); });
var h = height - ( }
(parseInt(e.css('border-top-width')) || 0) +
(parseInt(e.css('padding-top')) || 0) + function setOuterHeight(element, height, includeMargins) {
(parseInt(e.css('padding-bottom')) || 0) + element.each(function() {
(parseInt(e.css('border-bottom-width')) || 0)); var e = $(this);
if (includeMargins) { var h = height - (
h -= (parseInt(e.css('border-top-width')) || 0) +
(parseInt(e.css('margin-top')) || 0) + (parseInt(e.css('padding-top')) || 0) +
(parseInt(e.css('margin-bottom')) || 0); (parseInt(e.css('padding-bottom')) || 0) +
} (parseInt(e.css('border-bottom-width')) || 0));
e.height(h); if (includeMargins) {
}); h -=
} (parseInt(e.css('margin-top')) || 0) +
(parseInt(e.css('margin-bottom')) || 0);
}
e.height(h);
/* Hover Matrix });
-----------------------------------------------------------------------------*/ }
function HoverMatrix(changeCallback) {
var tops=[], lefts=[], /* Hover Matrix
prevRowE, prevColE, -----------------------------------------------------------------------------*/
origRow, origCol,
currRow, currCol; function HoverMatrix(changeCallback) {
// this.cell = null; var tops=[], lefts=[],
prevRowE, prevColE,
this.row = function(e) { origRow, origCol,
prevRowE = $(e); currRow, currCol;
tops.push(prevRowE.offset().top);
}; this.row = function(e, topBug) {
prevRowE = $(e);
this.col = function(e) { tops.push(prevRowE.offset().top + (topBug ? prevRowE.parent().position().top : 0));
prevColE = $(e); };
lefts.push(prevColE.offset().left);
}; this.col = function(e) {
prevColE = $(e);
this.start = function() { lefts.push(prevColE.offset().left);
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) {
}; if (origRow == undefined) {
tops.push(tops[tops.length-1] + prevRowE.outerHeight());
this.mouse = function(x, y) { lefts.push(lefts[lefts.length-1] + prevColE.outerWidth());
var r, c; currRow = currCol = -1;
for (r=0; r<tops.length && y>=tops[r]; r++) ; }
for (c=0; c<lefts.length && x>=lefts[c]; c++) ; var r, c;
r = r >= tops.length ? -1 : r - 1; for (r=0; r<tops.length && y>=tops[r]; r++) ;
c = c >= lefts.length ? -1 : c - 1; for (c=0; c<lefts.length && x>=lefts[c]; c++) ;
if (r != currRow || c != currCol) { r = r >= tops.length ? -1 : r - 1;
currRow = r; c = c >= lefts.length ? -1 : c - 1;
currCol = c; if (r != currRow || c != currCol) {
if (r == -1 || c == -1) { currRow = r;
this.cell = null; currCol = c;
}else{ if (r == -1 || c == -1) {
if (origRow == -1) { this.cell = null;
origRow = r; }else{
origCol = c; if (origRow == undefined) {
} origRow = r;
this.cell = { origCol = c;
row: r, }
col: c, this.cell = {
top: tops[r], row: r,
left: lefts[c], col: c,
width: lefts[c+1] - lefts[c], top: tops[r],
height: tops[r+1] - tops[r], left: lefts[c],
isOrig: r==origRow && c==origCol, width: lefts[c+1] - lefts[c],
rowDelta: r-origRow, height: tops[r+1] - tops[r],
colDelta: c-origCol isOrig: r==origRow && c==origCol,
}; rowDelta: r-origRow,
} colDelta: c-origCol
changeCallback(this.cell); };
} }
}; changeCallback(this.cell);
}
} };
}
/* Misc Utils
-----------------------------------------------------------------------------*/
/* Misc Utils
var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; -----------------------------------------------------------------------------*/
function zeroPad(n) { var undefined,
return (n < 10 ? '0' : '') + n; dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
}
function zeroPad(n) {
function strProp(s, prop) { return (n < 10 ? '0' : '') + n;
return typeof s == 'string' ? s : s[prop]; }
}
function strProp(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);
@ -61,6 +71,10 @@ var viewMethods = {
cachedEvents.push(event); cachedEvents.push(event);
} }
}, },
// 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);
@ -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,