function AgendaEventRenderer() { var t = this; // exports t.renderEvents = renderEvents; t.rerenderEvents = rerenderEvents; t.clearEvents = clearEvents; t.slotSegHtml = slotSegHtml; t.bindDaySeg = bindDaySeg; // imports DayEventRenderer.call(t); var opt = t.opt; var trigger = t.trigger; var eventEnd = t.eventEnd; var reportEvents = t.reportEvents; var clearEventData = t.clearEventData; var eventElementHandlers = t.eventElementHandlers; var setHeight = t.setHeight; var getDaySegmentContainer = t.getDaySegmentContainer; var getSlotSegmentContainer = t.getSlotSegmentContainer; var getHoverListener = t.getHoverListener; var getMaxMinute = t.getMaxMinute; var getMinMinute = t.getMinMinute; var timePosition = t.timePosition; var colContentLeft = t.colContentLeft; var colContentRight = t.colContentRight; var renderDaySegs = t.renderDaySegs; var resizableDayEvent = t.resizableDayEvent; // TODO: streamline binding architecture var getColCnt = t.getColCnt; var getColWidth = t.getColWidth; var getSlotHeight = t.getSlotHeight; var getBodyContent = t.getBodyContent; var reportEventElement = t.reportEventElement; var showEvents = t.showEvents; var hideEvents = t.hideEvents; var eventDrop = t.eventDrop; var eventResize = t.eventResize; var renderDayOverlay = t.renderDayOverlay; var clearOverlays = t.clearOverlays; var calendar = t.calendar; var formatDate = calendar.formatDate; var formatDates = calendar.formatDates; /* Rendering ----------------------------------------------------------------------------*/ var cachedEvents = []; function renderEvents(events, modifiedEventId) { reportEvents(cachedEvents = events); var i, len=events.length, dayEvents=[], slotEvents=[]; for (i=0; i" + "" + "" + "" + htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + "" + "" + htmlEscape(event.title) + "" + "" + ((event.editable || event.editable === undefined && opt('editable')) && !opt('disableResizing') && $.fn.resizable ? "
=
" : '') + ""; } function bindDaySeg(event, eventElement, seg) { eventElementHandlers(event, eventElement); if (event.editable || event.editable === undefined && opt('editable')) { draggableDayEvent(event, eventElement, seg.isStart); if (seg.isEnd) { resizableDayEvent(event, eventElement, getColWidth()); } } } function bindSlotSeg(event, eventElement, seg) { eventElementHandlers(event, eventElement); if (event.editable || event.editable === undefined && opt('editable')) { var timeElement = eventElement.find('span.fc-event-time'); draggableSlotEvent(event, eventElement, timeElement); if (seg.isEnd) { resizableSlotEvent(event, eventElement, timeElement); } } } /* Dragging -----------------------------------------------------------------------------------*/ // when event starts out FULL-DAY function draggableDayEvent(event, eventElement, isStart) { if (!opt('disableDragging') && eventElement.draggable) { var origWidth; var allDay=true; var dayDelta; var dis = opt('isRTL') ? -1 : 1; var hoverListener = getHoverListener(); var colWidth = getColWidth(); var slotHeight = getSlotHeight(); var minMinute = getMinMinute(); eventElement.draggable({ zIndex: 9, opacity: opt('dragOpacity', 'month'), // use whatever the month view was using revertDuration: opt('dragRevertDuration'), start: function(ev, ui) { trigger('eventDragStart', eventElement, event, ev, ui); hideEvents(event, eventElement); origWidth = eventElement.width(); hoverListener.start(function(cell, origCell, rowDelta, colDelta) { eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta); clearOverlays(); if (cell) { dayDelta = colDelta * dis; if (!cell.row) { // on full-days renderDayOverlay( addDays(cloneDate(event.start), dayDelta), addDays(exclEndDay(event), dayDelta) ); resetElement(); }else{ // mouse is over bottom slots if (isStart && allDay) { // convert event to temporary slot-event setOuterHeight( eventElement.width(colWidth - 10), // don't use entire width slotHeight * Math.round( (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) / opt('slotMinutes') ) ); eventElement.draggable('option', 'grid', [colWidth, 1]); allDay = false; } } } }, ev, 'drag'); }, stop: function(ev, ui) { var cell = hoverListener.stop(); clearOverlays(); trigger('eventDragStop', eventElement, event, ev, ui); if (cell && (!allDay || dayDelta)) { // changed! eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link var minuteDelta = 0; if (!allDay) { minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight) * opt('slotMinutes') + minMinute - (event.start.getHours() * 60 + event.start.getMinutes()); } eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui); }else{ // hasn't moved or is out of bounds (draggable has already reverted) resetElement(); if ($.browser.msie) { eventElement.css('filter', ''); // clear IE opacity side-effects } showEvents(event, eventElement); } } }); function resetElement() { if (!allDay) { eventElement .width(origWidth) .height('') .draggable('option', 'grid', null); allDay = true; } } } } // when event starts out IN TIMESLOTS function draggableSlotEvent(event, eventElement, timeElement) { if (!opt('disableDragging') && eventElement.draggable) { var origPosition; var allDay=false; var dayDelta; var minuteDelta; var prevMinuteDelta; var dis = opt('isRTL') ? -1 : 1; var hoverListener = getHoverListener(); var colCnt = getColCnt(); var colWidth = getColWidth(); var slotHeight = getSlotHeight(); eventElement.draggable({ zIndex: 9, scroll: false, grid: [colWidth, slotHeight], axis: colCnt==1 ? 'y' : false, opacity: opt('dragOpacity'), revertDuration: opt('dragRevertDuration'), start: function(ev, ui) { trigger('eventDragStart', eventElement, event, ev, ui); hideEvents(event, eventElement); if ($.browser.msie) { eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide } origPosition = eventElement.position(); minuteDelta = prevMinuteDelta = 0; hoverListener.start(function(cell, origCell, rowDelta, colDelta) { eventElement.draggable('option', 'revert', !cell); clearOverlays(); if (cell) { dayDelta = colDelta * dis; if (opt('allDaySlot') && !cell.row) { // over full days if (!allDay) { // convert to temporary all-day event allDay = true; timeElement.hide(); eventElement.draggable('option', 'grid', null); } renderDayOverlay( addDays(cloneDate(event.start), dayDelta), addDays(exclEndDay(event), dayDelta) ); }else{ // on slots resetElement(); } } }, ev, 'drag'); }, drag: function(ev, ui) { minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * opt('slotMinutes'); if (minuteDelta != prevMinuteDelta) { if (!allDay) { updateTimeText(minuteDelta); } prevMinuteDelta = minuteDelta; } }, stop: function(ev, ui) { var cell = hoverListener.stop(); clearOverlays(); trigger('eventDragStop', eventElement, event, ev, ui); if (cell && (dayDelta || minuteDelta || allDay)) { // changed! eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui); }else{ // either no change or out-of-bounds (draggable has already reverted) resetElement(); eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position updateTimeText(0); if ($.browser.msie) { eventElement .css('filter', '') // clear IE opacity side-effects .find('span.fc-event-bg') .css('display', ''); // .show() made display=inline } showEvents(event, eventElement); } } }); function updateTimeText(minuteDelta) { var newStart = addMinutes(cloneDate(event.start), minuteDelta); var newEnd; if (event.end) { newEnd = addMinutes(cloneDate(event.end), minuteDelta); } timeElement.text(formatDates(newStart, newEnd, opt('timeFormat'))); } function resetElement() { // convert back to original slot-event if (allDay) { timeElement.css('display', ''); // show() was causing display=inline eventElement.draggable('option', 'grid', [colWidth, slotHeight]); allDay = false; } } } } /* Resizing --------------------------------------------------------------------------------------*/ function resizableSlotEvent(event, eventElement, timeElement) { if (!opt('disableResizing') && eventElement.resizable) { var slotDelta, prevSlotDelta; var slotHeight = getSlotHeight(); eventElement.resizable({ handles: { s: 'div.ui-resizable-s' }, grid: slotHeight, start: function(ev, ui) { slotDelta = prevSlotDelta = 0; hideEvents(event, eventElement); if ($.browser.msie && $.browser.version == '6.0') { eventElement.css('overflow', 'hidden'); } eventElement.css('z-index', 9); trigger('eventResizeStart', this, event, ev, ui); }, resize: function(ev, ui) { // don't rely on ui.size.height, doesn't take grid into account slotDelta = Math.round((Math.max(slotHeight, eventElement.height()) - ui.originalSize.height) / slotHeight); if (slotDelta != prevSlotDelta) { timeElement.text( formatDates( event.start, (!slotDelta && !event.end) ? null : // no change, so don't display time range addMinutes(eventEnd(event), opt('slotMinutes')*slotDelta), opt('timeFormat') ) ); prevSlotDelta = slotDelta; } }, stop: function(ev, ui) { trigger('eventResizeStop', this, event, ev, ui); if (slotDelta) { eventResize(this, event, 0, opt('slotMinutes')*slotDelta, ev, ui); }else{ eventElement.css('z-index', 8); showEvents(event, eventElement); // BUG: if event was really short, need to put title back in span } } }); } } } function countForwardSegs(levels) { var i, j, k, level, segForward, segBack; for (i=levels.length-1; i>0; i--) { level = levels[i]; for (j=0; j