From 5d9928b47f7f7a2eabac6f09265d99a0f85098d9 Mon Sep 17 00:00:00 2001 From: "srkenny@gmail.com" Date: Tue, 13 Aug 2013 14:58:51 +0100 Subject: [PATCH] Fixed current day class issue. Added ability to add resource classnames. Updated to reflect latest core updates. Fixed some IE7 issues around indexOf --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 4 +- src/EventManager.js | 8 + src/common/DayEventRenderer.js | 4 +- src/resource/ResourceDayView.js | 61 +- src/resource/ResourceEventRenderer.js | 266 +-- src/resource/ResourceView.js | 1568 +++++++++-------- tests/.DS_Store | Bin 15364 -> 0 bytes tests/data_as_a_function.html | 2 +- tests/hiddenDays.html | 4 +- tests/index.html | 81 + tests/issue_230_height_json_events.html | 2 +- tests/issue_417_refetchEvents.html | 6 + tests/issue_477_event_width.html | 1 + ..._json.txt => many_agenda_events_json.json} | 0 tests/resourceDayView.html | 394 +++-- 16 files changed, 1345 insertions(+), 1056 deletions(-) delete mode 100644 .DS_Store delete mode 100644 tests/.DS_Store create mode 100644 tests/index.html rename tests/{many_agenda_events_json.txt => many_agenda_events_json.json} (100%) diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 51227d71a94e52683c4d00d01ac912a7dfa75f3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKI|>3p3{CuiU}I@HSMUad=n3`$7K)81_^Y?_TprDrPoXS!S|~4&yqQeiEc=Sh zMnrUeSOjyq1*=D6P-_S;VFyM%Fva+Zyp5TE&WLZbpy zfC^9nDnJE3tw46z(fHFB^FAs-1%6%u`#u!7VNGlU{nLTqBLHxKv>VnwO8|={fHkoV zL$0gG>bW2DSK<@<*e5h_zG?{ceokWPC@W?4D@!4jkV*c7e!sMHO^~d U8|ZZ8oet#BfayY`0^e5P0W;M1& diff --git a/.gitignore b/.gitignore index 22eff2e..70aeda4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ dist node_modules # for bower -components \ No newline at end of file +components + +.DS_Store \ No newline at end of file diff --git a/src/EventManager.js b/src/EventManager.js index a171a39..781dbab 100644 --- a/src/EventManager.js +++ b/src/EventManager.js @@ -376,6 +376,14 @@ function EventManager(options, _sources) { }else{ event.className = []; } + + if (event.resources) { + if (typeof event.resources == 'string') { + event.resources = event.resources.split(/\s+/); + } + }else{ + event.resources = []; + } // TODO: if there is no start date, return false to indicate an invalid event } diff --git a/src/common/DayEventRenderer.js b/src/common/DayEventRenderer.js index a2101c9..6c9c116 100644 --- a/src/common/DayEventRenderer.js +++ b/src/common/DayEventRenderer.js @@ -171,7 +171,7 @@ function DayEventRenderer() { function buildSegments(events) { var resources = t.getResources; - if (resources.length === 0){ + if (typeof resources === 'undefined'){ return buildSegmentsTEMP(events); // TEMP! } else { var segments = []; @@ -193,7 +193,7 @@ function DayEventRenderer() { function eventsForResource(resource, events) { var resourceEvents = []; for (var i = 0; i < events.length; i++) { - if (events[i].resource && events[i].resource.indexOf(resource.id) >= 0) { + if (events[i].resources && $.inArray(resource.id, events[i].resources) >= 0) { resourceEvents.push(events[i]) } } diff --git a/src/resource/ResourceDayView.js b/src/resource/ResourceDayView.js index 66f0561..77d9754 100644 --- a/src/resource/ResourceDayView.js +++ b/src/resource/ResourceDayView.js @@ -1,28 +1,41 @@ + fcViews.resourceDay = ResourceDayView; -function ResourceDayView(element, calendar) { - var t = this; - // exports - t.render = render; - // imports - ResourceView.call(t, element, calendar, 'resourceDay'); - var opt = t.opt; - var renderResource = t.renderResource; - //var skipHiddenDays = t.skipHiddenDays; - var formatDate = calendar.formatDate; - var getResources = t.getResources; - function render(date, delta) { - if (delta) { - addDays(date, delta); - } - //skipHiddenDays(date, delta < 0 ? -1 : 1); - var start = cloneDate(date, true); - var end = addDays(cloneDate(start), 1); - t.title = formatDate(date, opt('titleFormat')); - t.start = t.visStart = start; - t.end = t.visEnd = end; - - renderResource(getResources.length); - } +function ResourceDayView(element, calendar) { + var t = this; + + + // exports + t.render = render; + + + // imports + ResourceView.call(t, element, calendar, 'resourceDay'); + var opt = t.opt; + var renderResource = t.renderResource; + var skipHiddenDays = t.skipHiddenDays; + var formatDate = calendar.formatDate; + var getResources = t.getResources; + + + function render(date, delta) { + + if (delta) { + addDays(date, delta); + } + skipHiddenDays(date, delta < 0 ? -1 : 1); + + var start = cloneDate(date, true); + var end = addDays(cloneDate(start), 1); + + t.title = formatDate(date, opt('titleFormat')); + + t.start = t.visStart = start; + t.end = t.visEnd = end; + + renderResource(getResources.length); + } + + } \ No newline at end of file diff --git a/src/resource/ResourceEventRenderer.js b/src/resource/ResourceEventRenderer.js index 3d79fb3..fcc0a60 100644 --- a/src/resource/ResourceEventRenderer.js +++ b/src/resource/ResourceEventRenderer.js @@ -24,6 +24,7 @@ function ResourceEventRenderer() { var getMaxMinute = t.getMaxMinute; var getMinMinute = t.getMinMinute; var timePosition = t.timePosition; + var getIsCellAllDay = t.getIsCellAllDay; var colContentLeft = t.colContentLeft; var colContentRight = t.colContentRight; var cellToDate = t.cellToDate; @@ -44,7 +45,9 @@ function ResourceEventRenderer() { var calendar = t.calendar; var formatDate = calendar.formatDate; var formatDates = calendar.formatDates; - var resources = t.getResources; // imported from ResourceView.js + var resources = t.getResources; + + // overrides t.draggableDayEvent = draggableDayEvent; @@ -74,6 +77,7 @@ function ResourceEventRenderer() { renderSlotSegs(compileSlotSegs(slotEvents), modifiedEventId); } + function clearEvents() { getDaySegmentContainer().empty(); getSlotSegmentContainer().empty(); @@ -92,16 +96,13 @@ function ResourceEventRenderer() { segs = []; for (i=0; i= 0) { - resourceEvents.push(events[i]) - } - } - return resourceEvents; - } function sliceSegs(events, visEventEnds, start, end) { var segs = [], @@ -169,57 +161,16 @@ function ResourceEventRenderer() { } return segs.sort(segmentCompare); } - - // event rendering calculation utilities - function compileDaySegs(events) { - var levels = stackSegs(sliceSegs(events, $.map(events, exclEndDay), t.visStart, t.visEnd)), - i, levelCnt = levels.length, - level, - j, seg, - segs = []; - for (i = 0; i < levelCnt; i++) { - level = levels[i]; - for (j = 0; j < level.length; j++) { - seg = level[j]; - seg.row = 0; - seg.level = i; // not needed anymore - segs.push(seg); - } - } - return segs; - } - 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; - } + + function eventsForResource(resource, events) { + var resourceEvents = []; + for (var i = 0; i < events.length; i++) { + if (events[i].resources && $.inArray(resource.id, events[i].resources) >= 0) { + resourceEvents.push(events[i]) + } + } + return resourceEvents; + } function slotEventEnd(event) { if (event.end) { @@ -387,14 +338,20 @@ function ResourceEventRenderer() { } html += " class='" + classes.join(' ') + "'" + - " style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" + + " style=" + + "'" + + "position:absolute;" + + "top:" + seg.top + "px;" + + "left:" + seg.left + "px;" + + skinCss + + "'" + ">" + "
" + "
" + htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) + "
" + "
" + - htmlEscape(event.title) + + htmlEscape(event.title || '') + "
" + "
" + "
"; @@ -441,7 +398,6 @@ function ResourceEventRenderer() { 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) { @@ -527,80 +483,147 @@ function ResourceEventRenderer() { // 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 coordinateGrid = t.getCoordinateGrid(); var colCnt = getColCnt(); var colWidth = getColWidth(); var snapHeight = getSnapHeight(); var snapMinutes = getSnapMinutes(); + + // states + var origPosition; // original position of the element, not the mouse + var origCell; + var isInBounds, prevIsInBounds; + var isAllDay, prevIsAllDay; + var colDelta, prevColDelta; + var dayDelta; // derived from colDelta + var minuteDelta, prevMinuteDelta; + eventElement.draggable({ - zIndex: 9, scroll: false, - grid: [colWidth, snapHeight], + 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); + + coordinateGrid.build(); + + // initialize states origPosition = eventElement.position(); + origCell = coordinateGrid.cell(ev.pageX, ev.pageY); + isInBounds = prevIsInBounds = true; + isAllDay = prevIsAllDay = getIsCellAllDay(origCell); + colDelta = prevColDelta = 0; + dayDelta = 0; 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); + + // NOTE: this `cell` value is only useful for determining in-bounds and all-day. + // Bad for anything else due to the discrepancy between the mouse position and the + // element position while snapping. (problem revealed in PR #55) + // + // PS- the problem exists for draggableDayEvent() when dragging an all-day event to a slot event. + // We should overhaul the dragging system and stop relying on jQuery UI. + var cell = coordinateGrid.cell(ev.pageX, ev.pageY); + + // update states + isInBounds = !!cell; + if (isInBounds) { + isAllDay = getIsCellAllDay(cell); + + // calculate column delta + colDelta = Math.round((ui.position.left - origPosition.left) / colWidth); + if (colDelta != prevColDelta) { + // calculate the day delta based off of the original clicked column and the column delta + var origDate = cellToDate(0, origCell.col); + var col = origCell.col + colDelta; + col = Math.max(0, col); + col = Math.min(colCnt-1, col); + var date = cellToDate(0, col); + dayDelta = dayDiff(date, origDate); } + + // calculate minute delta (only if over slots) + if (!isAllDay) { + minuteDelta = Math.round((ui.position.top - origPosition.top) / snapHeight) * snapMinutes; + } + } + + // any state changes? + if ( + isInBounds != prevIsInBounds || + isAllDay != prevIsAllDay || + colDelta != prevColDelta || + minuteDelta != prevMinuteDelta + ) { + + updateUI(); + + // update previous states for next time + prevIsInBounds = isInBounds; + prevIsAllDay = isAllDay; + prevColDelta = colDelta; prevMinuteDelta = minuteDelta; } + + // if out-of-bounds, revert when done, and vice versa. + eventElement.draggable('option', 'revert', !isInBounds); + }, 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(); + + if (isInBounds && (isAllDay || dayDelta || minuteDelta)) { // changed! + eventDrop(this, event, dayDelta, isAllDay ? 0 : minuteDelta, isAllDay, ev, ui); + } + else { // either no change or out-of-bounds (draggable has already reverted) + + // reset states for next time, and for updateUI() + isInBounds = true; + isAllDay = false; + colDelta = 0; + dayDelta = 0; + minuteDelta = 0; + + updateUI(); eventElement.css('filter', ''); // clear IE opacity side-effects - eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position - updateTimeText(0); + + // sometimes fast drags make event revert to wrong position, so reset. + // also, if we dragged the element out of the area because of snapping, + // but the *mouse* is still in bounds, we need to reset the position. + eventElement.css(origPosition); + showEvents(event, eventElement); } } }); + + function updateUI() { + clearOverlays(); + if (isInBounds) { + if (isAllDay) { + timeElement.hide(); + eventElement.draggable('option', 'grid', null); // disable grid snapping + renderDayOverlay( + addDays(cloneDate(event.start), dayDelta), + addDays(exclEndDay(event), dayDelta) + ); + } + else { + updateTimeText(minuteDelta); + timeElement.css('display', ''); // show() was causing display=inline + eventElement.draggable('option', 'grid', [colWidth, snapHeight]); // re-enable grid snapping + } + } + } + function updateTimeText(minuteDelta) { var newStart = addMinutes(cloneDate(event.start), minuteDelta); var newEnd; @@ -609,14 +632,7 @@ function ResourceEventRenderer() { } 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; - } - } + } @@ -637,7 +653,6 @@ function ResourceEventRenderer() { 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) { @@ -660,7 +675,6 @@ function ResourceEventRenderer() { 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 } diff --git a/src/resource/ResourceView.js b/src/resource/ResourceView.js index 3318950..5f9ee5d 100644 --- a/src/resource/ResourceView.js +++ b/src/resource/ResourceView.js @@ -1,754 +1,896 @@ + +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(); // 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')); + t.renderResource = renderResource; + t.setWidth = setWidth; + t.setHeight = setHeight; + t.afterRender = afterRender; + t.defaultEventEnd = defaultEventEnd; + t.timePosition = timePosition; + t.getIsCellAllDay = getIsCellAllDay; + t.allDayRow = getAllDayRow; + t.getCoordinateGrid = function() { return coordinateGrid }; // specifically for AgendaEventRenderer + 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); + ResourceEventRenderer.call(t); + var opt = t.opt; + var trigger = t.trigger; + 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) - function renderResource(c) { - colCnt = c; - updateOptions(); - if (!dayTable) { - buildSkeleton(); // builds day table, slot area, events containers - } else { - buildDayTable(); // rebuilds day table - clearEvents(); - } - } + 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 tm; + var rtl; + var minMinute, maxMinute; + var colFormat; + var showWeekNumbers; + var weekNumberTitle; + var weekNumberFormat; + var resources = t.getResources; - 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 - -----------------------------------------------------------------------*/ + + /* Rendering + -----------------------------------------------------------------------------*/ + + + disableTextSelection(element.addClass('fc-agenda')); + + + function renderResource(c) { + colCnt = c; + updateOptions(); - 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 - -----------------------------------------------------------------------*/ + if (!dayTable) { // first time rendering? + buildSkeleton(); // builds day table, slot area, events containers + } + else { + buildDayTable(); // rebuilds day table + } + } + + + function updateOptions() { - 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 - } + tm = opt('theme') ? 'ui' : 'fc'; + rtl = opt('isRTL') + minMinute = parseTime(opt('minTime')); + maxMinute = parseTime(opt('maxTime')); + colFormat = opt('columnFormat'); - function buildDayTableHTML() { - var html = - "" + - buildDayTableHeadHTML() + - buildDayTableBodyHTML() + - "
"; - return html; - } + // 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" + - ""; - 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; - } + "" + + htmlEscape(resources[col].name) + + ""; + } - 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 - -----------------------------------------------------------------------*/ + html += + " " + + "" + + ""; - 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(); - } - } + return html; + } - 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 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 = ''; - function scroll() { - slotScroller.scrollTop(top); - } - scroll(); - setTimeout(scroll, 0); // overrides any previous scroll state made by the browser - } + html += + "" + + "" + + " "; - function beforeHide() { - savedScrollTop = slotScroller.scrollTop(); - } + cellsHTML = ''; - function afterShow() { - slotScroller.scrollTop(savedScrollTop); - } - /* Slot/Day clicking and binding - -----------------------------------------------------------------------*/ + for (col=0; col" + + "
" + + "
" + + "
 
" + + "
" + + "
" + + ""; - 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 - ) - ); - } - } + cellsHTML += cellHTML; + } - function renderCellOverlay(row0, col0, row1, col1) { // only for all-day? - var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer); - return renderOverlay(rect, slotLayer); - } + html += cellsHTML; + html += + " " + + "" + + ""; - 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(); + return html; + } - 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); - } + // TODO: data-date on the cells - function colContentLeft(col) { - return colContentPositions.left(col); - } + + + /* Dimensions + -----------------------------------------------------------------------*/ - function colRight(col) { - return colPositions.right(col); - } + + function setHeight(height) { + 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 + ); - function colContentRight(col) { - return colContentPositions.right(col); - } + dayBodyFirstCellStretcher + .height(bodyHeight - vsides(dayBodyFirstCell)); + + slotLayer.css('top', headHeight); + + slotScroller.height(bodyHeight - allDayHeight - 1); + + slotHeight = slotTableFirstInner.height() + 1; // +1 for border - function getIsCellAllDay(cell) { - return opt('allDaySlot') && !cell.row; - } + snapRatio = opt('slotMinutes') / snapMinutes; + snapHeight = slotHeight / snapRatio; + } + + + function setWidth(width) { + viewWidth = width; + colPositions.clear(); + colContentPositions.clear(); - 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) + 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')); + } - 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) - )); - } + 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); + } + - function getAllDayRow(index) { - return allDayRow; - } - function defaultEventEnd(event) { - var start = cloneDate(event.start); - if (event.allDay) { - return start; - } - return addMinutes(start, opt('defaultEventMinutes')); - } - /* Selection - ---------------------------------------------------------------------------------*/ + /* Scrolling + -----------------------------------------------------------------------*/ - 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 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 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 afterRender() { // after the view has been freshly rendered and sized + resetScroll(); + } + + + + /* Slot/Day clicking and binding + -----------------------------------------------------------------------*/ + - 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 dayBind(cells) { + cells.click(slotClick) + .mousedown(daySelectionMousedown); + } - 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 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 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 + + 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).find('td div')[0].offsetTop; + // .eq() is faster than ":eq()" selector + // [0].offsetTop is faster than .position().top (do we really need this optimization?) + // a better optimization would be to cache all these divs + } + 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'; + 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); + } + } + + +} diff --git a/tests/.DS_Store b/tests/.DS_Store deleted file mode 100644 index d14b4e63b57c73f1fee9c7a91f70c6f23323d16a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15364 zcmeHM&2k$>5bm`_m5r6yDFOikRf1D4NjX^l;igJCROK3~Qn?A)lC2Q^v0f)}PCDih zcmy7W0}sF>tSy zfp-dUd}!cg*(%FvEW^-&OIQM+tl@Sz@Ez-bfN7Lvt1PFn3{Y^!)k9W7vvP@%ML3=} zSRG}nET^%IaLFQEva;o@T%ly4b>JGIx@4_b`qdS11r`6R87zB*!w4XShBW3-{X`?mBR}jAepP2Mh!38W`B@ z@f`Lu)O`{58VXoV>kFK1TyvBoIg=AG=dj*l{C$pb6Nq!zFo4%Rcyb!NH_$wzp%ZG1J?#!j$1jelEy49p|6Fv~bQPEcI6Ft2!1 znqcaHa{|6&sPh=V3fM8m{cTenD*h@_{L4AZcZKTokna<)u_woUxXZp_Y7%r$nQtlP z9pQ(uWbYoKck_B`*~vUrc545duyP-4+$B80SwMllJbc%at97-lY$BJx1@9?TWWKRC zF^{cWqnd9MHFf?rVI!@a#k(yp;dd9DeUp>>1}t{jBE|BoZVzQ6J`(N5d62R|$OcqMipQMCMl77DBLbPxU-wH0c`@wU4}{v$6|4-&sOfFGCdM7t-#V}<%8pg6*4^*KTo zc*Yo1WQgLrh0&060gi?pFvby)vxHe(O*Y1H^_Rq-+e@*{-WT^^?<*-5RaQ1`C9F$W z#SuYSb<@CKVFyQj#q$w(I3D*Avn~|ktb{WFj#3w<;^;++rLI^iwXJ=cXj?}sd~fkB z$hm?MVOp(l9|AQ%Z23+yFjP!oZo((}Hk&QPxz`i0^pNKk4}4WTo8ah$jO1JViuo(E zsrDxE_y*9_ItNx~r_p}!J!SY94160;z~#W4h7pJJY9kJes6yUA^m_O_M@Eb=mmOl(#hHT1z(^_vO!xh81(sG|HP&o! z|No)=|Nqi_UeOhB1^#;qWVOTZ4u$5;f$y!doW>G&aQyp!24ruOcebzpm)?otd`GREef__%JFZsSTA%l5{U17> oUt9rKz!h)>Tme_W6>tSy0aw5ka0OfeSHKl;1zZ7F;D4#WU%>BCIsgCw diff --git a/tests/data_as_a_function.html b/tests/data_as_a_function.html index 86bb84f..b7ebe6b 100644 --- a/tests/data_as_a_function.html +++ b/tests/data_as_a_function.html @@ -20,7 +20,7 @@ }, editable: true, events: { - url: "many_events_json.txt", + url: "many_events_json.php", data: function() { var custom_data = { q: 'custom data value' }; // should see this in Networking console.log('setting custom_data as part of the eventSource fetch'); diff --git a/tests/hiddenDays.html b/tests/hiddenDays.html index c1b8cfe..97fa430 100644 --- a/tests/hiddenDays.html +++ b/tests/hiddenDays.html @@ -17,7 +17,7 @@ $('#calendar').fullCalendar({ header: { - left: 'prev,next today', + left: 'prev next today', center: 'title', right: 'month,agendaWeek,basicWeek,agendaDay,basicDay' }, @@ -27,7 +27,7 @@ month: 6, // july //weekNumbers: true, - isRTL: true, + isRTL: false, //selectable: true, //firstDay: 2, //weekends: false, diff --git a/tests/index.html b/tests/index.html new file mode 100644 index 0000000..a196cfd --- /dev/null +++ b/tests/index.html @@ -0,0 +1,81 @@ + + + + + + +
+ + + diff --git a/tests/issue_230_height_json_events.html b/tests/issue_230_height_json_events.html index 5c02dab..bd2ab13 100644 --- a/tests/issue_230_height_json_events.html +++ b/tests/issue_230_height_json_events.html @@ -16,7 +16,7 @@ //date: 22, //defaultView: 'agendaWeek', // error also occured with month view editable: true, - events: "../demos/json-events.php" + events: "../demos/json-events.json" }); $('#calendar').fullCalendar('option', 'height', $(window).height()-80); diff --git a/tests/issue_417_refetchEvents.html b/tests/issue_417_refetchEvents.html index fadfb34..e33a9d8 100644 --- a/tests/issue_417_refetchEvents.html +++ b/tests/issue_417_refetchEvents.html @@ -56,6 +56,12 @@ +

+ - click on day button + - click refetchEvents + - click on month button + event shouldn't disappear!!! +

diff --git a/tests/issue_477_event_width.html b/tests/issue_477_event_width.html index 10989f8..a6c71ce 100644 --- a/tests/issue_477_event_width.html +++ b/tests/issue_477_event_width.html @@ -48,6 +48,7 @@ +

// need to change to GMT+2 to recreate this bug!!!!

diff --git a/tests/many_agenda_events_json.txt b/tests/many_agenda_events_json.json similarity index 100% rename from tests/many_agenda_events_json.txt rename to tests/many_agenda_events_json.json diff --git a/tests/resourceDayView.html b/tests/resourceDayView.html index 4d9077c..3b0f5fb 100644 --- a/tests/resourceDayView.html +++ b/tests/resourceDayView.html @@ -1,209 +1,231 @@ - - + + - + -

+

- -

- + + + + + + + + + +

+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +