diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..51227d7 Binary files /dev/null and b/.DS_Store differ diff --git a/src/Calendar.js b/src/Calendar.js index b9fa775..35baf0a 100644 --- a/src/Calendar.js +++ b/src/Calendar.js @@ -35,10 +35,7 @@ function Calendar(element, options, eventSources) { var isFetchNeeded = t.isFetchNeeded; var fetchEvents = t.fetchEvents; - var resourceSources; - ResourceManager.call(t, options, resourceSources); - //var fetchResources = t.fetchResources; - + ResourceManager.call(t, options); // locals var _element = element[0]; diff --git a/src/ResourceManager.js b/src/ResourceManager.js index 58ea92a..eb0f121 100644 --- a/src/ResourceManager.js +++ b/src/ResourceManager.js @@ -1,23 +1,16 @@ function ResourceManager(options) { - var t = this; - - // exports + var t = this; + // exports t.fetchResources = fetchResources; - - // locals - var resources = []; - var cache = []; - - _addResourceSources(options.resources); - - /** - * ---------------------------------------------------------------- - * Categorize and add the provided sources - * ---------------------------------------------------------------- - */ - function _addResourceSources(sources) { + // locals + var resourceSources = []; + var cache; + // initialize the resources. + addResourceSources(options.resources); + // add the resource sources + + function addResourceSources(sources) { var resource = {}; - if ($.isFunction(sources)) { // is it a function? resource = { @@ -27,100 +20,97 @@ function ResourceManager(options) { } else if (typeof sources == 'string') { // is it a URL string? resource = { - url: sources + url: sources }; resources.push(resource); } else if (typeof sources == 'object') { // is it json object? - for (var i=0; i= 0) { + resourceEvents.push(events[i]) + } + } + return resourceEvents; + } - d = cellToDate(0, i); - addMinutes(d, minMinute); + function sliceSegs(events, visEventEnds, start, end) { + var segs = [], + i, len = events.length, + event, + eventStart, eventEnd, + segStart, segEnd, + isStart, isEnd; + for (i = 0; i < len; i++) { + event = events[i]; + eventStart = event.start; + eventEnd = visEventEnds[i]; + if (eventEnd > start && eventStart < end) { + if (eventStart < start) { + segStart = cloneDate(start); + isStart = false; + } else { + segStart = eventStart; + isStart = true; + } + if (eventEnd > end) { + segEnd = cloneDate(end); + isEnd = false; + } else { + segEnd = eventEnd; + isEnd = true; + } + segs.push({ + event: event, + start: segStart, + end: segEnd, + isStart: isStart, + isEnd: isEnd, + msLength: segEnd - segStart + }); + } + } + return segs.sort(segmentCompare); + } + // event rendering calculation utilities - col = stackAgendaSegs( - sliceSegs( - events, - visEventEnds, - d, - addMinutes(cloneDate(d), maxMinute-minMinute) - ) - ); - countForwardSegs(col); + function stackSegs(segs) { + var levels = [], + i, len = segs.length, + seg, + j, collide, k; + for (i = 0; i < len; i++) { + seg = segs[i]; + j = 0; // the level index where seg should belong + while (true) { + collide = false; + if (levels[j]) { + for (k = 0; k < levels[j].length; k++) { + if (segsCollide(levels[j][k], seg)) { + collide = true; + break; + } + } + } + if (collide) { + j++; + } else { + break; + } + } + if (levels[j]) { + levels[j].push(seg); + } else { + levels[j] = [seg]; + } + } + return levels; + } - for (j=0; j start && eventStart < end) { - if (eventStart < start) { - segStart = cloneDate(start); - isStart = false; - }else{ - segStart = eventStart; - isStart = true; - } - if (eventEnd > end) { - segEnd = cloneDate(end); - isEnd = false; - }else{ - segEnd = eventEnd; - isEnd = true; - } - segs.push({ - event: event, - start: segStart, - end: segEnd, - isStart: isStart, - isEnd: isEnd, - msLength: segEnd - segStart - }); - } - } - return segs.sort(segmentCompare); - } + function slotSegHtml(event, seg) { + var html = "<"; + var url = event.url; + var skinCss = getSkinCss(event, opt); + var classes = ['fc-event', 'fc-event-vert']; + if (isEventDraggable(event)) { + classes.push('fc-event-draggable'); + } + if (seg.isStart) { + classes.push('fc-event-start'); + } + if (seg.isEnd) { + classes.push('fc-event-end'); + } + classes = classes.concat(event.className); + if (event.source) { + classes = classes.concat(event.source.className || []); + } + if (url) { + html += "a href='" + htmlEscape(event.url) + "'"; + } else { + html += "div"; + } + html += + " class='" + classes.join(' ') + "'" + + " style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" + + ">" + + "
" + + "
" + + htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + + "
" + + "
" + + htmlEscape(event.title) + + "
" + + "
" + + "
"; + if (seg.isEnd && isEventResizable(event)) { + html += + "
=
"; + } + html += + ""; + return html; + } + function bindSlotSeg(event, eventElement, seg) { + var timeElement = eventElement.find('div.fc-event-time'); + if (isEventDraggable(event)) { + draggableSlotEvent(event, eventElement, timeElement); + } + if (seg.isEnd && isEventResizable(event)) { + resizableSlotEvent(event, eventElement, timeElement); + } + eventElementHandlers(event, eventElement); + } + /* Dragging + -----------------------------------------------------------------------------------*/ + // when event starts out FULL-DAY + // overrides DayEventRenderer's version because it needs to account for dragging elements + // to and from the slot area. - function slotEventEnd(event) { - if (event.end) { - return cloneDate(event.end); - }else{ - return addMinutes(cloneDate(event.start), opt('defaultEventMinutes')); - } - } - - - // renders events in the 'time slots' at the bottom - // TODO: when we refactor this, when user returns `false` eventRender, don't have empty space - // TODO: refactor will include using pixels to detect collisions instead of dates (handy for seg cmp) - - function renderSlotSegs(segs, modifiedEventId) { - - var i, segCnt=segs.length, seg, - event, - classes, - top, bottom, - colI, levelI, forward, - leftmost, - availWidth, - outerWidth, - left, - html='', - eventElements, - eventElement, - triggerRes, - titleElement, - height, - slotSegmentContainer = getSlotSegmentContainer(), - rtl, dis; - - if (rtl = opt('isRTL')) { - dis = -1; - }else{ - dis = 1; - } - - // calculate position/dimensions, create html - for (i=0; i" + - "
" + - "
" + - htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + - "
" + - "
" + - htmlEscape(event.title) + - "
" + - "
" + - "
"; - if (seg.isEnd && isEventResizable(event)) { - html += - "
=
"; - } - html += - ""; - return html; - } - - - function bindSlotSeg(event, eventElement, seg) { - var timeElement = eventElement.find('div.fc-event-time'); - if (isEventDraggable(event)) { - draggableSlotEvent(event, eventElement, timeElement); - } - if (seg.isEnd && isEventResizable(event)) { - resizableSlotEvent(event, eventElement, timeElement); - } - eventElementHandlers(event, eventElement); - } - - - - /* Dragging - -----------------------------------------------------------------------------------*/ - - - // when event starts out FULL-DAY - // overrides DayEventRenderer's version because it needs to account for dragging elements - // to and from the slot area. - - function draggableDayEvent(event, eventElement, seg) { - var isStart = seg.isStart; - var origWidth; - var revert; - var allDay = true; - var dayDelta; - var hoverListener = getHoverListener(); - var colWidth = getColWidth(); - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - 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) { - clearOverlays(); - if (cell) { - revert = false; - var origDate = cellToDate(0, origCell.col); - var date = cellToDate(0, cell.col); - dayDelta = dayDiff(date, origDate); - 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) { - if (allDay) { - // convert event to temporary slot-event - eventElement.width(colWidth - 10); // don't use entire width - setOuterHeight( - eventElement, - snapHeight * Math.round( - (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) / - snapMinutes - ) - ); - eventElement.draggable('option', 'grid', [colWidth, 1]); - allDay = false; - } - }else{ - revert = true; - } - } - revert = revert || (allDay && !dayDelta); - }else{ - resetElement(); - revert = true; - } - eventElement.draggable('option', 'revert', revert); - }, ev, 'drag'); - }, - stop: function(ev, ui) { - hoverListener.stop(); - clearOverlays(); - trigger('eventDragStop', eventElement, event, ev, ui); - if (revert) { - // hasn't moved or is out of bounds (draggable has already reverted) - resetElement(); - eventElement.css('filter', ''); // clear IE opacity side-effects - showEvents(event, eventElement); - }else{ - // changed! - var minuteDelta = 0; - if (!allDay) { - minuteDelta = Math.round((eventElement.offset().top - getSlotContainer().offset().top) / snapHeight) - * snapMinutes - + minMinute - - (event.start.getHours() * 60 + event.start.getMinutes()); - } - eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui); - } - } - }); - 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) { - var origPosition; - var allDay = false; - var dayDelta; - var minuteDelta; - var prevMinuteDelta; - var hoverListener = getHoverListener(); - var colCnt = getColCnt(); - var colWidth = getColWidth(); - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - eventElement.draggable({ - zIndex: 9, - scroll: false, - grid: [colWidth, snapHeight], - axis: colCnt==1 ? 'y' : false, - opacity: opt('dragOpacity'), - revertDuration: opt('dragRevertDuration'), - start: function(ev, ui) { - trigger('eventDragStart', eventElement, event, ev, ui); - hideEvents(event, eventElement); - origPosition = eventElement.position(); - minuteDelta = prevMinuteDelta = 0; - hoverListener.start(function(cell, origCell) { - eventElement.draggable('option', 'revert', !cell); - clearOverlays(); - if (cell) { - var origDate = cellToDate(0, origCell.col); - var date = cellToDate(0, cell.col); - dayDelta = dayDiff(date, origDate); - 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) / snapHeight) * snapMinutes; - 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('filter', ''); // clear IE opacity side-effects - eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position - updateTimeText(0); - 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, snapHeight]); - allDay = false; - } - } - } - - - - /* Resizing - --------------------------------------------------------------------------------------*/ - - - function resizableSlotEvent(event, eventElement, timeElement) { - var snapDelta, prevSnapDelta; - var snapHeight = getSnapHeight(); - var snapMinutes = getSnapMinutes(); - eventElement.resizable({ - handles: { - s: '.ui-resizable-handle' - }, - grid: snapHeight, - start: function(ev, ui) { - snapDelta = prevSnapDelta = 0; - hideEvents(event, eventElement); - 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 - snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight); - if (snapDelta != prevSnapDelta) { - timeElement.text( - formatDates( - event.start, - (!snapDelta && !event.end) ? null : // no change, so don't display time range - addMinutes(eventEnd(event), snapMinutes*snapDelta), - opt('timeFormat') - ) - ); - prevSnapDelta = snapDelta; - } - }, - stop: function(ev, ui) { - trigger('eventResizeStop', this, event, ev, ui); - if (snapDelta) { - eventResize(this, event, 0, snapMinutes*snapDelta, 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 draggableDayEvent(event, eventElement, seg) { + var isStart = seg.isStart; + var origWidth; + var revert; + var allDay = true; + var dayDelta; + var hoverListener = getHoverListener(); + var colWidth = getColWidth(); + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); + 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) { + clearOverlays(); + if (cell) { + revert = false; + var origDate = cellToDate(0, origCell.col); + var date = cellToDate(0, cell.col); + dayDelta = dayDiff(date, origDate); + 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) { + if (allDay) { + // convert event to temporary slot-event + eventElement.width(colWidth - 10); // don't use entire width + setOuterHeight( + eventElement, + snapHeight * Math.round( + (event.end ? ((event.end - event.start) / MINUTE_MS) : opt('defaultEventMinutes')) / + snapMinutes + ) + ); + eventElement.draggable('option', 'grid', [colWidth, 1]); + allDay = false; + } + } else { + revert = true; + } + } + revert = revert || (allDay && !dayDelta); + } else { + resetElement(); + revert = true; + } + eventElement.draggable('option', 'revert', revert); + }, ev, 'drag'); + }, + stop: function (ev, ui) { + hoverListener.stop(); + clearOverlays(); + trigger('eventDragStop', eventElement, event, ev, ui); + if (revert) { + // hasn't moved or is out of bounds (draggable has already reverted) + resetElement(); + eventElement.css('filter', ''); // clear IE opacity side-effects + showEvents(event, eventElement); + } else { + // changed! + var minuteDelta = 0; + if (!allDay) { + minuteDelta = Math.round((eventElement.offset().top - getSlotContainer().offset().top) / snapHeight) * snapMinutes + minMinute - (event.start.getHours() * 60 + event.start.getMinutes()); + } + eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui); + } + } + }); -} + 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) { + var origPosition; + var allDay = false; + var dayDelta; + var minuteDelta; + var prevMinuteDelta; + var hoverListener = getHoverListener(); + var colCnt = getColCnt(); + var colWidth = getColWidth(); + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); + eventElement.draggable({ + zIndex: 9, + scroll: false, + grid: [colWidth, snapHeight], + axis: colCnt == 1 ? 'y' : false, + opacity: opt('dragOpacity'), + revertDuration: opt('dragRevertDuration'), + start: function (ev, ui) { + trigger('eventDragStart', eventElement, event, ev, ui); + hideEvents(event, eventElement); + origPosition = eventElement.position(); + minuteDelta = prevMinuteDelta = 0; + hoverListener.start(function (cell, origCell) { + eventElement.draggable('option', 'revert', !cell); + clearOverlays(); + if (cell) { + var origDate = cellToDate(0, origCell.col); + var date = cellToDate(0, cell.col); + dayDelta = dayDiff(date, origDate); + 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) / snapHeight) * snapMinutes; + 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('filter', ''); // clear IE opacity side-effects + eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position + updateTimeText(0); + 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'))); + } -/* Agenda Event Segment Utilities ------------------------------------------------------------------------------*/ -// TODO: maybe somehow consolidate this with DayEventRenderer's segment system + function resetElement() { + // convert back to original slot-event + if (allDay) { + timeElement.css('display', ''); // show() was causing display=inline + eventElement.draggable('option', 'grid', [colWidth, snapHeight]); + allDay = false; + } + } + } + /* Resizing + --------------------------------------------------------------------------------------*/ + function resizableSlotEvent(event, eventElement, timeElement) { + var snapDelta, prevSnapDelta; + var snapHeight = getSnapHeight(); + var snapMinutes = getSnapMinutes(); + eventElement.resizable({ + handles: { + s: '.ui-resizable-handle' + }, + grid: snapHeight, + start: function (ev, ui) { + snapDelta = prevSnapDelta = 0; + hideEvents(event, eventElement); + 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 + snapDelta = Math.round((Math.max(snapHeight, eventElement.height()) - ui.originalSize.height) / snapHeight); + if (snapDelta != prevSnapDelta) { + timeElement.text( + formatDates( + event.start, (!snapDelta && !event.end) ? null : // no change, so don't display time range + addMinutes(eventEnd(event), snapMinutes * snapDelta), + opt('timeFormat') + ) + ); + prevSnapDelta = snapDelta; + } + }, + stop: function (ev, ui) { + trigger('eventResizeStop', this, event, ev, ui); + if (snapDelta) { + eventResize(this, event, 0, snapMinutes * snapDelta, ev, ui); + } else { + eventElement.css('z-index', 8); + showEvents(event, eventElement); + // BUG: if event was really short, need to put title back in span + } + } + }); + } + /* Agenda Event Segment Utilities + -----------------------------------------------------------------------------*/ + // TODO: maybe somehow consolidate this with DayEventRenderer's segment system -function stackAgendaSegs(segs) { - var levels = [], - i, len = segs.length, seg, - j, collide, k; - for (i=0; i 0; i--) { + level = levels[i]; + for (j = 0; j < level.length; j++) { + segForward = level[j]; + for (k = 0; k < levels[i - 1].length; k++) { + segBack = levels[i - 1][k]; + if (agendaSegsCollide(segForward, segBack)) { + segBack.forward = Math.max(segBack.forward || 0, (segForward.forward || 0) + 1); + } + } + } + } + } -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 seg2.start && seg1.start < seg2.end; -} - + function agendaSegsCollide(seg1, seg2) { + return seg1.end > seg2.start && seg1.start < seg2.end; + } +} \ No newline at end of file diff --git a/src/resource/ResourceView.js b/src/resource/ResourceView.js index e102d5f..e31f661 100644 --- a/src/resource/ResourceView.js +++ b/src/resource/ResourceView.js @@ -1,893 +1,753 @@ - -// setDefaults({ -// allDaySlot: true, -// allDayText: 'all-day', -// firstHour: 6, -// slotMinutes: 30, -// defaultEventMinutes: 120, -// axisFormat: 'h(:mm)tt', -// timeFormat: { -// agenda: 'h:mm{ - h:mm}' -// }, -// dragOpacity: { -// agenda: .5 -// }, -// minTime: 0, -// maxTime: 24 -// }); - - -// TODO: make it work in quirks mode (event corners, all-day height) -// TODO: test liquid width, especially in IE6 - - function ResourceView(element, calendar, viewName) { - var t = this; - - - // exports - t.renderResource = renderResource; - t.setWidth = setWidth; - t.setHeight = setHeight; - t.beforeHide = beforeHide; - t.afterShow = afterShow; - t.defaultEventEnd = defaultEventEnd; - t.timePosition = timePosition; - t.getIsCellAllDay = getIsCellAllDay; - t.allDayRow = getAllDayRow; - t.getHoverListener = function() { return hoverListener }; - t.colLeft = colLeft; - t.colRight = colRight; - t.colContentLeft = colContentLeft; - t.colContentRight = colContentRight; - t.getDaySegmentContainer = function() { return daySegmentContainer }; - t.getSlotSegmentContainer = function() { return slotSegmentContainer }; - t.getMinMinute = function() { return minMinute }; - t.getMaxMinute = function() { return maxMinute }; - t.getSlotContainer = function() { return slotContainer }; - t.getRowCnt = function() { return 1 }; - t.getColCnt = function() { return colCnt }; - t.getColWidth = function() { return colWidth }; - t.getSnapHeight = function() { return snapHeight }; - t.getSnapMinutes = function() { return snapMinutes }; - t.defaultSelectionEnd = defaultSelectionEnd; - t.renderDayOverlay = renderDayOverlay; - t.renderSelection = renderSelection; - t.clearSelection = clearSelection; - t.reportDayClick = reportDayClick; // selection mousedown hack - t.dragStart = dragStart; - t.dragStop = dragStop; - t.getResources = calendar.fetchResources; - - // imports - View.call(t, element, calendar, viewName); - OverlayManager.call(t); - SelectionManager.call(t); - AgendaEventRenderer.call(t); - var opt = t.opt; - var trigger = t.trigger; - var clearEvents = t.clearEvents; - var renderOverlay = t.renderOverlay; - var clearOverlays = t.clearOverlays; - var reportSelection = t.reportSelection; - var unselect = t.unselect; - var daySelectionMousedown = t.daySelectionMousedown; - var slotSegHtml = t.slotSegHtml; - var cellToDate = t.cellToDate; - var dateToCell = t.dateToCell; - var rangeToSegments = t.rangeToSegments; - var formatDate = calendar.formatDate; - - - // locals - - var dayTable; - var dayHead; - var dayHeadCells; - var dayBody; - var dayBodyCells; - var dayBodyCellInners; - var dayBodyCellContentInners; - var dayBodyFirstCell; - var dayBodyFirstCellStretcher; - var slotLayer; - var daySegmentContainer; - var allDayTable; - var allDayRow; - var slotScroller; - var slotContainer; - var slotSegmentContainer; - var slotTable; - var slotTableFirstInner; - var selectionHelper; - - var viewWidth; - var viewHeight; - var axisWidth; - var colWidth; - var gutterWidth; - var slotHeight; // TODO: what if slotHeight changes? (see issue 650) - - var snapMinutes; - var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4) - var snapHeight; // holds the pixel hight of a "selection" slot - - var colCnt; - var slotCnt; - var coordinateGrid; - var hoverListener; - var colPositions; - var colContentPositions; - var slotTopCache = {}; - var savedScrollTop; - - var tm; - var rtl; - var minMinute, maxMinute; - var colFormat; - var showWeekNumbers; - var weekNumberTitle; - var weekNumberFormat; - - - - /* Rendering - -----------------------------------------------------------------------------*/ - - - disableTextSelection(element.addClass('fc-agenda')); - - - function renderResource(c) { - colCnt = c; - updateOptions(); - if (!dayTable) { - buildSkeleton(); // builds day table, slot area, events containers - }else{ - buildDayTable(); // rebuilds day table - clearEvents(); - } - } - - - function updateOptions() { - - tm = opt('theme') ? 'ui' : 'fc'; - rtl = opt('isRTL') - minMinute = parseTime(opt('minTime')); - maxMinute = parseTime(opt('maxTime')); - colFormat = opt('columnFormat'); - - // week # options. (TODO: bad, logic also in other views) - showWeekNumbers = opt('weekNumbers'); - weekNumberTitle = opt('weekNumberTitle'); - if (opt('weekNumberCalculation') != 'iso') { - weekNumberFormat = "w"; - } - else { - weekNumberFormat = "W"; - } - - snapMinutes = opt('snapMinutes') || opt('slotMinutes'); - } - - - - /* Build DOM - -----------------------------------------------------------------------*/ - - - function buildSkeleton() { - var headerClass = tm + "-widget-header"; - var contentClass = tm + "-widget-content"; - var s; - var d; - var i; - var maxd; - var minutes; - var slotNormal = opt('slotMinutes') % 15 == 0; - - buildDayTable(); - - slotLayer = - $("
") - .appendTo(element); - - if (opt('allDaySlot')) { - - daySegmentContainer = - $("
") - .appendTo(slotLayer); - - s = - "" + - "" + - "" + - "" + - "" + - "" + - "
" + opt('allDayText') + "" + - "
" + - "
 
"; - allDayTable = $(s).appendTo(slotLayer); - allDayRow = allDayTable.find('tr'); - - dayBind(allDayRow.find('td')); - - slotLayer.append( - "
" + - "
" + - "
" - ); - - }else{ - - daySegmentContainer = $([]); // in jQuery 1.4, we can just do $() - - } - - slotScroller = - $("
") - .appendTo(slotLayer); - - slotContainer = - $("
") - .appendTo(slotScroller); - - slotSegmentContainer = - $("
") - .appendTo(slotContainer); - - s = - "" + - ""; - d = zeroDate(); - maxd = addMinutes(cloneDate(d), maxMinute); - addMinutes(d, minMinute); - slotCnt = 0; - for (i=0; d < maxd; i++) { - minutes = d.getMinutes(); - s += - "" + - "" + - "" + - ""; - addMinutes(d, opt('slotMinutes')); - slotCnt++; - } - s += - "" + - "
" + - ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') + - "" + - "
 
" + - "
"; - slotTable = $(s).appendTo(slotContainer); - slotTableFirstInner = slotTable.find('div:first'); - - slotBind(slotTable.find('td')); - } - - - - /* Build Day Table - -----------------------------------------------------------------------*/ - - - function buildDayTable() { - var html = buildDayTableHTML(); - - if (dayTable) { - dayTable.remove(); - } - dayTable = $(html).appendTo(element); - - dayHead = dayTable.find('thead'); - dayHeadCells = dayHead.find('th').slice(1, -1); // exclude gutter - dayBody = dayTable.find('tbody'); - dayBodyCells = dayBody.find('td').slice(0, -1); // exclude gutter - dayBodyCellInners = dayBodyCells.find('> div'); - dayBodyCellContentInners = dayBodyCells.find('.fc-day-content > div'); - - dayBodyFirstCell = dayBodyCells.eq(0); - dayBodyFirstCellStretcher = dayBodyCellInners.eq(0); - - markFirstLast(dayHead.add(dayHead.find('tr'))); - markFirstLast(dayBody.add(dayBody.find('tr'))); - - // TODO: now that we rebuild the cells every time, we should call dayRender - } - - - function buildDayTableHTML() { - var html = - "" + - buildDayTableHeadHTML() + - buildDayTableBodyHTML() + - "
"; - - return html; - } - - - function buildDayTableHeadHTML() { - var headerClass = tm + "-widget-header"; - var date; - var html = ''; - var weekText; - var col; - - html += - "" + - ""; - - if (showWeekNumbers) { - weekText = formatDate(date, weekNumberFormat); - if (rtl) { - weekText += weekNumberTitle; - } - else { - weekText = weekNumberTitle + weekText; - } - html += - "" + - htmlEscape(weekText) + - ""; - } - else { - html += " "; - } - - for (col=0; col" + - htmlEscape(formatDate(date, colFormat)) + - ""; - } - - html += - " " + - "" + - ""; - - return html; - } - - - function buildDayTableBodyHTML() { - var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called - var contentClass = tm + "-widget-content"; - var date; - var today = clearTime(new Date()); - var col; - var cellsHTML; - var cellHTML; - var classNames; - var html = ''; - - html += - "" + - "" + - " "; - - cellsHTML = ''; - - for (col=0; col" + - "
" + - "
" + - "
 
" + - "
" + - "
" + - ""; - - cellsHTML += cellHTML; - } - - html += cellsHTML; - html += - " " + - "" + - ""; - - return html; - } - - - // TODO: data-date on the cells - - - - /* Dimensions - -----------------------------------------------------------------------*/ - - - function setHeight(height, dateChanged) { - if (height === undefined) { - height = viewHeight; - } - viewHeight = height; - slotTopCache = {}; - - var headHeight = dayBody.position().top; - var allDayHeight = slotScroller.position().top; // including divider - var bodyHeight = Math.min( // total body height, including borders - height - headHeight, // when scrollbars - slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border - ); - - dayBodyFirstCellStretcher - .height(bodyHeight - vsides(dayBodyFirstCell)); - - slotLayer.css('top', headHeight); - - slotScroller.height(bodyHeight - allDayHeight - 1); - - slotHeight = slotTableFirstInner.height() + 1; // +1 for border - - snapRatio = opt('slotMinutes') / snapMinutes; - snapHeight = slotHeight / snapRatio; - - if (dateChanged) { - resetScroll(); - } - } - - - function setWidth(width) { - viewWidth = width; - colPositions.clear(); - colContentPositions.clear(); - - var axisFirstCells = dayHead.find('th:first'); - if (allDayTable) { - axisFirstCells = axisFirstCells.add(allDayTable.find('th:first')); - } - axisFirstCells = axisFirstCells.add(slotTable.find('th:first')); - - axisWidth = 0; - setOuterWidth( - axisFirstCells - .width('') - .each(function(i, _cell) { - axisWidth = Math.max(axisWidth, $(_cell).outerWidth()); - }), - axisWidth - ); - - var gutterCells = dayTable.find('.fc-agenda-gutter'); - if (allDayTable) { - gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter')); - } - - var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7) - - gutterWidth = slotScroller.width() - slotTableWidth; - if (gutterWidth) { - setOuterWidth(gutterCells, gutterWidth); - gutterCells - .show() - .prev() - .removeClass('fc-last'); - }else{ - gutterCells - .hide() - .prev() - .addClass('fc-last'); - } - - colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt); - setOuterWidth(dayHeadCells.slice(0, -1), colWidth); - } - - - - /* Scrolling - -----------------------------------------------------------------------*/ - - - function resetScroll() { - var d0 = zeroDate(); - var scrollDate = cloneDate(d0); - scrollDate.setHours(opt('firstHour')); - var top = timePosition(d0, scrollDate) + 1; // +1 for the border - function scroll() { - slotScroller.scrollTop(top); - } - scroll(); - setTimeout(scroll, 0); // overrides any previous scroll state made by the browser - } - - - function beforeHide() { - savedScrollTop = slotScroller.scrollTop(); - } - - - function afterShow() { - slotScroller.scrollTop(savedScrollTop); - } - - - - /* Slot/Day clicking and binding - -----------------------------------------------------------------------*/ - - - function dayBind(cells) { - cells.click(slotClick) - .mousedown(daySelectionMousedown); - } - - - function slotBind(cells) { - cells.click(slotClick) - .mousedown(slotSelectionMousedown); - } - - - function slotClick(ev) { - if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick - var col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth)); - var date = cellToDate(0, col); - var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data - if (rowMatch) { - var mins = parseInt(rowMatch[1]) * opt('slotMinutes'); - var hours = Math.floor(mins/60); - date.setHours(hours); - date.setMinutes(mins%60 + minMinute); - trigger('dayClick', dayBodyCells[col], date, false, ev); - }else{ - trigger('dayClick', dayBodyCells[col], date, true, ev); - } - } - } - - - - /* Semi-transparent Overlay Helpers - -----------------------------------------------------*/ - // TODO: should be consolidated with BasicView's methods - - - function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive - - if (refreshCoordinateGrid) { - coordinateGrid.build(); - } - - var segments = rangeToSegments(overlayStart, overlayEnd); - - for (var i=0; i= 0) { - addMinutes(d, minMinute + slotIndex * snapMinutes); - } - return d; - } - - - // get the Y coordinate of the given time on the given day (both Date objects) - function timePosition(day, time) { // both date objects. day holds 00:00 of current day - day = cloneDate(day, true); - if (time < addMinutes(cloneDate(day), minMinute)) { - return 0; - } - if (time >= addMinutes(cloneDate(day), maxMinute)) { - return slotTable.height(); - } - var slotMinutes = opt('slotMinutes'), - minutes = time.getHours()*60 + time.getMinutes() - minMinute, - slotI = Math.floor(minutes / slotMinutes), - slotTop = slotTopCache[slotI]; - if (slotTop === undefined) { - slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization??? - } - return Math.max(0, Math.round( - slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) - )); - } - - - function getAllDayRow(index) { - return allDayRow; - } - - - function defaultEventEnd(event) { - var start = cloneDate(event.start); - if (event.allDay) { - return start; - } - return addMinutes(start, opt('defaultEventMinutes')); - } - - - - /* Selection - ---------------------------------------------------------------------------------*/ - - - function defaultSelectionEnd(startDate, allDay) { - if (allDay) { - return cloneDate(startDate); - } - return addMinutes(cloneDate(startDate), opt('slotMinutes')); - } - - - function renderSelection(startDate, endDate, allDay) { // only for all-day - if (allDay) { - if (opt('allDaySlot')) { - renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); - } - }else{ - renderSlotSelection(startDate, endDate); - } - } - - - function renderSlotSelection(startDate, endDate) { - var helperOption = opt('selectHelper'); - coordinateGrid.build(); - if (helperOption) { - var col = dateToCell(startDate).col; - if (col >= 0 && col < colCnt) { // only works when times are on same day - var rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords - var top = timePosition(startDate, startDate); - var bottom = timePosition(startDate, endDate); - if (bottom > top) { // protect against selections that are entirely before or after visible range - rect.top = top; - rect.height = bottom - top; - rect.left += 2; - rect.width -= 5; - if ($.isFunction(helperOption)) { - var helperRes = helperOption(startDate, endDate); - if (helperRes) { - rect.position = 'absolute'; - rect.zIndex = 8; - selectionHelper = $(helperRes) - .css(rect) - .appendTo(slotContainer); - } - }else{ - rect.isStart = true; // conside rect a "seg" now - rect.isEnd = true; // - selectionHelper = $(slotSegHtml( - { - title: '', - start: startDate, - end: endDate, - className: ['fc-select-helper'], - editable: false - }, - rect - )); - selectionHelper.css('opacity', opt('dragOpacity')); - } - if (selectionHelper) { - slotBind(selectionHelper); - slotContainer.append(selectionHelper); - setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended - setOuterHeight(selectionHelper, rect.height, true); - } - } - } - }else{ - renderSlotOverlay(startDate, endDate); - } - } - - - function clearSelection() { - clearOverlays(); - if (selectionHelper) { - selectionHelper.remove(); - selectionHelper = null; - } - } - - - function slotSelectionMousedown(ev) { - if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button - unselect(ev); - var dates; - hoverListener.start(function(cell, origCell) { - clearSelection(); - if (cell && cell.col == origCell.col && !getIsCellAllDay(cell)) { - var d1 = realCellToDate(origCell); - var d2 = realCellToDate(cell); - dates = [ - d1, - addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes - d2, - addMinutes(cloneDate(d2), snapMinutes) - ].sort(dateCompare); - renderSlotSelection(dates[0], dates[3]); - }else{ - dates = null; - } - }, ev); - $(document).one('mouseup', function(ev) { - hoverListener.stop(); - if (dates) { - if (+dates[0] == +dates[1]) { - reportDayClick(dates[0], false, ev); - } - reportSelection(dates[0], dates[3], false, ev); - } - }); - } - } - - - function reportDayClick(date, allDay, ev) { - trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev); - } - - - - /* External Dragging - --------------------------------------------------------------------------------*/ - - - function dragStart(_dragElement, ev, ui) { - hoverListener.start(function(cell) { - clearOverlays(); - if (cell) { - if (getIsCellAllDay(cell)) { - renderCellOverlay(cell.row, cell.col, cell.row, cell.col); - }else{ - var d1 = realCellToDate(cell); - var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes')); - renderSlotOverlay(d1, d2); - } - } - }, ev); - } - - - function dragStop(_dragElement, ev, ui) { - var cell = hoverListener.stop(); - clearOverlays(); - if (cell) { - trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui); - } - } - - -} + var t = this; + // exports + t.renderResource = renderResource; + t.setWidth = setWidth; + t.setHeight = setHeight; + t.beforeHide = beforeHide; + t.afterShow = afterShow; + t.defaultEventEnd = defaultEventEnd; + t.timePosition = timePosition; + t.getIsCellAllDay = getIsCellAllDay; + t.allDayRow = getAllDayRow; + t.getHoverListener = function () { + return hoverListener + }; + t.colLeft = colLeft; + t.colRight = colRight; + t.colContentLeft = colContentLeft; + t.colContentRight = colContentRight; + t.getDaySegmentContainer = function () { + return daySegmentContainer + }; + t.getSlotSegmentContainer = function () { + return slotSegmentContainer + }; + t.getMinMinute = function () { + return minMinute + }; + t.getMaxMinute = function () { + return maxMinute + }; + t.getSlotContainer = function () { + return slotContainer + }; + t.getRowCnt = function () { + return 1 + }; + t.getColCnt = function () { + return colCnt + }; + t.getColWidth = function () { + return colWidth + }; + t.getSnapHeight = function () { + return snapHeight + }; + t.getSnapMinutes = function () { + return snapMinutes + }; + t.defaultSelectionEnd = defaultSelectionEnd; + t.renderDayOverlay = renderDayOverlay; + t.renderSelection = renderSelection; + t.clearSelection = clearSelection; + t.reportDayClick = reportDayClick; // selection mousedown hack + t.dragStart = dragStart; + t.dragStop = dragStop; + t.getResources = calendar.fetchResources(); // this is the key + // imports + View.call(t, element, calendar, viewName); + OverlayManager.call(t); + SelectionManager.call(t); + ResourceEventRenderer.call(t); + var opt = t.opt; + var trigger = t.trigger; + var clearEvents = t.clearEvents; + var renderOverlay = t.renderOverlay; + var clearOverlays = t.clearOverlays; + var reportSelection = t.reportSelection; + var unselect = t.unselect; + var daySelectionMousedown = t.daySelectionMousedown; + var slotSegHtml = t.slotSegHtml; + var cellToDate = t.cellToDate; + var dateToCell = t.dateToCell; + var rangeToSegments = t.rangeToSegments; + var formatDate = calendar.formatDate; + // locals + var dayTable; + var dayHead; + var dayHeadCells; + var dayBody; + var dayBodyCells; + var dayBodyCellInners; + var dayBodyCellContentInners; + var dayBodyFirstCell; + var dayBodyFirstCellStretcher; + var slotLayer; + var daySegmentContainer; + var allDayTable; + var allDayRow; + var slotScroller; + var slotContainer; + var slotSegmentContainer; + var slotTable; + var slotTableFirstInner; + var selectionHelper; + var viewWidth; + var viewHeight; + var axisWidth; + var colWidth; + var gutterWidth; + var slotHeight; // TODO: what if slotHeight changes? (see issue 650) + var snapMinutes; + var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4) + var snapHeight; // holds the pixel hight of a "selection" slot + var colCnt; + var slotCnt; + var coordinateGrid; + var hoverListener; + var colPositions; + var colContentPositions; + var slotTopCache = {}; + var savedScrollTop; + var tm; + var rtl; + var minMinute, maxMinute; + var colFormat; + var showWeekNumbers; + var weekNumberTitle; + var weekNumberFormat; + var resources = t.getResources; + /* Rendering + -----------------------------------------------------------------------------*/ + disableTextSelection(element.addClass('fc-agenda')); + + function renderResource(c) { + updateOptions(); + if (!dayTable) { + buildSkeleton(); // builds day table, slot area, events containers + } else { + buildDayTable(); // rebuilds day table + clearEvents(); + } + } + + function updateOptions() { + tm = opt('theme') ? 'ui' : 'fc'; + rtl = opt('isRTL') + minMinute = parseTime(opt('minTime')); + maxMinute = parseTime(opt('maxTime')); + colFormat = opt('columnFormat'); + // week # options. (TODO: bad, logic also in other views) + showWeekNumbers = opt('weekNumbers'); + weekNumberTitle = opt('weekNumberTitle'); + if (opt('weekNumberCalculation') != 'iso') { + weekNumberFormat = "w"; + } else { + weekNumberFormat = "W"; + } + snapMinutes = opt('snapMinutes') || opt('slotMinutes'); + } + /* Build DOM + -----------------------------------------------------------------------*/ + + function buildSkeleton() { + var headerClass = tm + "-widget-header"; + var contentClass = tm + "-widget-content"; + var s; + var d; + var i; + var maxd; + var minutes; + var slotNormal = opt('slotMinutes') % 15 == 0; + buildDayTable(); + slotLayer = + $("
") + .appendTo(element); + if (opt('allDaySlot')) { + daySegmentContainer = + $("
") + .appendTo(slotLayer); + s = + "" + + "" + + "" + + "" + + "" + + "" + + "
" + opt('allDayText') + "" + + "
" + + "
 
"; + allDayTable = $(s).appendTo(slotLayer); + allDayRow = allDayTable.find('tr'); + dayBind(allDayRow.find('td')); + slotLayer.append( + "
" + + "
" + + "
" + ); + } else { + daySegmentContainer = $([]); // in jQuery 1.4, we can just do $() + } + slotScroller = + $("
") + .appendTo(slotLayer); + slotContainer = + $("
") + .appendTo(slotScroller); + slotSegmentContainer = + $("
") + .appendTo(slotContainer); + s = + "" + + ""; + d = zeroDate(); + maxd = addMinutes(cloneDate(d), maxMinute); + addMinutes(d, minMinute); + slotCnt = 0; + for (i = 0; d < maxd; i++) { + minutes = d.getMinutes(); + s += + "" + + "" + + "" + + ""; + addMinutes(d, opt('slotMinutes')); + slotCnt++; + } + s += + "" + + "
" + + ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') + + "" + + "
 
" + + "
"; + slotTable = $(s).appendTo(slotContainer); + slotTableFirstInner = slotTable.find('div:first'); + slotBind(slotTable.find('td')); + } + /* Build Day Table + -----------------------------------------------------------------------*/ + + function buildDayTable() { + var html = buildDayTableHTML(); + if (dayTable) { + dayTable.remove(); + } + dayTable = $(html).appendTo(element); + dayHead = dayTable.find('thead'); + dayHeadCells = dayHead.find('th').slice(1, -1); // exclude gutter + dayBody = dayTable.find('tbody'); + dayBodyCells = dayBody.find('td').slice(0, -1); // exclude gutter + dayBodyCellInners = dayBodyCells.find('> div'); + dayBodyCellContentInners = dayBodyCells.find('.fc-day-content > div'); + dayBodyFirstCell = dayBodyCells.eq(0); + dayBodyFirstCellStretcher = dayBodyCellInners.eq(0); + markFirstLast(dayHead.add(dayHead.find('tr'))); + markFirstLast(dayBody.add(dayBody.find('tr'))); + // TODO: now that we rebuild the cells every time, we should call dayRender + } + + function buildDayTableHTML() { + var html = + "" + + buildDayTableHeadHTML() + + buildDayTableBodyHTML() + + "
"; + return html; + } + + function buildDayTableHeadHTML() { + var headerClass = tm + "-widget-header"; + var date; + var html = ''; + var weekText; + var col; + html += + "" + + ""; + if (showWeekNumbers) { + weekText = formatDate(date, weekNumberFormat); + if (rtl) { + weekText += weekNumberTitle; + } else { + weekText = weekNumberTitle + weekText; + } + html += + "" + + htmlEscape(weekText) + + ""; + } else { + html += " "; + } + // loop for the resources to set the headers + for (i = 0; i < resources.length; i++) { + //date = cellToDate(0, i); + html += + //"" + + "" + + //htmlEscape(formatDate(date, colFormat)) + + htmlEscape(resources[i].name) + + ""; + } + html += + " " + + "" + + ""; + return html; + } + + function buildDayTableBodyHTML() { + var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called + var contentClass = tm + "-widget-content"; + var date; + var today = clearTime(new Date()); + var col; + var cellsHTML; + var cellHTML; + var classNames; + var html = ''; + html += + "" + + "" + + " "; + cellsHTML = ''; + for (col = 0; col < resources.length; col++) { + // how to do this? + date = cellToDate(0, col); + classNames = [ + 'fc-col' + col, + //'fc-' + dayIDs[date.getDay()], + contentClass + ]; + if (+date == +today) { + classNames.push( + tm + '-state-highlight', + 'fc-today' + ); + } + cellHTML = + "" + + "
" + + "
" + + "
 
" + + "
" + + "
" + + ""; + cellsHTML += cellHTML; + } + html += cellsHTML; + html += + " " + + "" + + ""; + return html; + } + // TODO: data-date on the cells + /* Dimensions + -----------------------------------------------------------------------*/ + + function setHeight(height, dateChanged) { + if (height === undefined) { + height = viewHeight; + } + viewHeight = height; + slotTopCache = {}; + var headHeight = dayBody.position().top; + var allDayHeight = slotScroller.position().top; // including divider + var bodyHeight = Math.min( // total body height, including borders + height - headHeight, // when scrollbars + slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border + ); + dayBodyFirstCellStretcher + .height(bodyHeight - vsides(dayBodyFirstCell)); + slotLayer.css('top', headHeight); + slotScroller.height(bodyHeight - allDayHeight - 1); + slotHeight = slotTableFirstInner.height() + 1; // +1 for border + snapRatio = opt('slotMinutes') / snapMinutes; + snapHeight = slotHeight / snapRatio; + if (dateChanged) { + resetScroll(); + } + } + + function setWidth(width) { + viewWidth = width; + colPositions.clear(); + colContentPositions.clear(); + var axisFirstCells = dayHead.find('th:first'); + if (allDayTable) { + axisFirstCells = axisFirstCells.add(allDayTable.find('th:first')); + } + axisFirstCells = axisFirstCells.add(slotTable.find('th:first')); + axisWidth = 0; + setOuterWidth( + axisFirstCells + .width('') + .each(function (i, _cell) { + axisWidth = Math.max(axisWidth, $(_cell).outerWidth()); + }), + axisWidth + ); + var gutterCells = dayTable.find('.fc-agenda-gutter'); + if (allDayTable) { + gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter')); + } + var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7) + gutterWidth = slotScroller.width() - slotTableWidth; + if (gutterWidth) { + setOuterWidth(gutterCells, gutterWidth); + gutterCells + .show() + .prev() + .removeClass('fc-last'); + } else { + gutterCells + .hide() + .prev() + .addClass('fc-last'); + } + colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt); + setOuterWidth(dayHeadCells.slice(0, -1), colWidth); + } + /* Scrolling + -----------------------------------------------------------------------*/ + + function resetScroll() { + var d0 = zeroDate(); + var scrollDate = cloneDate(d0); + scrollDate.setHours(opt('firstHour')); + var top = timePosition(d0, scrollDate) + 1; // +1 for the border + + function scroll() { + slotScroller.scrollTop(top); + } + scroll(); + setTimeout(scroll, 0); // overrides any previous scroll state made by the browser + } + + function beforeHide() { + savedScrollTop = slotScroller.scrollTop(); + } + + function afterShow() { + slotScroller.scrollTop(savedScrollTop); + } + /* Slot/Day clicking and binding + -----------------------------------------------------------------------*/ + + function dayBind(cells) { + cells.click(slotClick) + .mousedown(daySelectionMousedown); + } + + function slotBind(cells) { + cells.click(slotClick) + .mousedown(slotSelectionMousedown); + } + + function slotClick(ev) { + if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick + var col = Math.min(colCnt - 1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth)); + var date = cellToDate(0, col); + var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data + if (rowMatch) { + var mins = parseInt(rowMatch[1]) * opt('slotMinutes'); + var hours = Math.floor(mins / 60); + date.setHours(hours); + date.setMinutes(mins % 60 + minMinute); + trigger('dayClick', dayBodyCells[col], date, false, ev); + } else { + trigger('dayClick', dayBodyCells[col], date, true, ev); + } + } + } + /* Semi-transparent Overlay Helpers + -----------------------------------------------------*/ + // TODO: should be consolidated with BasicView's methods + + function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive + if (refreshCoordinateGrid) { + coordinateGrid.build(); + } + var segments = rangeToSegments(overlayStart, overlayEnd); + for (var i = 0; i < segments.length; i++) { + var segment = segments[i]; + dayBind( + renderCellOverlay( + segment.row, + segment.leftCol, + segment.row, + segment.rightCol + ) + ); + } + } + + function renderCellOverlay(row0, col0, row1, col1) { // only for all-day? + var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer); + return renderOverlay(rect, slotLayer); + } + + function renderSlotOverlay(overlayStart, overlayEnd) { + for (var i = 0; i < colCnt; i++) { + var dayStart = cellToDate(0, i); + var dayEnd = addDays(cloneDate(dayStart), 1); + var stretchStart = new Date(Math.max(dayStart, overlayStart)); + var stretchEnd = new Date(Math.min(dayEnd, overlayEnd)); + if (stretchStart < stretchEnd) { + var rect = coordinateGrid.rect(0, i, 0, i, slotContainer); // only use it for horizontal coords + var top = timePosition(dayStart, stretchStart); + var bottom = timePosition(dayStart, stretchEnd); + rect.top = top; + rect.height = bottom - top; + slotBind( + renderOverlay(rect, slotContainer) + ); + } + } + } + /* Coordinate Utilities + -----------------------------------------------------------------------------*/ + coordinateGrid = new CoordinateGrid(function (rows, cols) { + var e, n, p; + dayHeadCells.each(function (i, _e) { + e = $(_e); + n = e.offset().left; + if (i) { + p[1] = n; + } + p = [n]; + cols[i] = p; + }); + p[1] = n + e.outerWidth(); + if (opt('allDaySlot')) { + e = allDayRow; + n = e.offset().top; + rows[0] = [n, n + e.outerHeight()]; + } + var slotTableTop = slotContainer.offset().top; + var slotScrollerTop = slotScroller.offset().top; + var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight(); + + function constrain(n) { + return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n)); + } + for (var i = 0; i < slotCnt * snapRatio; i++) { // adapt slot count to increased/decreased selection slot count + rows.push([ + constrain(slotTableTop + snapHeight * i), + constrain(slotTableTop + snapHeight * (i + 1)) + ]); + } + }); + hoverListener = new HoverListener(coordinateGrid); + colPositions = new HorizontalPositionCache(function (col) { + return dayBodyCellInners.eq(col); + }); + colContentPositions = new HorizontalPositionCache(function (col) { + return dayBodyCellContentInners.eq(col); + }); + + function colLeft(col) { + return colPositions.left(col); + } + + function colContentLeft(col) { + return colContentPositions.left(col); + } + + function colRight(col) { + return colPositions.right(col); + } + + function colContentRight(col) { + return colContentPositions.right(col); + } + + function getIsCellAllDay(cell) { + return opt('allDaySlot') && !cell.row; + } + + function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system + var d = cellToDate(0, cell.col); + var slotIndex = cell.row; + if (opt('allDaySlot')) { + slotIndex--; + } + if (slotIndex >= 0) { + addMinutes(d, minMinute + slotIndex * snapMinutes); + } + return d; + } + // get the Y coordinate of the given time on the given day (both Date objects) + + function timePosition(day, time) { // both date objects. day holds 00:00 of current day + day = cloneDate(day, true); + if (time < addMinutes(cloneDate(day), minMinute)) { + return 0; + } + if (time >= addMinutes(cloneDate(day), maxMinute)) { + return slotTable.height(); + } + var slotMinutes = opt('slotMinutes'), + minutes = time.getHours() * 60 + time.getMinutes() - minMinute, + slotI = Math.floor(minutes / slotMinutes), + slotTop = slotTopCache[slotI]; + if (slotTop === undefined) { + slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization??? + } + return Math.max(0, Math.round( + slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes) + )); + } + + function getAllDayRow(index) { + return allDayRow; + } + + function defaultEventEnd(event) { + var start = cloneDate(event.start); + if (event.allDay) { + return start; + } + return addMinutes(start, opt('defaultEventMinutes')); + } + /* Selection + ---------------------------------------------------------------------------------*/ + + function defaultSelectionEnd(startDate, allDay) { + if (allDay) { + return cloneDate(startDate); + } + return addMinutes(cloneDate(startDate), opt('slotMinutes')); + } + + function renderSelection(startDate, endDate, allDay) { // only for all-day + if (allDay) { + if (opt('allDaySlot')) { + renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); + } + } else { + renderSlotSelection(startDate, endDate); + } + } + + function renderSlotSelection(startDate, endDate) { + var helperOption = opt('selectHelper'); + coordinateGrid.build(); + if (helperOption) { + var col = dateToCell(startDate).col; + if (col >= 0 && col < colCnt) { // only works when times are on same day + var rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords + var top = timePosition(startDate, startDate); + var bottom = timePosition(startDate, endDate); + if (bottom > top) { // protect against selections that are entirely before or after visible range + rect.top = top; + rect.height = bottom - top; + rect.left += 2; + rect.width -= 5; + if ($.isFunction(helperOption)) { + var helperRes = helperOption(startDate, endDate); + if (helperRes) { + rect.position = 'absolute'; + rect.zIndex = 8; + selectionHelper = $(helperRes) + .css(rect) + .appendTo(slotContainer); + } + } else { + rect.isStart = true; // conside rect a "seg" now + rect.isEnd = true; // + selectionHelper = $(slotSegHtml({ + title: '', + start: startDate, + end: endDate, + className: ['fc-select-helper'], + editable: false + }, + rect + )); + selectionHelper.css('opacity', opt('dragOpacity')); + } + if (selectionHelper) { + slotBind(selectionHelper); + slotContainer.append(selectionHelper); + setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended + setOuterHeight(selectionHelper, rect.height, true); + } + } + } + } else { + renderSlotOverlay(startDate, endDate); + } + } + + function clearSelection() { + clearOverlays(); + if (selectionHelper) { + selectionHelper.remove(); + selectionHelper = null; + } + } + + function slotSelectionMousedown(ev) { + if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button + unselect(ev); + var dates; + hoverListener.start(function (cell, origCell) { + clearSelection(); + if (cell && cell.col == origCell.col && !getIsCellAllDay(cell)) { + var d1 = realCellToDate(origCell); + var d2 = realCellToDate(cell); + dates = [ + d1, + addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes + d2, + addMinutes(cloneDate(d2), snapMinutes) + ].sort(dateCompare); + renderSlotSelection(dates[0], dates[3]); + } else { + dates = null; + } + }, ev); + $(document).one('mouseup', function (ev) { + hoverListener.stop(); + if (dates) { + if (+dates[0] == +dates[1]) { + reportDayClick(dates[0], false, ev); + } + reportSelection(dates[0], dates[3], false, ev); + } + }); + } + } + + function reportDayClick(date, allDay, ev) { + trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev); + } + /* External Dragging + --------------------------------------------------------------------------------*/ + + function dragStart(_dragElement, ev, ui) { + hoverListener.start(function (cell) { + clearOverlays(); + if (cell) { + if (getIsCellAllDay(cell)) { + renderCellOverlay(cell.row, cell.col, cell.row, cell.col); + } else { + var d1 = realCellToDate(cell); + var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes')); + renderSlotOverlay(d1, d2); + } + } + }, ev); + } + + function dragStop(_dragElement, ev, ui) { + var cell = hoverListener.stop(); + clearOverlays(); + if (cell) { + trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui); + } + } +} \ No newline at end of file diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000..d14b4e6 Binary files /dev/null and b/tests/.DS_Store differ diff --git a/tests/resourceDayView.html b/tests/resourceDayView.html index bfd51ba..880a095 100644 --- a/tests/resourceDayView.html +++ b/tests/resourceDayView.html @@ -122,34 +122,34 @@ // } // }, defaultView: 'resourceDay', - resources: [{"id":"resource1","name":"Resource 1"},{"id":"resource2", "name":"Resource 2"}], + resources: [{"id":"resource1","name":"Resource 1"},{"id":"resource2", "name":"Resource 2"},{"id":"resource3", "name":"Resource 3"}], events: [ { - title: 'Lunch 12.15-14.45', + title: 'R1-R2: Lunch 12.15-14.45', start: new Date(y, m, d, 12, 15), end: new Date(y, m, d, 14, 45), allDay: false, resource: ['resource1','resource2'] }, { - title: 'Meeting from this day to this +4', + title: 'R1: All day', start: new Date(y, m, d, 10, 30), end: new Date(y, m, d, 11, 00), - allDay: false, + allDay: true, resource: 'resource1' }, { - title: 'Meeting 11.00', - start: new Date(y, m-2, d, 11, 00), - allDay: true, + title: 'R2: Meeting 11.00', + start: new Date(y, m, d, 11, 00), + allDay: false, resource: 'resource2' }, { - title: 'Lunch 12-14', + title: 'R1/R2: Lunch 12-14', start: new Date(y, m, d, 12, 0), end: new Date(y, m, d, 14, 0), allDay: false, - resource: 'resource1' + resource: ['resource1','resource2'] } ] });