2010-09-19 07:54:35 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function AgendaView(element, calendar, viewName) {
|
|
|
|
var t = this;
|
|
|
|
|
|
|
|
|
|
|
|
// exports
|
|
|
|
t.renderAgenda = renderAgenda;
|
|
|
|
t.setWidth = setWidth;
|
|
|
|
t.setHeight = setHeight;
|
|
|
|
t.beforeHide = beforeHide;
|
|
|
|
t.afterShow = afterShow;
|
|
|
|
t.defaultEventEnd = defaultEventEnd;
|
|
|
|
t.timePosition = timePosition;
|
|
|
|
t.dayOfWeekCol = dayOfWeekCol;
|
|
|
|
t.cellDate = cellDate;
|
|
|
|
t.cellIsAllDay = cellIsAllDay;
|
|
|
|
t.allDayTR = allDayTR;
|
|
|
|
t.allDayBounds = allDayBounds;
|
|
|
|
t.getHoverListener = function() { return hoverListener };
|
|
|
|
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.getBodyContent = function() { return bodyContent };
|
|
|
|
t.getRowCnt = function() { return 1 };
|
|
|
|
t.getColCnt = function() { return colCnt };
|
|
|
|
t.getColWidth = function() { return colWidth };
|
|
|
|
t.getSlotHeight = function() { return slotHeight };
|
|
|
|
t.defaultSelectionEnd = defaultSelectionEnd;
|
|
|
|
t.renderDayOverlay = renderDayOverlay;
|
|
|
|
t.renderSelection = renderSelection;
|
|
|
|
t.clearSelection = clearSelection;
|
|
|
|
t.dragStart = dragStart;
|
|
|
|
t.dragStop = dragStop;
|
|
|
|
|
|
|
|
|
|
|
|
// 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 formatDate = calendar.formatDate;
|
|
|
|
|
|
|
|
|
|
|
|
// locals
|
|
|
|
var head, body, bodyContent, bodyTable, bg;
|
|
|
|
var colCnt;
|
|
|
|
var slotCnt=0; // spanning all the way across
|
|
|
|
var axisWidth, colWidth, slotHeight;
|
|
|
|
var viewWidth, viewHeight;
|
|
|
|
var savedScrollTop;
|
|
|
|
var tm, firstDay;
|
|
|
|
var nwe; // no weekends (int)
|
|
|
|
var rtl, dis, dit; // day index sign / translate
|
|
|
|
var minMinute, maxMinute;
|
|
|
|
var coordinateGrid;
|
|
|
|
var hoverListener;
|
|
|
|
var colContentPositions;
|
|
|
|
var slotTopCache = {};
|
|
|
|
var selectionHelper;
|
|
|
|
var daySegmentContainer;
|
|
|
|
var slotSegmentContainer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Rendering
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
disableTextSelection(element.addClass('fc-agenda'));
|
|
|
|
|
|
|
|
|
|
|
|
function renderAgenda(c) {
|
|
|
|
|
|
|
|
colCnt = c;
|
|
|
|
|
|
|
|
// update option-derived variables
|
|
|
|
tm = opt('theme') ? 'ui' : 'fc';
|
|
|
|
nwe = opt('weekends') ? 0 : 1;
|
|
|
|
firstDay = opt('firstDay');
|
|
|
|
if (rtl = opt('isRTL')) {
|
|
|
|
dis = -1;
|
|
|
|
dit = colCnt - 1;
|
|
|
|
}else{
|
|
|
|
dis = 1;
|
|
|
|
dit = 0;
|
|
|
|
}
|
|
|
|
minMinute = parseTime(opt('minTime'));
|
|
|
|
maxMinute = parseTime(opt('maxTime'));
|
|
|
|
|
|
|
|
var d0 = rtl ? addDays(cloneDate(t.visEnd), -1) : cloneDate(t.visStart),
|
|
|
|
d = cloneDate(d0),
|
|
|
|
today = clearTime(new Date()),
|
|
|
|
colFormat = opt('columnFormat');
|
|
|
|
|
|
|
|
if (!head) { // first time rendering, build from scratch
|
|
|
|
|
|
|
|
var i,
|
|
|
|
minutes,
|
|
|
|
slotNormal = opt('slotMinutes') % 15 == 0, //...
|
|
|
|
|
|
|
|
// head
|
|
|
|
s = "<div class='fc-agenda-head' style='position:relative;z-index:4'>" +
|
|
|
|
"<table style='width:100%'>" +
|
|
|
|
"<tr class='fc-first" + (opt('allDaySlot') ? '' : ' fc-last') + "'>" +
|
|
|
|
"<th class='fc-leftmost " +
|
|
|
|
tm + "-state-default'> </th>";
|
|
|
|
for (i=0; i<colCnt; i++) {
|
|
|
|
s += "<th class='fc-" +
|
|
|
|
dayIDs[d.getDay()] + ' ' + // needs to be first
|
|
|
|
tm + '-state-default' +
|
|
|
|
"'>" + formatDate(d, colFormat) + "</th>";
|
|
|
|
addDays(d, dis);
|
|
|
|
if (nwe) {
|
|
|
|
skipWeekend(d, dis);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s += "<th class='" + tm + "-state-default'> </th></tr>";
|
|
|
|
if (opt('allDaySlot')) {
|
|
|
|
s += "<tr class='fc-all-day'>" +
|
|
|
|
"<th class='fc-axis fc-leftmost " + tm + "-state-default'>" + opt('allDayText') + "</th>" +
|
|
|
|
"<td colspan='" + colCnt + "' class='" + tm + "-state-default'>" +
|
|
|
|
"<div class='fc-day-content'><div style='position:relative'> </div></div></td>" +
|
|
|
|
"<th class='" + tm + "-state-default'> </th>" +
|
|
|
|
"</tr><tr class='fc-divider fc-last'><th colspan='" + (colCnt+2) + "' class='" +
|
|
|
|
tm + "-state-default fc-leftmost'><div/></th></tr>";
|
|
|
|
}
|
|
|
|
s+= "</table></div>";
|
|
|
|
head = $(s).appendTo(element);
|
|
|
|
dayBind(head.find('td'));
|
|
|
|
|
|
|
|
// all-day event container
|
|
|
|
daySegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(head);
|
|
|
|
|
|
|
|
// body
|
|
|
|
d = zeroDate();
|
|
|
|
var maxd = addMinutes(cloneDate(d), maxMinute);
|
|
|
|
addMinutes(d, minMinute);
|
|
|
|
s = "<table>";
|
|
|
|
for (i=0; d < maxd; i++) {
|
|
|
|
minutes = d.getMinutes();
|
|
|
|
s += "<tr class='" +
|
|
|
|
(!i ? 'fc-first' : (!minutes ? '' : 'fc-minor')) +
|
|
|
|
"'><th class='fc-axis fc-leftmost " + tm + "-state-default'>" +
|
|
|
|
((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') +
|
|
|
|
"</th><td class='fc-slot" + i + ' ' +
|
|
|
|
tm + "-state-default'><div style='position:relative'> </div></td></tr>";
|
|
|
|
addMinutes(d, opt('slotMinutes'));
|
|
|
|
slotCnt++;
|
|
|
|
}
|
|
|
|
s += "</table>";
|
|
|
|
body = $("<div class='fc-agenda-body' style='position:relative;z-index:2;overflow:auto'/>")
|
|
|
|
.append(bodyContent = $("<div style='position:relative;overflow:hidden'>")
|
|
|
|
.append(bodyTable = $(s)))
|
|
|
|
.appendTo(element);
|
|
|
|
slotBind(body.find('td'));
|
|
|
|
|
|
|
|
// slot event container
|
|
|
|
slotSegmentContainer = $("<div style='position:absolute;z-index:8;top:0;left:0'/>").appendTo(bodyContent);
|
|
|
|
|
|
|
|
// background stripes
|
|
|
|
d = cloneDate(d0);
|
|
|
|
s = "<div class='fc-agenda-bg' style='position:absolute;z-index:1'>" +
|
|
|
|
"<table style='width:100%;height:100%'><tr class='fc-first'>";
|
|
|
|
for (i=0; i<colCnt; i++) {
|
|
|
|
s += "<td class='fc-" +
|
|
|
|
dayIDs[d.getDay()] + ' ' + // needs to be first
|
|
|
|
tm + '-state-default ' +
|
|
|
|
(!i ? 'fc-leftmost ' : '') +
|
|
|
|
(+d == +today ? tm + '-state-highlight fc-today' : 'fc-not-today') +
|
|
|
|
"'><div class='fc-day-content'><div> </div></div></td>";
|
|
|
|
addDays(d, dis);
|
|
|
|
if (nwe) {
|
|
|
|
skipWeekend(d, dis);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s += "</tr></table></div>";
|
|
|
|
bg = $(s).appendTo(element);
|
|
|
|
|
|
|
|
}else{ // skeleton already built, just modify it
|
|
|
|
|
|
|
|
clearEvents();
|
|
|
|
|
|
|
|
// redo column header text and class
|
|
|
|
head.find('tr:first th').slice(1, -1).each(function(i, th) {
|
|
|
|
$(th).text(formatDate(d, colFormat));
|
|
|
|
th.className = th.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
|
|
|
addDays(d, dis);
|
|
|
|
if (nwe) {
|
|
|
|
skipWeekend(d, dis);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// change classes of background stripes
|
|
|
|
d = cloneDate(d0);
|
|
|
|
bg.find('td').each(function(i, td) {
|
|
|
|
td.className = td.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[d.getDay()]);
|
|
|
|
if (+d == +today) {
|
|
|
|
$(td)
|
|
|
|
.removeClass('fc-not-today')
|
|
|
|
.addClass('fc-today')
|
|
|
|
.addClass(tm + '-state-highlight');
|
|
|
|
}else{
|
|
|
|
$(td)
|
|
|
|
.addClass('fc-not-today')
|
|
|
|
.removeClass('fc-today')
|
|
|
|
.removeClass(tm + '-state-highlight');
|
|
|
|
}
|
|
|
|
addDays(d, dis);
|
|
|
|
if (nwe) {
|
|
|
|
skipWeekend(d, dis);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setHeight(height, dateChanged) {
|
|
|
|
|
|
|
|
if (height === undefined) {
|
|
|
|
height = viewHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
viewHeight = height;
|
|
|
|
slotTopCache = {};
|
|
|
|
|
|
|
|
body.height(height - head.height());
|
|
|
|
|
|
|
|
slotHeight = body.find('tr:first div').height() + 1;
|
|
|
|
|
|
|
|
bg.css({
|
|
|
|
top: head.find('tr').height(),
|
|
|
|
height: height
|
|
|
|
});
|
|
|
|
|
2010-09-27 02:45:34 +02:00
|
|
|
// if the table ends up shorter than the allotted view, shrink the view to fit the table
|
|
|
|
var tableHeight = body.find('table:first').height();
|
|
|
|
if (tableHeight < body.height()) {
|
|
|
|
body.height(tableHeight);
|
|
|
|
}
|
|
|
|
|
2010-09-19 07:54:35 +02:00
|
|
|
if (dateChanged) {
|
|
|
|
resetScroll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setWidth(width) {
|
|
|
|
viewWidth = width;
|
|
|
|
colContentPositions.clear();
|
|
|
|
|
|
|
|
body.width(width);
|
|
|
|
bodyTable.width('');
|
|
|
|
|
|
|
|
var topTDs = head.find('tr:first th'),
|
|
|
|
stripeTDs = bg.find('td'),
|
|
|
|
clientWidth = body[0].clientWidth;
|
|
|
|
|
|
|
|
bodyTable.width(clientWidth);
|
|
|
|
|
|
|
|
// time-axis width
|
|
|
|
axisWidth = 0;
|
|
|
|
setOuterWidth(
|
|
|
|
head.find('tr:lt(2) th:first').add(body.find('tr:first th'))
|
|
|
|
.width('')
|
|
|
|
.each(function() {
|
|
|
|
axisWidth = Math.max(axisWidth, $(this).outerWidth());
|
|
|
|
}),
|
|
|
|
axisWidth
|
|
|
|
);
|
|
|
|
axisWidth = axisWidth;
|
|
|
|
|
2010-09-27 02:45:34 +02:00
|
|
|
// column width, except for last column
|
2010-09-19 07:54:35 +02:00
|
|
|
colWidth = Math.floor((clientWidth - axisWidth) / colCnt);
|
|
|
|
setOuterWidth(stripeTDs.slice(0, -1), colWidth);
|
|
|
|
setOuterWidth(topTDs.slice(1, -2), colWidth);
|
2010-09-27 02:45:34 +02:00
|
|
|
|
|
|
|
// column width for last column
|
|
|
|
var hasScrollbar = body[0].scrollHeight != body[0].clientHeight;
|
|
|
|
if (hasScrollbar) {
|
|
|
|
setOuterWidth(topTDs.slice(-2, -1), clientWidth - axisWidth - colWidth*(colCnt-1));
|
|
|
|
}else{
|
|
|
|
topTDs.slice(-1).hide();
|
|
|
|
$('tr.fc-all-day th').slice(-1).hide();
|
|
|
|
}
|
2010-09-19 07:54:35 +02:00
|
|
|
|
|
|
|
bg.css({
|
|
|
|
left: axisWidth,
|
|
|
|
width: clientWidth - axisWidth
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function resetScroll() {
|
|
|
|
var d0 = zeroDate(),
|
|
|
|
scrollDate = cloneDate(d0);
|
|
|
|
scrollDate.setHours(opt('firstHour'));
|
|
|
|
var top = timePosition(d0, scrollDate) + 1, // +1 for the border
|
|
|
|
scroll = function() {
|
|
|
|
body.scrollTop(top);
|
|
|
|
};
|
|
|
|
scroll();
|
|
|
|
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function beforeHide() {
|
|
|
|
savedScrollTop = body.scrollTop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function afterShow() {
|
|
|
|
body.scrollTop(savedScrollTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Slot/Day clicking and binding
|
|
|
|
-----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function dayBind(tds) {
|
|
|
|
tds.click(slotClick)
|
|
|
|
.mousedown(daySelectionMousedown);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function slotBind(tds) {
|
|
|
|
tds.click(slotClick)
|
|
|
|
.mousedown(slotSelectionMousedown);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function slotClick(ev) {
|
|
|
|
if (!opt('selectable')) { // SelectionManager will worry about dayClick
|
|
|
|
var col = Math.min(colCnt-1, Math.floor((ev.pageX - bg.offset().left) / colWidth)),
|
|
|
|
date = addDays(cloneDate(t.visStart), col*dis+dit),
|
|
|
|
rowMatch = this.className.match(/fc-slot(\d+)/);
|
|
|
|
if (rowMatch) {
|
|
|
|
var mins = parseInt(rowMatch[1]) * opt('slotMinutes'),
|
|
|
|
hours = Math.floor(mins/60);
|
|
|
|
date.setHours(hours);
|
|
|
|
date.setMinutes(mins%60 + minMinute);
|
|
|
|
trigger('dayClick', this, date, false, ev);
|
|
|
|
}else{
|
|
|
|
trigger('dayClick', this, date, true, ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Semi-transparent Overlay Helpers
|
|
|
|
-----------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function renderDayOverlay(startDate, endDate, refreshCoordinateGrid) { // endDate is exclusive
|
|
|
|
if (refreshCoordinateGrid) {
|
|
|
|
coordinateGrid.build();
|
|
|
|
}
|
|
|
|
var visStart = cloneDate(t.visStart);
|
|
|
|
var startCol, endCol;
|
|
|
|
if (rtl) {
|
|
|
|
startCol = dayDiff(endDate, visStart)*dis+dit+1;
|
|
|
|
endCol = dayDiff(startDate, visStart)*dis+dit+1;
|
|
|
|
}else{
|
|
|
|
startCol = dayDiff(startDate, visStart);
|
|
|
|
endCol = dayDiff(endDate, visStart);
|
|
|
|
}
|
|
|
|
startCol = Math.max(0, startCol);
|
|
|
|
endCol = Math.min(colCnt, endCol);
|
|
|
|
if (startCol < endCol) {
|
|
|
|
dayBind(
|
|
|
|
renderCellOverlay(0, startCol, 0, endCol-1)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderCellOverlay(col0, row0, col1, row1) {
|
|
|
|
var rect = coordinateGrid.rect(col0, row0, col1, row1, head);
|
|
|
|
return renderOverlay(rect, head);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function renderSlotOverlay(overlayStart, overlayEnd) {
|
|
|
|
var dayStart = cloneDate(t.visStart);
|
|
|
|
var dayEnd = addDays(cloneDate(dayStart), 1);
|
|
|
|
for (var i=0; i<colCnt; i++) {
|
|
|
|
var stretchStart = new Date(Math.max(dayStart, overlayStart));
|
|
|
|
var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
|
|
|
|
if (stretchStart < stretchEnd) {
|
|
|
|
var col = i*dis+dit;
|
|
|
|
var rect = coordinateGrid.rect(0, col, 0, col, bodyContent); // 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, bodyContent)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
addDays(dayStart, 1);
|
|
|
|
addDays(dayEnd, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Coordinate Utilities
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
coordinateGrid = new CoordinateGrid(function(rows, cols) {
|
|
|
|
var e, n, p;
|
|
|
|
bg.find('td').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 = head.find('td');
|
|
|
|
n = e.offset().top;
|
|
|
|
rows[0] = [n, n+e.outerHeight()];
|
|
|
|
}
|
|
|
|
var bodyContentTop = bodyContent.offset().top;
|
|
|
|
var bodyTop = body.offset().top;
|
|
|
|
var bodyBottom = bodyTop + body.outerHeight();
|
|
|
|
function constrain(n) {
|
|
|
|
return Math.max(bodyTop, Math.min(bodyBottom, n));
|
|
|
|
}
|
|
|
|
for (var i=0; i<slotCnt; i++) {
|
|
|
|
rows.push([
|
|
|
|
constrain(bodyContentTop + slotHeight*i),
|
|
|
|
constrain(bodyContentTop + slotHeight*(i+1))
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
hoverListener = new HoverListener(coordinateGrid);
|
|
|
|
|
|
|
|
|
|
|
|
colContentPositions = new HorizontalPositionCache(function(col) {
|
|
|
|
return bg.find('td:eq(' + col + ') div div');
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function colContentLeft(col) {
|
|
|
|
return axisWidth + colContentPositions.left(col);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function colContentRight(col) {
|
|
|
|
return axisWidth + colContentPositions.right(col);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function dayOfWeekCol(dayOfWeek) {
|
|
|
|
return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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 bodyContent.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] = body.find('tr:eq(' + slotI + ') td div')[0].offsetTop;
|
|
|
|
}
|
|
|
|
return Math.max(0, Math.round(
|
|
|
|
slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function cellDate(cell) {
|
|
|
|
var d = addDays(cloneDate(t.visStart), cell.col*dis+dit);
|
|
|
|
var slotIndex = cell.row;
|
|
|
|
if (opt('allDaySlot')) {
|
|
|
|
slotIndex--;
|
|
|
|
}
|
|
|
|
if (slotIndex >= 0) {
|
|
|
|
addMinutes(d, minMinute + slotIndex*opt('slotMinutes'));
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function cellIsAllDay(cell) {
|
|
|
|
return opt('allDaySlot') && !cell.row;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function allDayBounds() {
|
|
|
|
return {
|
|
|
|
left: axisWidth,
|
|
|
|
right: viewWidth
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function allDayTR(index) {
|
|
|
|
return head.find('tr.fc-all-day');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
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 = dayDiff(startDate, t.visStart) * dis + dit;
|
|
|
|
if (col >= 0 && col < colCnt) { // only works when times are on same day
|
|
|
|
var rect = coordinateGrid.rect(0, col, 0, col, bodyContent); // 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(bodyContent);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
selectionHelper = $(slotSegHtml(
|
|
|
|
{
|
|
|
|
title: '',
|
|
|
|
start: startDate,
|
|
|
|
end: endDate,
|
|
|
|
className: [],
|
|
|
|
editable: false
|
|
|
|
},
|
|
|
|
rect,
|
|
|
|
'fc-event fc-event-vert fc-corner-top fc-corner-bottom '
|
|
|
|
));
|
|
|
|
if ($.browser.msie) {
|
|
|
|
selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
|
|
|
|
}
|
|
|
|
selectionHelper.css('opacity', opt('dragOpacity'));
|
|
|
|
}
|
|
|
|
if (selectionHelper) {
|
|
|
|
slotBind(selectionHelper);
|
|
|
|
bodyContent.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 (opt('selectable')) {
|
|
|
|
unselect(ev);
|
|
|
|
var _mousedownElement = this;
|
|
|
|
var dates;
|
|
|
|
hoverListener.start(function(cell, origCell) {
|
|
|
|
clearSelection();
|
|
|
|
if (cell && cell.col == origCell.col && !cellIsAllDay(cell)) {
|
|
|
|
var d1 = cellDate(origCell);
|
|
|
|
var d2 = cellDate(cell);
|
|
|
|
dates = [
|
|
|
|
d1,
|
|
|
|
addMinutes(cloneDate(d1), opt('slotMinutes')),
|
|
|
|
d2,
|
|
|
|
addMinutes(cloneDate(d2), opt('slotMinutes'))
|
|
|
|
].sort(cmp);
|
|
|
|
renderSlotSelection(dates[0], dates[3]);
|
|
|
|
}else{
|
|
|
|
dates = null;
|
|
|
|
}
|
|
|
|
}, ev);
|
|
|
|
$(document).one('mouseup', function(ev) {
|
|
|
|
hoverListener.stop();
|
|
|
|
if (dates) {
|
|
|
|
if (+dates[0] == +dates[1]) {
|
|
|
|
trigger('dayClick', _mousedownElement, dates[0], false, ev);
|
|
|
|
// BUG: _mousedownElement will sometimes be the overlay
|
|
|
|
}
|
|
|
|
reportSelection(dates[0], dates[3], false, ev);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* External Dragging
|
|
|
|
--------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function dragStart(_dragElement, ev, ui) {
|
|
|
|
hoverListener.start(function(cell) {
|
|
|
|
clearOverlays();
|
|
|
|
if (cell) {
|
|
|
|
if (cellIsAllDay(cell)) {
|
|
|
|
renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
|
|
|
|
}else{
|
|
|
|
var d1 = cellDate(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, cellDate(cell), cellIsAllDay(cell), ev, ui);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|