|
@@ -2,22 +2,39 @@
|
|
|
setDefaults({
|
|
setDefaults({
|
|
|
allDaySlot: true,
|
|
allDaySlot: true,
|
|
|
allDayText: 'all-day',
|
|
allDayText: 'all-day',
|
|
|
- firstHour: 6,
|
|
|
|
|
- slotMinutes: 30,
|
|
|
|
|
- defaultEventMinutes: 120,
|
|
|
|
|
- axisFormat: 'h(:mm)tt',
|
|
|
|
|
|
|
+
|
|
|
|
|
+ scrollTime: '06:00:00',
|
|
|
|
|
+
|
|
|
|
|
+ slotDuration: '00:30:00',
|
|
|
|
|
+
|
|
|
|
|
+ axisFormat: generateAgendaAxisFormat,
|
|
|
timeFormat: {
|
|
timeFormat: {
|
|
|
- agenda: 'h:mm{ - h:mm}'
|
|
|
|
|
|
|
+ agenda: generateAgendaTimeFormat
|
|
|
},
|
|
},
|
|
|
|
|
+
|
|
|
dragOpacity: {
|
|
dragOpacity: {
|
|
|
agenda: .5
|
|
agenda: .5
|
|
|
},
|
|
},
|
|
|
- minTime: 0,
|
|
|
|
|
- maxTime: 24,
|
|
|
|
|
|
|
+ minTime: '00:00:00',
|
|
|
|
|
+ maxTime: '24:00:00',
|
|
|
slotEventOverlap: true
|
|
slotEventOverlap: true
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+function generateAgendaAxisFormat(options, langData) {
|
|
|
|
|
+ return langData.longDateFormat('LT')
|
|
|
|
|
+ .replace(':mm', '(:mm)')
|
|
|
|
|
+ .replace(/(\Wmm)$/, '($1)') // like above, but for foreign langs
|
|
|
|
|
+ .replace(/\s*a$/i, 'a'); // convert AM/PM/am/pm to lowercase. remove any spaces beforehand
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+function generateAgendaTimeFormat(options, langData) {
|
|
|
|
|
+ return langData.longDateFormat('LT')
|
|
|
|
|
+ .replace(/\s*a$/i, ''); // remove trailing AM/PM
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
// TODO: make it work in quirks mode (event corners, all-day height)
|
|
// TODO: make it work in quirks mode (event corners, all-day height)
|
|
|
// TODO: test liquid width, especially in IE6
|
|
// TODO: test liquid width, especially in IE6
|
|
|
|
|
|
|
@@ -31,10 +48,9 @@ function AgendaView(element, calendar, viewName) {
|
|
|
t.setWidth = setWidth;
|
|
t.setWidth = setWidth;
|
|
|
t.setHeight = setHeight;
|
|
t.setHeight = setHeight;
|
|
|
t.afterRender = afterRender;
|
|
t.afterRender = afterRender;
|
|
|
- t.defaultEventEnd = defaultEventEnd;
|
|
|
|
|
- t.timePosition = timePosition;
|
|
|
|
|
|
|
+ t.computeDateTop = computeDateTop;
|
|
|
t.getIsCellAllDay = getIsCellAllDay;
|
|
t.getIsCellAllDay = getIsCellAllDay;
|
|
|
- t.allDayRow = getAllDayRow;
|
|
|
|
|
|
|
+ t.allDayRow = function() { return allDayRow }; // badly named
|
|
|
t.getCoordinateGrid = function() { return coordinateGrid }; // specifically for AgendaEventRenderer
|
|
t.getCoordinateGrid = function() { return coordinateGrid }; // specifically for AgendaEventRenderer
|
|
|
t.getHoverListener = function() { return hoverListener };
|
|
t.getHoverListener = function() { return hoverListener };
|
|
|
t.colLeft = colLeft;
|
|
t.colLeft = colLeft;
|
|
@@ -43,14 +59,16 @@ function AgendaView(element, calendar, viewName) {
|
|
|
t.colContentRight = colContentRight;
|
|
t.colContentRight = colContentRight;
|
|
|
t.getDaySegmentContainer = function() { return daySegmentContainer };
|
|
t.getDaySegmentContainer = function() { return daySegmentContainer };
|
|
|
t.getSlotSegmentContainer = function() { return slotSegmentContainer };
|
|
t.getSlotSegmentContainer = function() { return slotSegmentContainer };
|
|
|
- t.getMinMinute = function() { return minMinute };
|
|
|
|
|
- t.getMaxMinute = function() { return maxMinute };
|
|
|
|
|
t.getSlotContainer = function() { return slotContainer };
|
|
t.getSlotContainer = function() { return slotContainer };
|
|
|
t.getRowCnt = function() { return 1 };
|
|
t.getRowCnt = function() { return 1 };
|
|
|
t.getColCnt = function() { return colCnt };
|
|
t.getColCnt = function() { return colCnt };
|
|
|
t.getColWidth = function() { return colWidth };
|
|
t.getColWidth = function() { return colWidth };
|
|
|
t.getSnapHeight = function() { return snapHeight };
|
|
t.getSnapHeight = function() { return snapHeight };
|
|
|
- t.getSnapMinutes = function() { return snapMinutes };
|
|
|
|
|
|
|
+ t.getSnapDuration = function() { return snapDuration };
|
|
|
|
|
+ t.getSlotHeight = function() { return slotHeight };
|
|
|
|
|
+ t.getSlotDuration = function() { return slotDuration };
|
|
|
|
|
+ t.getMinTime = function() { return minTime };
|
|
|
|
|
+ t.getMaxTime = function() { return maxTime };
|
|
|
t.defaultSelectionEnd = defaultSelectionEnd;
|
|
t.defaultSelectionEnd = defaultSelectionEnd;
|
|
|
t.renderDayOverlay = renderDayOverlay;
|
|
t.renderDayOverlay = renderDayOverlay;
|
|
|
t.renderSelection = renderSelection;
|
|
t.renderSelection = renderSelection;
|
|
@@ -76,7 +94,9 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var cellToDate = t.cellToDate;
|
|
var cellToDate = t.cellToDate;
|
|
|
var dateToCell = t.dateToCell;
|
|
var dateToCell = t.dateToCell;
|
|
|
var rangeToSegments = t.rangeToSegments;
|
|
var rangeToSegments = t.rangeToSegments;
|
|
|
|
|
+ var calendar = t.calendar;
|
|
|
var formatDate = calendar.formatDate;
|
|
var formatDate = calendar.formatDate;
|
|
|
|
|
+ var calculateWeekNumber = calendar.calculateWeekNumber;
|
|
|
|
|
|
|
|
|
|
|
|
|
// locals
|
|
// locals
|
|
@@ -105,9 +125,11 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var axisWidth;
|
|
var axisWidth;
|
|
|
var colWidth;
|
|
var colWidth;
|
|
|
var gutterWidth;
|
|
var gutterWidth;
|
|
|
|
|
+
|
|
|
|
|
+ var slotDuration;
|
|
|
var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
|
|
var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
|
|
|
|
|
|
|
|
- var snapMinutes;
|
|
|
|
|
|
|
+ var snapDuration;
|
|
|
var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4)
|
|
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 snapHeight; // holds the pixel hight of a "selection" slot
|
|
|
|
|
|
|
@@ -121,11 +143,9 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
var tm;
|
|
var tm;
|
|
|
var rtl;
|
|
var rtl;
|
|
|
- var minMinute, maxMinute;
|
|
|
|
|
|
|
+ var minTime;
|
|
|
|
|
+ var maxTime;
|
|
|
var colFormat;
|
|
var colFormat;
|
|
|
- var showWeekNumbers;
|
|
|
|
|
- var weekNumberTitle;
|
|
|
|
|
- var weekNumberFormat;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -153,21 +173,14 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
tm = opt('theme') ? 'ui' : 'fc';
|
|
tm = opt('theme') ? 'ui' : 'fc';
|
|
|
rtl = opt('isRTL')
|
|
rtl = opt('isRTL')
|
|
|
- minMinute = parseTime(opt('minTime'));
|
|
|
|
|
- maxMinute = parseTime(opt('maxTime'));
|
|
|
|
|
colFormat = opt('columnFormat');
|
|
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";
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ minTime = moment.duration(opt('minTime'));
|
|
|
|
|
+ maxTime = moment.duration(opt('maxTime'));
|
|
|
|
|
|
|
|
- snapMinutes = opt('snapMinutes') || opt('slotMinutes');
|
|
|
|
|
|
|
+ slotDuration = moment.duration(opt('slotDuration'));
|
|
|
|
|
+ snapDuration = opt('snapDuration');
|
|
|
|
|
+ snapDuration = snapDuration ? moment.duration(snapDuration) : slotDuration;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -177,14 +190,13 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
|
|
|
|
|
function buildSkeleton() {
|
|
function buildSkeleton() {
|
|
|
|
|
+ var s;
|
|
|
var headerClass = tm + "-widget-header";
|
|
var headerClass = tm + "-widget-header";
|
|
|
var contentClass = tm + "-widget-content";
|
|
var contentClass = tm + "-widget-content";
|
|
|
- var s;
|
|
|
|
|
- var d;
|
|
|
|
|
- var i;
|
|
|
|
|
- var maxd;
|
|
|
|
|
|
|
+ var slotTime;
|
|
|
|
|
+ var slotDate;
|
|
|
var minutes;
|
|
var minutes;
|
|
|
- var slotNormal = opt('slotMinutes') % 15 == 0;
|
|
|
|
|
|
|
+ var slotNormal = slotDuration.asMinutes() % 15 == 0;
|
|
|
|
|
|
|
|
buildDayTable();
|
|
buildDayTable();
|
|
|
|
|
|
|
@@ -201,7 +213,12 @@ function AgendaView(element, calendar, viewName) {
|
|
|
s =
|
|
s =
|
|
|
"<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
|
|
"<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
|
|
|
"<tr>" +
|
|
"<tr>" +
|
|
|
- "<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
|
|
|
|
|
|
|
+ "<th class='" + headerClass + " fc-agenda-axis'>" +
|
|
|
|
|
+ (
|
|
|
|
|
+ opt('allDayHTML') ||
|
|
|
|
|
+ htmlEscape(opt('allDayText'))
|
|
|
|
|
+ ) +
|
|
|
|
|
+ "</th>" +
|
|
|
"<td>" +
|
|
"<td>" +
|
|
|
"<div class='fc-day-content'><div style='position:relative'/></div>" +
|
|
"<div class='fc-day-content'><div style='position:relative'/></div>" +
|
|
|
"</td>" +
|
|
"</td>" +
|
|
@@ -240,27 +257,32 @@ function AgendaView(element, calendar, viewName) {
|
|
|
s =
|
|
s =
|
|
|
"<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
|
|
"<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
|
|
|
"<tbody>";
|
|
"<tbody>";
|
|
|
- d = zeroDate();
|
|
|
|
|
- maxd = addMinutes(cloneDate(d), maxMinute);
|
|
|
|
|
- addMinutes(d, minMinute);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ slotTime = moment.duration(+minTime); // i wish there was .clone() for durations
|
|
|
slotCnt = 0;
|
|
slotCnt = 0;
|
|
|
- for (i=0; d < maxd; i++) {
|
|
|
|
|
- minutes = d.getMinutes();
|
|
|
|
|
|
|
+ while (slotTime < maxTime) {
|
|
|
|
|
+ slotDate = t.start.clone().time(slotTime); // will be in UTC but that's good. to avoid DST issues
|
|
|
|
|
+ minutes = slotDate.minutes();
|
|
|
s +=
|
|
s +=
|
|
|
- "<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
|
|
|
|
|
|
|
+ "<tr class='fc-slot" + slotCnt + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
|
|
|
"<th class='fc-agenda-axis " + headerClass + "'>" +
|
|
"<th class='fc-agenda-axis " + headerClass + "'>" +
|
|
|
- ((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') +
|
|
|
|
|
|
|
+ ((!slotNormal || !minutes) ?
|
|
|
|
|
+ htmlEscape(formatDate(slotDate, opt('axisFormat'))) :
|
|
|
|
|
+ ' '
|
|
|
|
|
+ ) +
|
|
|
"</th>" +
|
|
"</th>" +
|
|
|
"<td class='" + contentClass + "'>" +
|
|
"<td class='" + contentClass + "'>" +
|
|
|
"<div style='position:relative'> </div>" +
|
|
"<div style='position:relative'> </div>" +
|
|
|
"</td>" +
|
|
"</td>" +
|
|
|
"</tr>";
|
|
"</tr>";
|
|
|
- addMinutes(d, opt('slotMinutes'));
|
|
|
|
|
|
|
+ slotTime.add(slotDuration);
|
|
|
slotCnt++;
|
|
slotCnt++;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
s +=
|
|
s +=
|
|
|
"</tbody>" +
|
|
"</tbody>" +
|
|
|
"</table>";
|
|
"</table>";
|
|
|
|
|
+
|
|
|
slotTable = $(s).appendTo(slotContainer);
|
|
slotTable = $(s).appendTo(slotContainer);
|
|
|
|
|
|
|
|
slotBind(slotTable.find('td'));
|
|
slotBind(slotTable.find('td'));
|
|
@@ -319,14 +341,14 @@ function AgendaView(element, calendar, viewName) {
|
|
|
"<thead>" +
|
|
"<thead>" +
|
|
|
"<tr>";
|
|
"<tr>";
|
|
|
|
|
|
|
|
- if (showWeekNumbers) {
|
|
|
|
|
|
|
+ if (opt('weekNumbers')) {
|
|
|
date = cellToDate(0, 0);
|
|
date = cellToDate(0, 0);
|
|
|
- weekText = formatDate(date, weekNumberFormat);
|
|
|
|
|
|
|
+ weekText = calculateWeekNumber(date);
|
|
|
if (rtl) {
|
|
if (rtl) {
|
|
|
- weekText += weekNumberTitle;
|
|
|
|
|
|
|
+ weekText += opt('weekNumberTitle');
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
- weekText = weekNumberTitle + weekText;
|
|
|
|
|
|
|
+ weekText = opt('weekNumberTitle') + weekText;
|
|
|
}
|
|
}
|
|
|
html +=
|
|
html +=
|
|
|
"<th class='fc-agenda-axis fc-week-number " + headerClass + "'>" +
|
|
"<th class='fc-agenda-axis fc-week-number " + headerClass + "'>" +
|
|
@@ -340,7 +362,7 @@ function AgendaView(element, calendar, viewName) {
|
|
|
for (col=0; col<colCnt; col++) {
|
|
for (col=0; col<colCnt; col++) {
|
|
|
date = cellToDate(0, col);
|
|
date = cellToDate(0, col);
|
|
|
html +=
|
|
html +=
|
|
|
- "<th class='fc-" + dayIDs[date.getDay()] + " fc-col" + col + ' ' + headerClass + "'>" +
|
|
|
|
|
|
|
+ "<th class='fc-" + dayIDs[date.day()] + " fc-col" + col + ' ' + headerClass + "'>" +
|
|
|
htmlEscape(formatDate(date, colFormat)) +
|
|
htmlEscape(formatDate(date, colFormat)) +
|
|
|
"</th>";
|
|
"</th>";
|
|
|
}
|
|
}
|
|
@@ -358,7 +380,7 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called
|
|
var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called
|
|
|
var contentClass = tm + "-widget-content";
|
|
var contentClass = tm + "-widget-content";
|
|
|
var date;
|
|
var date;
|
|
|
- var today = clearTime(new Date());
|
|
|
|
|
|
|
+ var today = calendar.getNow().stripTime();
|
|
|
var col;
|
|
var col;
|
|
|
var cellsHTML;
|
|
var cellsHTML;
|
|
|
var cellHTML;
|
|
var cellHTML;
|
|
@@ -378,10 +400,10 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
classNames = [
|
|
classNames = [
|
|
|
'fc-col' + col,
|
|
'fc-col' + col,
|
|
|
- 'fc-' + dayIDs[date.getDay()],
|
|
|
|
|
|
|
+ 'fc-' + dayIDs[date.day()],
|
|
|
contentClass
|
|
contentClass
|
|
|
];
|
|
];
|
|
|
- if (+date == +today) {
|
|
|
|
|
|
|
+ if (date.isSame(today, 'day')) {
|
|
|
classNames.push(
|
|
classNames.push(
|
|
|
tm + '-state-highlight',
|
|
tm + '-state-highlight',
|
|
|
'fc-today'
|
|
'fc-today'
|
|
@@ -447,9 +469,12 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
// the stylesheet guarantees that the first row has no border.
|
|
// the stylesheet guarantees that the first row has no border.
|
|
|
// this allows .height() to work well cross-browser.
|
|
// this allows .height() to work well cross-browser.
|
|
|
- slotHeight = slotTable.find('tr:first').height() + 1; // +1 for bottom border
|
|
|
|
|
|
|
+ var slotHeight0 = slotTable.find('tr:first').height() + 1; // +1 for bottom border
|
|
|
|
|
+ var slotHeight1 = slotTable.find('tr:eq(1)').height();
|
|
|
|
|
+ // HACK: i forget why we do this, but i think a cross-browser issue
|
|
|
|
|
+ slotHeight = (slotHeight0 + slotHeight1) / 2;
|
|
|
|
|
|
|
|
- snapRatio = opt('slotMinutes') / snapMinutes;
|
|
|
|
|
|
|
+ snapRatio = slotDuration / snapDuration;
|
|
|
snapHeight = slotHeight / snapRatio;
|
|
snapHeight = slotHeight / snapRatio;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -507,13 +532,14 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
|
|
|
|
|
function resetScroll() {
|
|
function resetScroll() {
|
|
|
- var d0 = zeroDate();
|
|
|
|
|
- var scrollDate = cloneDate(d0);
|
|
|
|
|
- scrollDate.setHours(opt('firstHour'));
|
|
|
|
|
- var top = timePosition(d0, scrollDate) + 1; // +1 for the border
|
|
|
|
|
|
|
+ var top = computeTimeTop(
|
|
|
|
|
+ moment.duration(opt('scrollTime'))
|
|
|
|
|
+ ) + 1; // +1 for the border
|
|
|
|
|
+
|
|
|
function scroll() {
|
|
function scroll() {
|
|
|
slotScroller.scrollTop(top);
|
|
slotScroller.scrollTop(top);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
scroll();
|
|
scroll();
|
|
|
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
|
|
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
|
|
|
}
|
|
}
|
|
@@ -545,15 +571,24 @@ function AgendaView(element, calendar, viewName) {
|
|
|
if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
|
|
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 col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
|
|
|
var date = cellToDate(0, col);
|
|
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);
|
|
|
|
|
|
|
+ var match = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
|
|
|
|
|
+ if (match) {
|
|
|
|
|
+ var slotIndex = parseInt(match[1]);
|
|
|
|
|
+ date.add(minTime + slotIndex * slotDuration);
|
|
|
|
|
+ date = calendar.rezoneDate(date);
|
|
|
|
|
+ trigger(
|
|
|
|
|
+ 'dayClick',
|
|
|
|
|
+ dayBodyCells[col],
|
|
|
|
|
+ date,
|
|
|
|
|
+ ev
|
|
|
|
|
+ );
|
|
|
}else{
|
|
}else{
|
|
|
- trigger('dayClick', dayBodyCells[col], date, true, ev);
|
|
|
|
|
|
|
+ trigger(
|
|
|
|
|
+ 'dayClick',
|
|
|
|
|
+ dayBodyCells[col],
|
|
|
|
|
+ date,
|
|
|
|
|
+ ev
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -594,15 +629,24 @@ function AgendaView(element, calendar, viewName) {
|
|
|
|
|
|
|
|
|
|
|
|
|
function renderSlotOverlay(overlayStart, overlayEnd) {
|
|
function renderSlotOverlay(overlayStart, overlayEnd) {
|
|
|
- for (var i=0; i<colCnt; i++) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // normalize, because dayStart/dayEnd have stripped time+zone
|
|
|
|
|
+ overlayStart = overlayStart.clone().stripZone();
|
|
|
|
|
+ overlayEnd = overlayEnd.clone().stripZone();
|
|
|
|
|
+
|
|
|
|
|
+ for (var i=0; i<colCnt; i++) { // loop through the day columns
|
|
|
|
|
+
|
|
|
var dayStart = cellToDate(0, 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));
|
|
|
|
|
|
|
+ var dayEnd = dayStart.clone().add('days', 1);
|
|
|
|
|
+
|
|
|
|
|
+ var stretchStart = dayStart < overlayStart ? overlayStart : dayStart; // the max of the two
|
|
|
|
|
+ var stretchEnd = dayEnd < overlayEnd ? dayEnd : overlayEnd; // the min of the two
|
|
|
|
|
+
|
|
|
if (stretchStart < stretchEnd) {
|
|
if (stretchStart < stretchEnd) {
|
|
|
var rect = coordinateGrid.rect(0, i, 0, i, slotContainer); // only use it for horizontal coords
|
|
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);
|
|
|
|
|
|
|
+ var top = computeDateTop(stretchStart, dayStart);
|
|
|
|
|
+ var bottom = computeDateTop(stretchEnd, dayStart);
|
|
|
|
|
+
|
|
|
rect.top = top;
|
|
rect.top = top;
|
|
|
rect.height = bottom - top;
|
|
rect.height = bottom - top;
|
|
|
slotBind(
|
|
slotBind(
|
|
@@ -681,84 +725,92 @@ function AgendaView(element, calendar, viewName) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- function getIsCellAllDay(cell) {
|
|
|
|
|
|
|
+ function getIsCellAllDay(cell) { // TODO: remove because mom.hasTime() from realCellToDate() is better
|
|
|
return opt('allDaySlot') && !cell.row;
|
|
return opt('allDaySlot') && !cell.row;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system
|
|
function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system
|
|
|
- var d = cellToDate(0, cell.col);
|
|
|
|
|
|
|
+ var date = cellToDate(0, cell.col);
|
|
|
var slotIndex = cell.row;
|
|
var slotIndex = cell.row;
|
|
|
|
|
+
|
|
|
if (opt('allDaySlot')) {
|
|
if (opt('allDaySlot')) {
|
|
|
slotIndex--;
|
|
slotIndex--;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (slotIndex >= 0) {
|
|
if (slotIndex >= 0) {
|
|
|
- addMinutes(d, minMinute + slotIndex * snapMinutes);
|
|
|
|
|
|
|
+ date.time(moment.duration(minTime + slotIndex * slotDuration));
|
|
|
|
|
+ date = calendar.rezoneDate(date);
|
|
|
}
|
|
}
|
|
|
- return d;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return date;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- // 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)) {
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ function computeDateTop(date, startOfDayDate) {
|
|
|
|
|
+ return computeTimeTop(
|
|
|
|
|
+ moment.duration(
|
|
|
|
|
+ date.clone().stripZone() - startOfDayDate.clone().stripTime()
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ function computeTimeTop(time) { // time is a duration
|
|
|
|
|
+
|
|
|
|
|
+ if (time < minTime) {
|
|
|
return 0;
|
|
return 0;
|
|
|
}
|
|
}
|
|
|
- if (time >= addMinutes(cloneDate(day), maxMinute)) {
|
|
|
|
|
|
|
+ if (time >= maxTime) {
|
|
|
return slotTable.height();
|
|
return slotTable.height();
|
|
|
}
|
|
}
|
|
|
- var slotMinutes = opt('slotMinutes'),
|
|
|
|
|
- minutes = time.getHours()*60 + time.getMinutes() - minMinute,
|
|
|
|
|
- slotI = Math.floor(minutes / slotMinutes),
|
|
|
|
|
- slotTop = slotTopCache[slotI];
|
|
|
|
|
|
|
+
|
|
|
|
|
+ var slots = (time - minTime) / slotDuration;
|
|
|
|
|
+ var slotIndex = Math.floor(slots);
|
|
|
|
|
+ var slotPartial = slots - slotIndex;
|
|
|
|
|
+ var slotTop = slotTopCache[slotIndex];
|
|
|
|
|
+
|
|
|
|
|
+ // find the position of the corresponding <tr>
|
|
|
|
|
+ // need to use this tecnhique because not all rows are rendered at same height sometimes.
|
|
|
if (slotTop === undefined) {
|
|
if (slotTop === undefined) {
|
|
|
- slotTop = slotTopCache[slotI] =
|
|
|
|
|
- slotTable.find('tr').eq(slotI).find('td div')[0].offsetTop;
|
|
|
|
|
|
|
+ slotTop = slotTopCache[slotIndex] =
|
|
|
|
|
+ slotTable.find('tr').eq(slotIndex).find('td div')[0].offsetTop;
|
|
|
// .eq() is faster than ":eq()" selector
|
|
// .eq() is faster than ":eq()" selector
|
|
|
// [0].offsetTop is faster than .position().top (do we really need this optimization?)
|
|
// [0].offsetTop is faster than .position().top (do we really need this optimization?)
|
|
|
// a better optimization would be to cache all these divs
|
|
// 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'));
|
|
|
|
|
|
|
+
|
|
|
|
|
+ var top =
|
|
|
|
|
+ slotTop - 1 + // because first row doesn't have a top border
|
|
|
|
|
+ slotPartial * slotHeight; // part-way through the row
|
|
|
|
|
+
|
|
|
|
|
+ top = Math.max(top, 0);
|
|
|
|
|
+
|
|
|
|
|
+ return top;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Selection
|
|
/* Selection
|
|
|
---------------------------------------------------------------------------------*/
|
|
---------------------------------------------------------------------------------*/
|
|
|
|
|
+
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- function defaultSelectionEnd(startDate, allDay) {
|
|
|
|
|
- if (allDay) {
|
|
|
|
|
- return cloneDate(startDate);
|
|
|
|
|
|
|
+ function defaultSelectionEnd(start) {
|
|
|
|
|
+ if (start.hasTime()) {
|
|
|
|
|
+ return start.clone().add(slotDuration);
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ return start.clone().add('days', 1);
|
|
|
}
|
|
}
|
|
|
- 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 renderSelection(start, end) {
|
|
|
|
|
+ if (start.hasTime() || end.hasTime()) {
|
|
|
|
|
+ renderSlotSelection(start, end);
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (opt('allDaySlot')) {
|
|
|
|
|
+ renderDayOverlay(start, end, true); // true for refreshing coordinate grid
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -770,8 +822,8 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var col = dateToCell(startDate).col;
|
|
var col = dateToCell(startDate).col;
|
|
|
if (col >= 0 && col < colCnt) { // only works when times are on same day
|
|
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 rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords
|
|
|
- var top = timePosition(startDate, startDate);
|
|
|
|
|
- var bottom = timePosition(startDate, endDate);
|
|
|
|
|
|
|
+ var top = computeDateTop(startDate, startDate);
|
|
|
|
|
+ var bottom = computeDateTop(endDate, startDate);
|
|
|
if (bottom > top) { // protect against selections that are entirely before or after visible range
|
|
if (bottom > top) { // protect against selections that are entirely before or after visible range
|
|
|
rect.top = top;
|
|
rect.top = top;
|
|
|
rect.height = bottom - top;
|
|
rect.height = bottom - top;
|
|
@@ -834,9 +886,9 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var d2 = realCellToDate(cell);
|
|
var d2 = realCellToDate(cell);
|
|
|
dates = [
|
|
dates = [
|
|
|
d1,
|
|
d1,
|
|
|
- addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes
|
|
|
|
|
|
|
+ d1.clone().add(snapDuration), // calculate minutes depending on selection slot minutes
|
|
|
d2,
|
|
d2,
|
|
|
- addMinutes(cloneDate(d2), snapMinutes)
|
|
|
|
|
|
|
+ d2.clone().add(snapDuration)
|
|
|
].sort(dateCompare);
|
|
].sort(dateCompare);
|
|
|
renderSlotSelection(dates[0], dates[3]);
|
|
renderSlotSelection(dates[0], dates[3]);
|
|
|
}else{
|
|
}else{
|
|
@@ -847,17 +899,17 @@ function AgendaView(element, calendar, viewName) {
|
|
|
hoverListener.stop();
|
|
hoverListener.stop();
|
|
|
if (dates) {
|
|
if (dates) {
|
|
|
if (+dates[0] == +dates[1]) {
|
|
if (+dates[0] == +dates[1]) {
|
|
|
- reportDayClick(dates[0], false, ev);
|
|
|
|
|
|
|
+ reportDayClick(dates[0], ev);
|
|
|
}
|
|
}
|
|
|
- reportSelection(dates[0], dates[3], false, ev);
|
|
|
|
|
|
|
+ reportSelection(dates[0], dates[3], ev);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- function reportDayClick(date, allDay, ev) {
|
|
|
|
|
- trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev);
|
|
|
|
|
|
|
+ function reportDayClick(date, ev) {
|
|
|
|
|
+ trigger('dayClick', dayBodyCells[dateToCell(date).col], date, ev);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -870,13 +922,16 @@ function AgendaView(element, calendar, viewName) {
|
|
|
hoverListener.start(function(cell) {
|
|
hoverListener.start(function(cell) {
|
|
|
clearOverlays();
|
|
clearOverlays();
|
|
|
if (cell) {
|
|
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'));
|
|
|
|
|
|
|
+ var d1 = realCellToDate(cell);
|
|
|
|
|
+ var d2 = d1.clone();
|
|
|
|
|
+ if (d1.hasTime()) {
|
|
|
|
|
+ d2.add(calendar.defaultTimedEventDuration);
|
|
|
renderSlotOverlay(d1, d2);
|
|
renderSlotOverlay(d1, d2);
|
|
|
}
|
|
}
|
|
|
|
|
+ else {
|
|
|
|
|
+ d2.add(calendar.defaultAllDayEventDuration);
|
|
|
|
|
+ renderDayOverlay(d1, d2);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}, ev);
|
|
}, ev);
|
|
|
}
|
|
}
|
|
@@ -886,7 +941,13 @@ function AgendaView(element, calendar, viewName) {
|
|
|
var cell = hoverListener.stop();
|
|
var cell = hoverListener.stop();
|
|
|
clearOverlays();
|
|
clearOverlays();
|
|
|
if (cell) {
|
|
if (cell) {
|
|
|
- trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui);
|
|
|
|
|
|
|
+ trigger(
|
|
|
|
|
+ 'drop',
|
|
|
|
|
+ _dragElement,
|
|
|
|
|
+ realCellToDate(cell),
|
|
|
|
|
+ ev,
|
|
|
|
|
+ ui
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|