diff --git a/src/agenda.js b/src/agenda.js index 7eb2243..1754054 100755 --- a/src/agenda.js +++ b/src/agenda.js @@ -5,9 +5,9 @@ setDefaults({ allDayHeader: true, allDayText: 'all-day', + firstHour: 6, slotMinutes: 30, defaultEventMinutes: 120, - defaultScrollHour: 6, axisFormat: 'h(:mm)tt', timeFormat: { agenda: 'h:mm{ - h:mm}' @@ -52,7 +52,7 @@ function Agenda(element, options, methods) { var head, body, bodyContent, bodyTable, bg, colCnt, - timeWidth, colWidth, rowHeight, // todo: timeWidth -> axisWidth, rowHeight->slotHeight ? + axisWidth, colWidth, slotHeight, // todo: axisWidth -> axisWidth, slotHeight->slotHeight ? cachedDaySegs, cachedSlotSegs, tm, firstDay, rtl, dis, dit, // day index sign / translate @@ -220,7 +220,7 @@ function Agenda(element, options, methods) { updateSize(); - scrollDate.setHours(options.defaultScrollHour); + scrollDate.setHours(options.firstHour); body.scrollTop(timePosition(d0, scrollDate) + 1); // +1 for the border fetchEvents(renderEvents); @@ -243,31 +243,31 @@ function Agenda(element, options, methods) { bodyTable.width(contentWidth); // time-axis width - timeWidth = 0; + axisWidth = 0; setOuterWidth( head.find('tr:lt(2) th:first').add(body.find('tr:first th')) .width('') .each(function() { - timeWidth = Math.max(timeWidth, $(this).outerWidth()); + axisWidth = Math.max(axisWidth, $(this).outerWidth()); }) .add(stripeTDs.eq(0)), - timeWidth + axisWidth ); // column width - colWidth = Math.floor((contentWidth - timeWidth) / colCnt); + colWidth = Math.floor((contentWidth - axisWidth) / colCnt); setOuterWidth(stripeTDs.slice(0, -1), colWidth); setOuterWidth(topTDs.slice(1, -2), colWidth); - setOuterWidth(topTDs.slice(-2, -1), contentWidth - timeWidth - colWidth*(colCnt-1)); + setOuterWidth(topTDs.slice(-2, -1), contentWidth - axisWidth - colWidth*(colCnt-1)); bg.css({ top: head.find('tr').height(), - left: timeWidth, - width: contentWidth - timeWidth, + left: axisWidth, + width: contentWidth - axisWidth, height: element.height() }); - rowHeight = body.find('tr:eq(1)').height(); // use second, first prob doesn't have a border + slotHeight = body.find('tr:first div').height() + 1; } function slotClick(ev) { @@ -345,8 +345,9 @@ function Agenda(element, options, methods) { if (options.allDayHeader) { var td = head.find('td'), tdInner = td.find('div div'), - top = tdInner.position().top, - rowHeight = 0, + tr = td.parent(), + top = safePosition(tdInner, td, tr, tr.parent()).top, + rowContentHeight = 0, i, len=segRow.length, level, levelHeight, j, seg, @@ -377,16 +378,16 @@ 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 + timeWidth; + left = bg.find('td:eq('+(((leftDay-firstDay+colCnt)%colCnt)*dis+dit)+') div div').position().left + axisWidth; }else{ - left = timeWidth; + left = axisWidth; } if (rightRounded) { className += 'fc-corner-right '; right = bg.find('td:eq('+(((rightDay-firstDay+colCnt)%colCnt)*dis+dit)+') div div'); - right = right.position().left + right.width() + timeWidth; + right = right.position().left + right.width() + axisWidth; }else{ - right = timeWidth + bg.width(); + right = axisWidth + bg.width(); } eventElement = $("
") .append(anchorElement = $("") @@ -421,9 +422,9 @@ function Agenda(element, options, methods) { } } top += levelHeight; - rowHeight += levelHeight; + rowContentHeight += levelHeight; } - tdInner.height(rowHeight); + tdInner.height(rowContentHeight); updateSize(); // tdInner might have pushed the body down, so resize } } @@ -468,7 +469,7 @@ function Agenda(element, options, methods) { width = availWidth * .96; } } - left = timeWidth + tdInner.position().left + // leftmost possible + left = axisWidth + tdInner.position().left + // leftmost possible (availWidth / (levelI + forward + 1) * levelI) // indentation * dis + (rtl ? availWidth - width : 0); // rtl className = 'fc-event fc-event-vert '; @@ -480,16 +481,10 @@ function Agenda(element, options, methods) { } eventElement = $("
") .append(anchorElement = $("") + .append(timeElement = $("") + .text(formatDates(event.start, event.end, view.option('timeFormat')))) .append(titleElement = $("") .text(event.title))) - if (seg.isStart) { - // add the time header - anchorElement - .prepend(timeElement = $("") - .text(formatDates(event.start, event.end, view.option('timeFormat')))) - }else{ - timeElement = null; - } if (event.url) { anchorElement.attr('href', event.url); } @@ -508,7 +503,7 @@ function Agenda(element, options, methods) { .appendTo(bodyContent); setOuterWidth(eventElement, width, true); setOuterHeight(eventElement, bottom-top, true); - if (timeElement && eventElement.height() - titleElement.position().top < 10) { + if (eventElement.height() - titleElement.position().top < 10) { // event title doesn't have enough room, put next to the time timeElement.text(formatDate(event.start, view.option('timeFormat')) + ' - ' + event.title); titleElement.remove(); @@ -572,7 +567,7 @@ function Agenda(element, options, methods) { // convert event to temporary slot-event setOuterHeight( eventElement.width(colWidth - 10), // don't use entire width - rowHeight * Math.round( + slotHeight * Math.round( (event.end ? ((event.end - event.start)/MINUTE_MS) : options.defaultEventMinutes) /options.slotMinutes) ); @@ -607,13 +602,16 @@ function Agenda(element, options, methods) { if (!cell || !dayDelta && !cell.rowDelta) { // over nothing (has reverted) resetElement(); + if ($.browser.msie) { + eventElement.css('filter', ''); // clear IE opacity side-effects + } view.showEvents(event, eventElement); }else{ - //eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link + eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link view.eventDrop( this, event, dayDelta, allDay ? 0 : // minute delta - Math.round((eventElement.offset().top - bodyContent.offset().top) / rowHeight) + Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight) * options.slotMinutes - (event.start.getHours() * 60 + event.start.getMinutes()), allDay, ev, ui @@ -638,7 +636,7 @@ function Agenda(element, options, methods) { eventElement.draggable({ zIndex: 9, scroll: false, - grid: [colWidth, rowHeight], + grid: [colWidth, slotHeight], axis: colCnt==1 ? 'y' : false, opacity: view.option('dragOpacity'), revertDuration: options.dragRevertDuration, @@ -652,10 +650,8 @@ function Agenda(element, options, methods) { resetElement = function() { // convert back to original slot-event if (allDay) { - if (timeElement) { - timeElement.css('display', ''); // show() was causing display=inline - } - eventElement.draggable('option', 'grid', [colWidth, rowHeight]); + timeElement.css('display', ''); // show() was causing display=inline + eventElement.draggable('option', 'grid', [colWidth, slotHeight]); allDay = false; } }; @@ -667,9 +663,7 @@ function Agenda(element, options, methods) { if (!allDay) { // convert to temporary all-day event allDay = true; - if (timeElement) { - timeElement.hide(); - } + timeElement.hide(); eventElement.draggable('option', 'grid', null); } view.showOverlay(cell); @@ -691,9 +685,9 @@ function Agenda(element, options, methods) { matrix.mouse(ev.pageX, ev.pageY); }, drag: function(ev, ui) { - slotDelta = Math.round((ui.position.top - origPosition.top) / rowHeight); + slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight); if (slotDelta != prevSlotDelta) { - if (timeElement && !allDay) { + if (!allDay) { // update time header var minuteDelta = slotDelta*options.slotMinutes, newStart = addMinutes(cloneDate(event.start), minuteDelta), @@ -726,7 +720,6 @@ function Agenda(element, options, methods) { eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position view.showEvents(event, eventElement); }else{ - //TODO: eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link view.eventDrop( this, event, dayDelta, allDay ? 0 : slotDelta * options.slotMinutes, // minute delta @@ -752,7 +745,7 @@ function Agenda(element, options, methods) { eventElement .resizable({ handles: 's', - grid: rowHeight, + grid: slotHeight, start: function(ev, ui) { slotDelta = prevSlotDelta = 0; view.hideEvents(event, eventElement); @@ -764,18 +757,16 @@ function Agenda(element, options, methods) { }, resize: function(ev, ui) { // don't rely on ui.size.height, doesn't take grid into account - slotDelta = Math.round((Math.max(rowHeight, eventElement.height()) - ui.originalSize.height) / rowHeight); + slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight); if (slotDelta != prevSlotDelta) { - if (timeElement) { - timeElement.text( - formatDates( - event.start, - (!slotDelta && !event.end) ? null : // no change, so don't display time range - addMinutes(view.eventEnd(event), options.slotMinutes*slotDelta), - view.option('timeFormat') - ) - ); - } + timeElement.text( + formatDates( + event.start, + (!slotDelta && !event.end) ? null : // no change, so don't display time range + addMinutes(view.eventEnd(event), options.slotMinutes*slotDelta), + view.option('timeFormat') + ) + ); prevSlotDelta = slotDelta; } }, @@ -812,8 +803,12 @@ function Agenda(element, options, methods) { var slotMinutes = options.slotMinutes, minutes = time.getHours()*60 + time.getMinutes(), slotI = Math.floor(minutes / slotMinutes), - innerDiv = body.find('tr:eq(' + slotI + ') td div'); - return Math.max(0, Math.round(innerDiv.position().top - 1 + rowHeight * ((minutes % slotMinutes) / slotMinutes))); + tr = body.find('tr:eq(' + slotI + ')'), + td = tr.find('td'), + innerDiv = td.find('div'); + return Math.max(0, Math.round( + safePosition(innerDiv, td, tr, tr.parent()).top - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) + )); } } diff --git a/src/css/main.css b/src/css/main.css index 021b340..cfa737c 100755 --- a/src/css/main.css +++ b/src/css/main.css @@ -126,6 +126,10 @@ table.fc-header { .fc-header .fc-no-right { padding-right: 0; + } + +.fc-header .fc-no-right a { + margin-right: 0; border-right: 0; } diff --git a/src/grid.js b/src/grid.js index 009edc8..fe2d614 100755 --- a/src/grid.js +++ b/src/grid.js @@ -67,9 +67,7 @@ views.basicDay = function(element, options) { // rendering bugs -var tdTopBug, trTopBug, tbodyTopBug, - tdHeightBug, - rtlLeftDiff; +var tdHeightBug, rtlLeftDiff; function Grid(element, options, methods) { @@ -269,21 +267,13 @@ function Grid(element, options, methods) { rowHeight1 = Math.floor(tbodyHeight / rowCnt); rowHeight2 = tbodyHeight - rowHeight1*(rowCnt-1); } - - if (tdTopBug == undefined) { - // nasty bugs in opera 9.25 - // position() returning relative to direct parent - var tr = tbody.find('tr:first'), - td = tr.find('td:first'), - trTop = tr.position().top, - tdTop = td.position().top; - tdTopBug = tdTop < 0; - trTopBug = trTop != tdTop; - tbodyTopBug = tbody.position().top != trTop; - } + + reportTBody(tbody); if (tdHeightBug == undefined) { // bug in firefox where cell height includes padding + var tr = tbody.find('tr:first'), + td = tr.find('td:first'); td.height(rowHeight1); tdHeightBug = rowHeight1 != td.height(); } @@ -344,7 +334,7 @@ function Grid(element, options, methods) { tr, td, innerDiv, top, - weekHeight, + rowContentHeight, j, segs, levelHeight, k, seg, @@ -359,17 +349,8 @@ function Grid(element, options, methods) { tr = tbody.find('tr:eq('+i+')'); td = tr.find('td:first'); innerDiv = td.find('div.fc-day-content div').css('position', 'relative'); - top = innerDiv.position().top; - if (tdTopBug) { - top -= td.position().top; - } - if (trTopBug) { - top += tr.position().top; - } - if (tbodyTopBug) { - top += tbody.position().top; - } - weekHeight = 0; + top = safePosition(innerDiv, td, tr, tbody).top; + rowContentHeight = 0; for (j=0; j= view.start && today < view.end) { - header.find('div.fc-button-today').addClass(tm + '-state-disabled'); - }else{ - header.find('div.fc-button-today').removeClass(tm + '-state-disabled'); - } - } } else if (view.sizeDirty) { view.updateSize(); @@ -233,6 +224,13 @@ $.fn.fullCalendar = function(options) { if (header) { // update title text header.find('h2.fc-header-title').html(view.title); + // enable/disable 'today' button + var today = new Date(); + if (today >= view.start && today < view.end) { + header.find('div.fc-button-today').addClass(tm + '-state-disabled'); + }else{ + header.find('div.fc-button-today').removeClass(tm + '-state-disabled'); + } } view.sizeDirty = false; view.eventsDirty = false; @@ -574,8 +572,8 @@ $.fn.fullCalendar = function(options) { prevButton.addClass(tm + '-no-right'); } var button, - icon = options.theme ? viewOption(options, 'buttonIcons', buttonName) : null, - text = viewOption(options, 'buttonText', buttonName); + icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null, + text = smartProperty(options.buttonText, buttonName); if (icon) { button = $("
" + "
"); @@ -586,7 +584,11 @@ $.fn.fullCalendar = function(options) { } if (button) { button - .click(buttonClick) + .click(function() { + if (!button.hasClass(tm + '-state-disabled')) { + buttonClick(); + } + }) .mousedown(function() { button.addClass(tm + '-state-down'); }) @@ -689,29 +691,6 @@ $.fn.fullCalendar = function(options) { -// TODO: rename - -function viewOption(options, property, viewName) { - var v = options[property]; - if (typeof v == 'object') { - if (v[viewName] != undefined) { - return v[viewName]; - } - var parts = viewName.split(/(?=[A-Z])/), - i=parts.length-1, res; - for (; i>=0; i--) { - res = v[parts[i].toLowerCase()]; - if (res != undefined) { - return res; - } - } - return v['']; - } - return v; -} - - - /* Important Event Utilities -----------------------------------------------------------------------------*/ diff --git a/src/util.js b/src/util.js index 2e1f8d5..d9255a8 100755 --- a/src/util.js +++ b/src/util.js @@ -270,6 +270,29 @@ function setOuterHeight(element, height, includeMargins) { +/* Position Calculation +-----------------------------------------------------------------------------*/ +// nasty bugs in opera 9.25 +// position() returning relative to direct parent + +var operaPositionBug; + +function reportTBody(tbody) { + if (operaPositionBug == undefined) { + operaPositionBug = tbody.position().top != tbody.find('tr').position().top; + } +} + +function safePosition(element, td, tr, tbody) { + var position = element.position(); + if (operaPositionBug) { + position.top += tbody.position().top + tr.position().top - td.position().top; + } + return position; +} + + + /* Hover Matrix -----------------------------------------------------------------------------*/ @@ -282,7 +305,9 @@ function HoverMatrix(changeCallback) { this.row = function(e, topBug) { prevRowE = $(e); - tops.push(prevRowE.offset().top + (topBug ? prevRowE.parent().position().top : 0)); + tops.push(prevRowE.offset().top + ( + (operaPositionBug && prevRowE.is('tr')) ? prevRowE.parent().position().top : 0 + )); }; this.col = function(e) { @@ -341,3 +366,19 @@ function zeroPad(n) { return (n < 10 ? '0' : '') + n; } +function smartProperty(obj, name) { // get a camel-cased/namespaced property + if (obj[name] != undefined) { + return obj[name]; + } + var parts = name.split(/(?=[A-Z])/), + i=parts.length-1, res; + for (; i>=0; i--) { + res = obj[parts[i].toLowerCase()]; + if (res != undefined) { + return res; + } + } + return obj['']; +} + + diff --git a/src/view.js b/src/view.js index dc38f83..59b53cc 100755 --- a/src/view.js +++ b/src/view.js @@ -262,7 +262,11 @@ var viewMethods = { // get a property from the 'options' object, using smart view naming option: function(name, viewName) { - return viewOption(this.options, name, this.name || viewName); + var v = this.options[name]; + if (typeof v == 'object') { + return smartProperty(v, viewName || this.name); + } + return v; }, diff --git a/tests/basic.html b/tests/basic.html index 1a6ca17..2502c87 100755 --- a/tests/basic.html +++ b/tests/basic.html @@ -13,7 +13,7 @@ $('#calendar').fullCalendar({ header: { - left: 'prevYear,prev,next,nextYear today', + left: 'prev,next today', right: 'month,agendaWeek,basicWeek,agendaDay,basicDay' }, editable: true,