From 23d99323cc901e60bcaeb784dc04a527f082a5ce Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sat, 31 Oct 2009 23:51:30 +0000 Subject: [PATCH] v1.4.1 --- changelog.txt | 8 ++++ docs/google-calendar.txt | 21 ++++++++- docs/index.txt | 3 ++ src/agenda.js | 47 +++++++++++++++----- src/gcal.js | 81 ++++++++++++++++----------------- src/grid.js | 96 +++++++++++++++++++++++++++++++--------- src/main.js | 13 ++++-- src/util.js | 17 +++++++ tests/default.html | 2 +- tests/gcal.html | 88 ++++++------------------------------ tests/locale.html | 1 + tests/methods.html | 1 + tests/options.html | 15 +++++-- tests/theming.html | 1 + tests/triggers.html | 7 ++- version.txt | 2 +- 16 files changed, 247 insertions(+), 156 deletions(-) diff --git a/changelog.txt b/changelog.txt index 715684a..b0f5ff9 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,12 @@ +version 1.4.1 (10/31/09) + - can exclude weekends with new 'weekends' option + - gcal feed 'currentTimezone' option + - bugfixes + - year/month/date option sometimes wouldn't set correctly (depending on current date) + - daylight savings issue caused agenda views to start at 1am (for BST users) + - cleanup of gcal.js code + version 1.4 (10/19/09) - agendaWeek and agendaDay views - added some options for agenda views: diff --git a/docs/google-calendar.txt b/docs/google-calendar.txt index bd1da23..1f1226f 100755 --- a/docs/google-calendar.txt +++ b/docs/google-calendar.txt @@ -45,5 +45,24 @@ Here is a list of available options: * **className** - CSS class to attach to each event from this Google Calendar * **editable** - whether to allow dragging/resizing (default: ``false``) + +* **currentTimezone** - a string like "America/Chicago". Consult http://php.net/manual/en/timezones.php for a full list. -See *gcal.html* in the *examples* directory for a complete example. \ No newline at end of file +See *gcal.html* in the *examples* directory for a complete example. + + +Timezones Gotchas +----------------- + +Sometimes it can be confusing as to why FullCalendar displays event times differently +than the Google Calendar interface. There are the two factors involved in this: + +* **the calendar's timezone**, accessed through "Calendar settings" after clicking the arrow next to the calendar's name + +* **your Google Account's timezone**, accessed through the "Settings" link at the top right + of the Google Calendar screen (near the "Sign out" link) + +When both timezones are the same, you should have no problems. When they are different, FullCalendar will display +times in the *calendar's* timezone. Thus, times will be different than what you see in the Google Calendar interface +because they are being adjusted to the GMT of the calendar. The solution is to use the ``currentTimezone`` option. +If this is set to the same timezone as your Google Account, all dates should appear consistent. diff --git a/docs/index.txt b/docs/index.txt index bfa5a1d..01beec7 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -81,6 +81,9 @@ Basic Options **allDayDefault**: Boolean, *Default*: ``true`` Determines the default value for each :ref:`CalEvent's ` ``allDay`` property, when it is unspecified. + +**weekends**: Boolean, *Default*: ``true`` + Whether to include Saturday/Sunday columns in any of the views. **weekMode**: String, *Default*: ``'fixed'`` Determines the number of weeks displayed in a month view. diff --git a/src/agenda.js b/src/agenda.js index b7ef144..7375cc9 100755 --- a/src/agenda.js +++ b/src/agenda.js @@ -23,13 +23,23 @@ views.agendaWeek = function(element, options) { if (delta) { addDays(date, delta * 7); } + var visStart = this.visStart = cloneDate( + this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) + ), + visEnd = this.visEnd = cloneDate( + this.end = addDays(cloneDate(visStart), 7) + ); + if (!options.weekends) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } this.title = formatDates( - 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), + visStart, + addDays(cloneDate(visEnd), -1), this.option('titleFormat'), options ); - this.renderAgenda(7, this.option('columnFormat'), fetchEvents); + this.renderAgenda(options.weekends ? 7 : 5, this.option('columnFormat'), fetchEvents); } }); }; @@ -39,6 +49,9 @@ views.agendaDay = function(element, options) { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta); + if (!options.weekends) { + skipWeekend(date, delta < 0 ? -1 : 1); + } } this.title = formatDate(date, this.option('titleFormat'), options); this.start = this.visStart = cloneDate(date, true); @@ -55,6 +68,7 @@ function Agenda(element, options, methods) { axisWidth, colWidth, slotHeight, cachedDaySegs, cachedSlotSegs, tm, firstDay, + nwe, // no weekends (int) rtl, dis, dit, // day index sign / translate // ... @@ -104,7 +118,8 @@ function Agenda(element, options, methods) { colCnt = c; // update option-derived variables - tm = options.theme ? 'ui' : 'fc'; + tm = options.theme ? 'ui' : 'fc'; + nwe = options.weekends ? 0 : 1; firstDay = options.firstDay; if (rtl = options.isRTL) { dis = -1; @@ -136,10 +151,13 @@ function Agenda(element, options, methods) { tm + '-state-default' + "'>" + formatDate(d, colFormat, options) + ""; addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } } - s+= " "; + s += " "; if (options.allDaySlot) { - s+= "" + + s += "" + "" + options.allDayText + "" + "" + "
 
" + @@ -152,7 +170,7 @@ function Agenda(element, options, methods) { head.find('td').click(slotClick); // body - d = new Date(1970, 0, 1); + d = zeroDate(); s = ""; for (i=0; d.getDate() != 2; i++) { minutes = d.getMinutes(); @@ -183,6 +201,9 @@ function Agenda(element, options, methods) { (+d == +today ? tm + '-state-highlight fc-today' : 'fc-not-today') + "'>
 
"; addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } } s += "
"; bg = $(s).appendTo(element); @@ -196,6 +217,9 @@ function Agenda(element, options, methods) { $(this).text(formatDate(d, colFormat, options)); this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } }); // change classes of background stripes @@ -214,6 +238,9 @@ function Agenda(element, options, methods) { .removeClass(tm + '-state-highlight'); } addDays(d, dis); + if (nwe) { + skipWeekend(d, dis); + } }); } @@ -226,7 +253,7 @@ function Agenda(element, options, methods) { function resetScroll() { - var d0 = new Date(1970, 0, 1), + var d0 = zeroDate(), scrollDate = cloneDate(d0); scrollDate.setHours(options.firstHour); var go = function() { @@ -394,13 +421,13 @@ function Agenda(element, options, methods) { } if (leftRounded) { className += 'fc-corner-left '; - left = bg.find('td:eq('+(((leftDay-firstDay+colCnt)%colCnt)*dis+dit)+') div div').position().left + axisWidth; + left = bg.find('td:eq('+(((leftDay-Math.max(firstDay,nwe)+colCnt)%colCnt)*dis+dit)+') div div').position().left + axisWidth; }else{ left = axisWidth; } if (rightRounded) { className += 'fc-corner-right '; - right = bg.find('td:eq('+(((rightDay-firstDay+colCnt)%colCnt)*dis+dit)+') div div'); + right = bg.find('td:eq('+(((rightDay-Math.max(firstDay,nwe)+colCnt)%colCnt)*dis+dit)+') div div'); right = right.position().left + right.width() + axisWidth; }else{ right = axisWidth + bg.width(); diff --git a/src/gcal.js b/src/gcal.js index 66e3e22..3a0b9b4 100755 --- a/src/gcal.js +++ b/src/gcal.js @@ -16,49 +16,50 @@ options = options || {}; return function(start, end, callback) { - $.getJSON(feedUrl + "?alt=json-in-script&callback=?", - { - 'start-min': $.fullCalendar.formatDate(start, 'u'), - 'start-max': $.fullCalendar.formatDate(end, 'u'), - 'singleevents': true, - 'max-results': 9999 - }, - function(data) { - var events = []; - if (data.feed.entry) { - $.each(data.feed.entry, function(i, entry) { - var startStr = entry['gd$when'][0]['startTime'], - start = $.fullCalendar.parseDate(startStr), - end = $.fullCalendar.parseDate(entry['gd$when'][0]['endTime']), - allDay = startStr.indexOf('T') == -1, - classNames = [], - url; - $.each(entry.link, function() { - if (this.type == 'text/html') { - url = this.href; - } - }); - if (allDay) { - end = new Date(end - 1); // make inclusive + var params = { + 'start-min': $.fullCalendar.formatDate(start, 'u'), + 'start-max': $.fullCalendar.formatDate(end, 'u'), + 'singleevents': true, + 'max-results': 9999 + }; + if (options.currentTimezone) { + params.ctz = options.currentTimezone.replace(' ', '_'); + } + $.getJSON(feedUrl + "?alt=json-in-script&callback=?", params, function(data) { + var events = []; + if (data.feed.entry) { + $.each(data.feed.entry, function(i, entry) { + var startStr = entry['gd$when'][0]['startTime'], + start = $.fullCalendar.parseISO8601(startStr, true), + end = $.fullCalendar.parseISO8601(entry['gd$when'][0]['endTime'], true), + allDay = startStr.indexOf('T') == -1, + url; + $.each(entry.link, function() { + if (this.type == 'text/html') { + url = this.href; } - events.push({ - id: entry['gCal$uid']['value'], - title: entry['title']['$t'], - url: url, - start: $.fullCalendar.parseDate(entry['gd$when'][0]['startTime']), - end: end, - allDay: allDay, - location: entry['gd$where'][0]['valueString'], - description: entry['content']['$t'], - className: options.className, - editable: options.editable || false - }); }); - } - callback(events); - }); + if (allDay) { + $.fullCalendar.addDays(end, -1); // make inclusive + } + events.push({ + id: entry['gCal$uid']['value'], + title: entry['title']['$t'], + url: url, + start: start, + end: end, + allDay: allDay, + location: entry['gd$where'][0]['valueString'], + description: entry['content']['$t'], + className: options.className, + editable: options.editable || false + }); + }); + } + callback(events); + }); } } -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/src/grid.js b/src/grid.js index 4824827..1f10c94 100755 --- a/src/grid.js +++ b/src/grid.js @@ -13,22 +13,39 @@ views.month = function(element, options) { addMonths(date, delta); date.setDate(1); } + // start/end var start = this.start = cloneDate(date, true); start.setDate(1); - this.title = formatDates( + this.end = addMonths(cloneDate(start), 1); + // visStart/visEnd + var visStart = this.visStart = cloneDate(start), + visEnd = this.visEnd = cloneDate(this.end), + nwe = options.weekends ? 0 : 1; + if (nwe) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } + addDays(visStart, -((visStart.getDay() - Math.max(options.firstDay, nwe) + 7) % 7)); + addDays(visEnd, (7 - visEnd.getDay() + Math.max(options.firstDay, nwe)) % 7); + // row count + var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7)); + if (options.weekMode == 'fixed') { + addDays(visEnd, (6 - rowCnt) * 7); + rowCnt = 6; + } + // title + this.title = formatDate( start, - addDays(cloneDate(this.end = addMonths(cloneDate(start), 1)), -1), this.option('titleFormat'), options ); - addDays(this.visStart = cloneDate(start), -((start.getDay() - options.firstDay + 7) % 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)); - if (options.weekMode == 'fixed') { - addDays(this.visEnd, (6 - rowCnt) * 7); - rowCnt = 6; - } - this.renderGrid(rowCnt, 7, this.option('columnFormat'), true, fetchEvents); + // render + this.renderGrid( + rowCnt, options.weekends ? 7 : 5, + this.option('columnFormat'), + true, + fetchEvents + ); } }); } @@ -39,13 +56,28 @@ views.basicWeek = function(element, options) { if (delta) { addDays(date, delta * 7); } + var visStart = this.visStart = cloneDate( + this.start = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)) + ), + visEnd = this.visEnd = cloneDate( + this.end = addDays(cloneDate(visStart), 7) + ); + if (!options.weekends) { + skipWeekend(visStart); + skipWeekend(visEnd, -1, true); + } this.title = formatDates( - 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), + visStart, + addDays(cloneDate(visEnd), -1), this.option('titleFormat'), options ); - this.renderGrid(1, 7, this.option('columnFormat'), false, fetchEvents); + this.renderGrid( + 1, options.weekends ? 7 : 5, + this.option('columnFormat'), + false, + fetchEvents + ); } }); }; @@ -55,6 +87,9 @@ views.basicDay = function(element, options) { render: function(date, delta, fetchEvents) { if (delta) { addDays(date, delta); + if (!options.weekends) { + skipWeekend(date, delta < 0 ? -1 : 1); + } } this.title = formatDate(date, this.option('titleFormat'), options); this.start = this.visStart = cloneDate(date, true); @@ -73,6 +108,7 @@ var tdHeightBug, rtlLeftDiff; function Grid(element, options, methods) { var tm, firstDay, + nwe, // no weekends (int) rtl, dis, dit, // day index sign / translate rowCnt, colCnt, colWidth, @@ -115,7 +151,8 @@ function Grid(element, options, methods) { colCnt = c; // update option-derived variables - tm = options.theme ? 'ui' : 'fc'; + tm = options.theme ? 'ui' : 'fc'; + nwe = options.weekends ? 0 : 1; firstDay = options.firstDay; if (rtl = options.isRTL) { dis = -1; @@ -141,6 +178,9 @@ function Grid(element, options, methods) { (i==dit ? ' fc-leftmost' : '') + "'>" + formatDate(d, colFormat, options) + ""; addDays(d, 1); + if (nwe) { + skipWeekend(d); + } } thead = $(s + "").appendTo(table); @@ -160,6 +200,9 @@ function Grid(element, options, methods) { (showNumbers ? "
" + d.getDate() + "
" : '') + "
 
"; addDays(d, 1); + if (nwe) { + skipWeekend(d); + } } s += ""; } @@ -187,6 +230,9 @@ function Grid(element, options, methods) { "
 
" + ""; addDays(d, 1); + if (nwe) { + skipWeekend(d); + } } s += ""; } @@ -216,6 +262,9 @@ function Grid(element, options, methods) { } td.find('div.fc-day-number').text(d.getDate()); addDays(d, 1); + if (nwe) { + skipWeekend(d); + } }); if (rowCnt == 1) { // more changes likely (week or day view) @@ -226,6 +275,9 @@ function Grid(element, options, methods) { $(this).text(formatDate(d, colFormat, options)); this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); + if (nwe) { + skipWeekend(d); + } }); // redo cell day-of-weeks @@ -233,6 +285,9 @@ function Grid(element, options, methods) { tbody.find('td').each(function() { this.className = this.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]); addDays(d, 1); + if (nwe) { + skipWeekend(d); + } }); } @@ -246,10 +301,11 @@ function Grid(element, options, methods) { function dayClick(ev) { - var date = addDays( - cloneDate(view.visStart), - parseInt(this.className.match(/fc\-day(\d+)/)[1]) - ); + var n = parseInt(this.className.match(/fc\-day(\d+)/)[1]), + date = addDays( + cloneDate(view.visStart), + Math.floor(n/colCnt) * 7 + n % colCnt + ); view.trigger('dayClick', this, date, true, ev); } @@ -359,10 +415,10 @@ function Grid(element, options, methods) { event = seg.event; className = 'fc-event fc-event-hori '; startElm = seg.isStart ? - tr.find('td:eq('+((seg.start.getDay()-firstDay+colCnt)%colCnt)+') div div') : + tr.find('td:eq('+((seg.start.getDay()-Math.max(firstDay,nwe)+colCnt)%colCnt)+') div div') : tbody; endElm = seg.isEnd ? - tr.find('td:eq('+((seg.end.getDay()-firstDay+colCnt-1)%colCnt)+') div div') : + tr.find('td:eq('+((seg.end.getDay()-Math.max(firstDay,nwe)+colCnt-1)%colCnt)+') div div') : tbody; if (rtl) { left = endElm.position().left; diff --git a/src/main.js b/src/main.js index 52fcbbe..9502277 100755 --- a/src/main.js +++ b/src/main.js @@ -16,6 +16,7 @@ var defaults = { center: '', right: 'today prev,next' }, + weekends: true, // editing //editable: false, @@ -159,10 +160,14 @@ $.fn.fullCalendar = function(options) { var date = new Date(), viewName, view, // the current view viewInstances = {}; - if (options.year != undefined) { - date.setYear(options.year); + + if (options.year != undefined && options.year != date.getFullYear()) { + date.setDate(1); + date.setMonth(0); + date.setFullYear(options.year); } - if (options.month != undefined) { + if (options.month != undefined && options.month != date.getMonth()) { + date.setDate(1); date.setMonth(options.month); } if (options.date != undefined) { @@ -396,7 +401,7 @@ $.fn.fullCalendar = function(options) { date = cloneDate(year); // provided 1 argument, a Date }else{ if (year != undefined) { - date.setYear(year); + date.setFullYear(year); } if (month != undefined) { date.setMonth(month); diff --git a/src/util.js b/src/util.js index 6a08185..8a3cb89 100755 --- a/src/util.js +++ b/src/util.js @@ -47,6 +47,7 @@ function addDays(d, n, keepTime) { // deals with daylight savings } return d; } +fc.addDays = addDays; function addMinutes(d, n) { d.setMinutes(d.getMinutes() + n); @@ -68,6 +69,22 @@ function cloneDate(d, dontKeepTime) { return new Date(+d); } +function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1 + var i=0, d; + do { + d = new Date(1970, i++, 1); + } while (d.getHours() != 0); + return d; +} + +function skipWeekend(date, inc, excl) { + inc = inc || 1; + while (date.getDay()==0 || (excl && date.getDay()==1 || !excl && date.getDay()==6)) { + addDays(date, inc); + } + return date; +} + /* Date Parsing diff --git a/tests/default.html b/tests/default.html index 9599873..13e6a84 100755 --- a/tests/default.html +++ b/tests/default.html @@ -89,4 +89,4 @@
- \ No newline at end of file + diff --git a/tests/gcal.html b/tests/gcal.html index 1a1dad2..1d3dba0 100755 --- a/tests/gcal.html +++ b/tests/gcal.html @@ -4,13 +4,9 @@