mirror of
https://github.com/wassname/fullcalendar.git
synced 2026-07-26 13:18:16 +08:00
Adding in the basics for resoruce view
This commit is contained in:
@@ -0,0 +1,893 @@
|
||||
|
||||
// setDefaults({
|
||||
// allDaySlot: true,
|
||||
// allDayText: 'all-day',
|
||||
// firstHour: 6,
|
||||
// slotMinutes: 30,
|
||||
// defaultEventMinutes: 120,
|
||||
// axisFormat: 'h(:mm)tt',
|
||||
// timeFormat: {
|
||||
// agenda: 'h:mm{ - h:mm}'
|
||||
// },
|
||||
// dragOpacity: {
|
||||
// agenda: .5
|
||||
// },
|
||||
// minTime: 0,
|
||||
// maxTime: 24
|
||||
// });
|
||||
|
||||
|
||||
// TODO: make it work in quirks mode (event corners, all-day height)
|
||||
// TODO: test liquid width, especially in IE6
|
||||
|
||||
|
||||
function ResourceView(element, calendar, viewName) {
|
||||
var t = this;
|
||||
|
||||
|
||||
// exports
|
||||
t.renderResource = renderResource;
|
||||
t.setWidth = setWidth;
|
||||
t.setHeight = setHeight;
|
||||
t.beforeHide = beforeHide;
|
||||
t.afterShow = afterShow;
|
||||
t.defaultEventEnd = defaultEventEnd;
|
||||
t.timePosition = timePosition;
|
||||
t.getIsCellAllDay = getIsCellAllDay;
|
||||
t.allDayRow = getAllDayRow;
|
||||
t.getHoverListener = function() { return hoverListener };
|
||||
t.colLeft = colLeft;
|
||||
t.colRight = colRight;
|
||||
t.colContentLeft = colContentLeft;
|
||||
t.colContentRight = colContentRight;
|
||||
t.getDaySegmentContainer = function() { return daySegmentContainer };
|
||||
t.getSlotSegmentContainer = function() { return slotSegmentContainer };
|
||||
t.getMinMinute = function() { return minMinute };
|
||||
t.getMaxMinute = function() { return maxMinute };
|
||||
t.getSlotContainer = function() { return slotContainer };
|
||||
t.getRowCnt = function() { return 1 };
|
||||
t.getColCnt = function() { return colCnt };
|
||||
t.getColWidth = function() { return colWidth };
|
||||
t.getSnapHeight = function() { return snapHeight };
|
||||
t.getSnapMinutes = function() { return snapMinutes };
|
||||
t.defaultSelectionEnd = defaultSelectionEnd;
|
||||
t.renderDayOverlay = renderDayOverlay;
|
||||
t.renderSelection = renderSelection;
|
||||
t.clearSelection = clearSelection;
|
||||
t.reportDayClick = reportDayClick; // selection mousedown hack
|
||||
t.dragStart = dragStart;
|
||||
t.dragStop = dragStop;
|
||||
t.getResources = calendar.fetchResources;
|
||||
|
||||
// imports
|
||||
View.call(t, element, calendar, viewName);
|
||||
OverlayManager.call(t);
|
||||
SelectionManager.call(t);
|
||||
AgendaEventRenderer.call(t);
|
||||
var opt = t.opt;
|
||||
var trigger = t.trigger;
|
||||
var clearEvents = t.clearEvents;
|
||||
var renderOverlay = t.renderOverlay;
|
||||
var clearOverlays = t.clearOverlays;
|
||||
var reportSelection = t.reportSelection;
|
||||
var unselect = t.unselect;
|
||||
var daySelectionMousedown = t.daySelectionMousedown;
|
||||
var slotSegHtml = t.slotSegHtml;
|
||||
var cellToDate = t.cellToDate;
|
||||
var dateToCell = t.dateToCell;
|
||||
var rangeToSegments = t.rangeToSegments;
|
||||
var formatDate = calendar.formatDate;
|
||||
|
||||
|
||||
// locals
|
||||
|
||||
var dayTable;
|
||||
var dayHead;
|
||||
var dayHeadCells;
|
||||
var dayBody;
|
||||
var dayBodyCells;
|
||||
var dayBodyCellInners;
|
||||
var dayBodyCellContentInners;
|
||||
var dayBodyFirstCell;
|
||||
var dayBodyFirstCellStretcher;
|
||||
var slotLayer;
|
||||
var daySegmentContainer;
|
||||
var allDayTable;
|
||||
var allDayRow;
|
||||
var slotScroller;
|
||||
var slotContainer;
|
||||
var slotSegmentContainer;
|
||||
var slotTable;
|
||||
var slotTableFirstInner;
|
||||
var selectionHelper;
|
||||
|
||||
var viewWidth;
|
||||
var viewHeight;
|
||||
var axisWidth;
|
||||
var colWidth;
|
||||
var gutterWidth;
|
||||
var slotHeight; // TODO: what if slotHeight changes? (see issue 650)
|
||||
|
||||
var snapMinutes;
|
||||
var snapRatio; // ratio of number of "selection" slots to normal slots. (ex: 1, 2, 4)
|
||||
var snapHeight; // holds the pixel hight of a "selection" slot
|
||||
|
||||
var colCnt;
|
||||
var slotCnt;
|
||||
var coordinateGrid;
|
||||
var hoverListener;
|
||||
var colPositions;
|
||||
var colContentPositions;
|
||||
var slotTopCache = {};
|
||||
var savedScrollTop;
|
||||
|
||||
var tm;
|
||||
var rtl;
|
||||
var minMinute, maxMinute;
|
||||
var colFormat;
|
||||
var showWeekNumbers;
|
||||
var weekNumberTitle;
|
||||
var weekNumberFormat;
|
||||
|
||||
|
||||
|
||||
/* Rendering
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
disableTextSelection(element.addClass('fc-agenda'));
|
||||
|
||||
|
||||
function renderResource(c) {
|
||||
colCnt = c;
|
||||
updateOptions();
|
||||
if (!dayTable) {
|
||||
buildSkeleton(); // builds day table, slot area, events containers
|
||||
}else{
|
||||
buildDayTable(); // rebuilds day table
|
||||
clearEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateOptions() {
|
||||
|
||||
tm = opt('theme') ? 'ui' : 'fc';
|
||||
rtl = opt('isRTL')
|
||||
minMinute = parseTime(opt('minTime'));
|
||||
maxMinute = parseTime(opt('maxTime'));
|
||||
colFormat = opt('columnFormat');
|
||||
|
||||
// week # options. (TODO: bad, logic also in other views)
|
||||
showWeekNumbers = opt('weekNumbers');
|
||||
weekNumberTitle = opt('weekNumberTitle');
|
||||
if (opt('weekNumberCalculation') != 'iso') {
|
||||
weekNumberFormat = "w";
|
||||
}
|
||||
else {
|
||||
weekNumberFormat = "W";
|
||||
}
|
||||
|
||||
snapMinutes = opt('snapMinutes') || opt('slotMinutes');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Build DOM
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function buildSkeleton() {
|
||||
var headerClass = tm + "-widget-header";
|
||||
var contentClass = tm + "-widget-content";
|
||||
var s;
|
||||
var d;
|
||||
var i;
|
||||
var maxd;
|
||||
var minutes;
|
||||
var slotNormal = opt('slotMinutes') % 15 == 0;
|
||||
|
||||
buildDayTable();
|
||||
|
||||
slotLayer =
|
||||
$("<div style='position:absolute;z-index:2;left:0;width:100%'/>")
|
||||
.appendTo(element);
|
||||
|
||||
if (opt('allDaySlot')) {
|
||||
|
||||
daySegmentContainer =
|
||||
$("<div style='position:absolute;z-index:8;top:0;left:0'/>")
|
||||
.appendTo(slotLayer);
|
||||
|
||||
s =
|
||||
"<table style='width:100%' class='fc-agenda-allday' cellspacing='0'>" +
|
||||
"<tr>" +
|
||||
"<th class='" + headerClass + " fc-agenda-axis'>" + opt('allDayText') + "</th>" +
|
||||
"<td>" +
|
||||
"<div class='fc-day-content'><div style='position:relative'/></div>" +
|
||||
"</td>" +
|
||||
"<th class='" + headerClass + " fc-agenda-gutter'> </th>" +
|
||||
"</tr>" +
|
||||
"</table>";
|
||||
allDayTable = $(s).appendTo(slotLayer);
|
||||
allDayRow = allDayTable.find('tr');
|
||||
|
||||
dayBind(allDayRow.find('td'));
|
||||
|
||||
slotLayer.append(
|
||||
"<div class='fc-agenda-divider " + headerClass + "'>" +
|
||||
"<div class='fc-agenda-divider-inner'/>" +
|
||||
"</div>"
|
||||
);
|
||||
|
||||
}else{
|
||||
|
||||
daySegmentContainer = $([]); // in jQuery 1.4, we can just do $()
|
||||
|
||||
}
|
||||
|
||||
slotScroller =
|
||||
$("<div style='position:absolute;width:100%;overflow-x:hidden;overflow-y:auto'/>")
|
||||
.appendTo(slotLayer);
|
||||
|
||||
slotContainer =
|
||||
$("<div style='position:relative;width:100%;overflow:hidden'/>")
|
||||
.appendTo(slotScroller);
|
||||
|
||||
slotSegmentContainer =
|
||||
$("<div style='position:absolute;z-index:8;top:0;left:0'/>")
|
||||
.appendTo(slotContainer);
|
||||
|
||||
s =
|
||||
"<table class='fc-agenda-slots' style='width:100%' cellspacing='0'>" +
|
||||
"<tbody>";
|
||||
d = zeroDate();
|
||||
maxd = addMinutes(cloneDate(d), maxMinute);
|
||||
addMinutes(d, minMinute);
|
||||
slotCnt = 0;
|
||||
for (i=0; d < maxd; i++) {
|
||||
minutes = d.getMinutes();
|
||||
s +=
|
||||
"<tr class='fc-slot" + i + ' ' + (!minutes ? '' : 'fc-minor') + "'>" +
|
||||
"<th class='fc-agenda-axis " + headerClass + "'>" +
|
||||
((!slotNormal || !minutes) ? formatDate(d, opt('axisFormat')) : ' ') +
|
||||
"</th>" +
|
||||
"<td class='" + contentClass + "'>" +
|
||||
"<div style='position:relative'> </div>" +
|
||||
"</td>" +
|
||||
"</tr>";
|
||||
addMinutes(d, opt('slotMinutes'));
|
||||
slotCnt++;
|
||||
}
|
||||
s +=
|
||||
"</tbody>" +
|
||||
"</table>";
|
||||
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 =
|
||||
"<table style='width:100%' class='fc-agenda-days fc-border-separate' cellspacing='0'>" +
|
||||
buildDayTableHeadHTML() +
|
||||
buildDayTableBodyHTML() +
|
||||
"</table>";
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function buildDayTableHeadHTML() {
|
||||
var headerClass = tm + "-widget-header";
|
||||
var date;
|
||||
var html = '';
|
||||
var weekText;
|
||||
var col;
|
||||
|
||||
html +=
|
||||
"<thead>" +
|
||||
"<tr>";
|
||||
|
||||
if (showWeekNumbers) {
|
||||
weekText = formatDate(date, weekNumberFormat);
|
||||
if (rtl) {
|
||||
weekText += weekNumberTitle;
|
||||
}
|
||||
else {
|
||||
weekText = weekNumberTitle + weekText;
|
||||
}
|
||||
html +=
|
||||
"<th class='fc-agenda-axis fc-week-number " + headerClass + "'>" +
|
||||
htmlEscape(weekText) +
|
||||
"</th>";
|
||||
}
|
||||
else {
|
||||
html += "<th class='fc-agenda-axis " + headerClass + "'> </th>";
|
||||
}
|
||||
|
||||
for (col=0; col<colCnt; col++) {
|
||||
date = cellToDate(0, col);
|
||||
html +=
|
||||
"<th class='fc-" + dayIDs[date.getDay()] + " fc-col" + col + ' ' + headerClass + "'>" +
|
||||
htmlEscape(formatDate(date, colFormat)) +
|
||||
"</th>";
|
||||
}
|
||||
|
||||
html +=
|
||||
"<th class='fc-agenda-gutter " + headerClass + "'> </th>" +
|
||||
"</tr>" +
|
||||
"</thead>";
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function buildDayTableBodyHTML() {
|
||||
var headerClass = tm + "-widget-header"; // TODO: make these when updateOptions() called
|
||||
var contentClass = tm + "-widget-content";
|
||||
var date;
|
||||
var today = clearTime(new Date());
|
||||
var col;
|
||||
var cellsHTML;
|
||||
var cellHTML;
|
||||
var classNames;
|
||||
var html = '';
|
||||
|
||||
html +=
|
||||
"<tbody>" +
|
||||
"<tr>" +
|
||||
"<th class='fc-agenda-axis " + headerClass + "'> </th>";
|
||||
|
||||
cellsHTML = '';
|
||||
|
||||
for (col=0; col<colCnt; col++) {
|
||||
|
||||
date = cellToDate(0, col);
|
||||
|
||||
classNames = [
|
||||
'fc-col' + col,
|
||||
'fc-' + dayIDs[date.getDay()],
|
||||
contentClass
|
||||
];
|
||||
if (+date == +today) {
|
||||
classNames.push(
|
||||
tm + '-state-highlight',
|
||||
'fc-today'
|
||||
);
|
||||
}
|
||||
|
||||
cellHTML =
|
||||
"<td class='" + classNames.join(' ') + "'>" +
|
||||
"<div>" +
|
||||
"<div class='fc-day-content'>" +
|
||||
"<div style='position:relative'> </div>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
"</td>";
|
||||
|
||||
cellsHTML += cellHTML;
|
||||
}
|
||||
|
||||
html += cellsHTML;
|
||||
html +=
|
||||
"<td class='fc-agenda-gutter " + contentClass + "'> </td>" +
|
||||
"</tr>" +
|
||||
"</tbody>";
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// TODO: data-date on the cells
|
||||
|
||||
|
||||
|
||||
/* Dimensions
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function setHeight(height, dateChanged) {
|
||||
if (height === undefined) {
|
||||
height = viewHeight;
|
||||
}
|
||||
viewHeight = height;
|
||||
slotTopCache = {};
|
||||
|
||||
var headHeight = dayBody.position().top;
|
||||
var allDayHeight = slotScroller.position().top; // including divider
|
||||
var bodyHeight = Math.min( // total body height, including borders
|
||||
height - headHeight, // when scrollbars
|
||||
slotTable.height() + allDayHeight + 1 // when no scrollbars. +1 for bottom border
|
||||
);
|
||||
|
||||
dayBodyFirstCellStretcher
|
||||
.height(bodyHeight - vsides(dayBodyFirstCell));
|
||||
|
||||
slotLayer.css('top', headHeight);
|
||||
|
||||
slotScroller.height(bodyHeight - allDayHeight - 1);
|
||||
|
||||
slotHeight = slotTableFirstInner.height() + 1; // +1 for border
|
||||
|
||||
snapRatio = opt('slotMinutes') / snapMinutes;
|
||||
snapHeight = slotHeight / snapRatio;
|
||||
|
||||
if (dateChanged) {
|
||||
resetScroll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setWidth(width) {
|
||||
viewWidth = width;
|
||||
colPositions.clear();
|
||||
colContentPositions.clear();
|
||||
|
||||
var axisFirstCells = dayHead.find('th:first');
|
||||
if (allDayTable) {
|
||||
axisFirstCells = axisFirstCells.add(allDayTable.find('th:first'));
|
||||
}
|
||||
axisFirstCells = axisFirstCells.add(slotTable.find('th:first'));
|
||||
|
||||
axisWidth = 0;
|
||||
setOuterWidth(
|
||||
axisFirstCells
|
||||
.width('')
|
||||
.each(function(i, _cell) {
|
||||
axisWidth = Math.max(axisWidth, $(_cell).outerWidth());
|
||||
}),
|
||||
axisWidth
|
||||
);
|
||||
|
||||
var gutterCells = dayTable.find('.fc-agenda-gutter');
|
||||
if (allDayTable) {
|
||||
gutterCells = gutterCells.add(allDayTable.find('th.fc-agenda-gutter'));
|
||||
}
|
||||
|
||||
var slotTableWidth = slotScroller[0].clientWidth; // needs to be done after axisWidth (for IE7)
|
||||
|
||||
gutterWidth = slotScroller.width() - slotTableWidth;
|
||||
if (gutterWidth) {
|
||||
setOuterWidth(gutterCells, gutterWidth);
|
||||
gutterCells
|
||||
.show()
|
||||
.prev()
|
||||
.removeClass('fc-last');
|
||||
}else{
|
||||
gutterCells
|
||||
.hide()
|
||||
.prev()
|
||||
.addClass('fc-last');
|
||||
}
|
||||
|
||||
colWidth = Math.floor((slotTableWidth - axisWidth) / colCnt);
|
||||
setOuterWidth(dayHeadCells.slice(0, -1), colWidth);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Scrolling
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function resetScroll() {
|
||||
var d0 = zeroDate();
|
||||
var scrollDate = cloneDate(d0);
|
||||
scrollDate.setHours(opt('firstHour'));
|
||||
var top = timePosition(d0, scrollDate) + 1; // +1 for the border
|
||||
function scroll() {
|
||||
slotScroller.scrollTop(top);
|
||||
}
|
||||
scroll();
|
||||
setTimeout(scroll, 0); // overrides any previous scroll state made by the browser
|
||||
}
|
||||
|
||||
|
||||
function beforeHide() {
|
||||
savedScrollTop = slotScroller.scrollTop();
|
||||
}
|
||||
|
||||
|
||||
function afterShow() {
|
||||
slotScroller.scrollTop(savedScrollTop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Slot/Day clicking and binding
|
||||
-----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function dayBind(cells) {
|
||||
cells.click(slotClick)
|
||||
.mousedown(daySelectionMousedown);
|
||||
}
|
||||
|
||||
|
||||
function slotBind(cells) {
|
||||
cells.click(slotClick)
|
||||
.mousedown(slotSelectionMousedown);
|
||||
}
|
||||
|
||||
|
||||
function slotClick(ev) {
|
||||
if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
|
||||
var col = Math.min(colCnt-1, Math.floor((ev.pageX - dayTable.offset().left - axisWidth) / colWidth));
|
||||
var date = cellToDate(0, col);
|
||||
var rowMatch = this.parentNode.className.match(/fc-slot(\d+)/); // TODO: maybe use data
|
||||
if (rowMatch) {
|
||||
var mins = parseInt(rowMatch[1]) * opt('slotMinutes');
|
||||
var hours = Math.floor(mins/60);
|
||||
date.setHours(hours);
|
||||
date.setMinutes(mins%60 + minMinute);
|
||||
trigger('dayClick', dayBodyCells[col], date, false, ev);
|
||||
}else{
|
||||
trigger('dayClick', dayBodyCells[col], date, true, ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Semi-transparent Overlay Helpers
|
||||
-----------------------------------------------------*/
|
||||
// TODO: should be consolidated with BasicView's methods
|
||||
|
||||
|
||||
function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
|
||||
|
||||
if (refreshCoordinateGrid) {
|
||||
coordinateGrid.build();
|
||||
}
|
||||
|
||||
var segments = rangeToSegments(overlayStart, overlayEnd);
|
||||
|
||||
for (var i=0; i<segments.length; i++) {
|
||||
var segment = segments[i];
|
||||
dayBind(
|
||||
renderCellOverlay(
|
||||
segment.row,
|
||||
segment.leftCol,
|
||||
segment.row,
|
||||
segment.rightCol
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function renderCellOverlay(row0, col0, row1, col1) { // only for all-day?
|
||||
var rect = coordinateGrid.rect(row0, col0, row1, col1, slotLayer);
|
||||
return renderOverlay(rect, slotLayer);
|
||||
}
|
||||
|
||||
|
||||
function renderSlotOverlay(overlayStart, overlayEnd) {
|
||||
for (var i=0; i<colCnt; i++) {
|
||||
var dayStart = cellToDate(0, i);
|
||||
var dayEnd = addDays(cloneDate(dayStart), 1);
|
||||
var stretchStart = new Date(Math.max(dayStart, overlayStart));
|
||||
var stretchEnd = new Date(Math.min(dayEnd, overlayEnd));
|
||||
if (stretchStart < stretchEnd) {
|
||||
var rect = coordinateGrid.rect(0, i, 0, i, slotContainer); // only use it for horizontal coords
|
||||
var top = timePosition(dayStart, stretchStart);
|
||||
var bottom = timePosition(dayStart, stretchEnd);
|
||||
rect.top = top;
|
||||
rect.height = bottom - top;
|
||||
slotBind(
|
||||
renderOverlay(rect, slotContainer)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Coordinate Utilities
|
||||
-----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
coordinateGrid = new CoordinateGrid(function(rows, cols) {
|
||||
var e, n, p;
|
||||
dayHeadCells.each(function(i, _e) {
|
||||
e = $(_e);
|
||||
n = e.offset().left;
|
||||
if (i) {
|
||||
p[1] = n;
|
||||
}
|
||||
p = [n];
|
||||
cols[i] = p;
|
||||
});
|
||||
p[1] = n + e.outerWidth();
|
||||
if (opt('allDaySlot')) {
|
||||
e = allDayRow;
|
||||
n = e.offset().top;
|
||||
rows[0] = [n, n+e.outerHeight()];
|
||||
}
|
||||
var slotTableTop = slotContainer.offset().top;
|
||||
var slotScrollerTop = slotScroller.offset().top;
|
||||
var slotScrollerBottom = slotScrollerTop + slotScroller.outerHeight();
|
||||
function constrain(n) {
|
||||
return Math.max(slotScrollerTop, Math.min(slotScrollerBottom, n));
|
||||
}
|
||||
for (var i=0; i<slotCnt*snapRatio; i++) { // adapt slot count to increased/decreased selection slot count
|
||||
rows.push([
|
||||
constrain(slotTableTop + snapHeight*i),
|
||||
constrain(slotTableTop + snapHeight*(i+1))
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
hoverListener = new HoverListener(coordinateGrid);
|
||||
|
||||
colPositions = new HorizontalPositionCache(function(col) {
|
||||
return dayBodyCellInners.eq(col);
|
||||
});
|
||||
|
||||
colContentPositions = new HorizontalPositionCache(function(col) {
|
||||
return dayBodyCellContentInners.eq(col);
|
||||
});
|
||||
|
||||
|
||||
function colLeft(col) {
|
||||
return colPositions.left(col);
|
||||
}
|
||||
|
||||
|
||||
function colContentLeft(col) {
|
||||
return colContentPositions.left(col);
|
||||
}
|
||||
|
||||
|
||||
function colRight(col) {
|
||||
return colPositions.right(col);
|
||||
}
|
||||
|
||||
|
||||
function colContentRight(col) {
|
||||
return colContentPositions.right(col);
|
||||
}
|
||||
|
||||
|
||||
function getIsCellAllDay(cell) {
|
||||
return opt('allDaySlot') && !cell.row;
|
||||
}
|
||||
|
||||
|
||||
function realCellToDate(cell) { // ugh "real" ... but blame it on our abuse of the "cell" system
|
||||
var d = cellToDate(0, cell.col);
|
||||
var slotIndex = cell.row;
|
||||
if (opt('allDaySlot')) {
|
||||
slotIndex--;
|
||||
}
|
||||
if (slotIndex >= 0) {
|
||||
addMinutes(d, minMinute + slotIndex * snapMinutes);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// get the Y coordinate of the given time on the given day (both Date objects)
|
||||
function timePosition(day, time) { // both date objects. day holds 00:00 of current day
|
||||
day = cloneDate(day, true);
|
||||
if (time < addMinutes(cloneDate(day), minMinute)) {
|
||||
return 0;
|
||||
}
|
||||
if (time >= addMinutes(cloneDate(day), maxMinute)) {
|
||||
return slotTable.height();
|
||||
}
|
||||
var slotMinutes = opt('slotMinutes'),
|
||||
minutes = time.getHours()*60 + time.getMinutes() - minMinute,
|
||||
slotI = Math.floor(minutes / slotMinutes),
|
||||
slotTop = slotTopCache[slotI];
|
||||
if (slotTop === undefined) {
|
||||
slotTop = slotTopCache[slotI] = slotTable.find('tr:eq(' + slotI + ') td div')[0].offsetTop; //.position().top; // need this optimization???
|
||||
}
|
||||
return Math.max(0, Math.round(
|
||||
slotTop - 1 + slotHeight * ((minutes % slotMinutes) / slotMinutes)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
function getAllDayRow(index) {
|
||||
return allDayRow;
|
||||
}
|
||||
|
||||
|
||||
function defaultEventEnd(event) {
|
||||
var start = cloneDate(event.start);
|
||||
if (event.allDay) {
|
||||
return start;
|
||||
}
|
||||
return addMinutes(start, opt('defaultEventMinutes'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Selection
|
||||
---------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function defaultSelectionEnd(startDate, allDay) {
|
||||
if (allDay) {
|
||||
return cloneDate(startDate);
|
||||
}
|
||||
return addMinutes(cloneDate(startDate), opt('slotMinutes'));
|
||||
}
|
||||
|
||||
|
||||
function renderSelection(startDate, endDate, allDay) { // only for all-day
|
||||
if (allDay) {
|
||||
if (opt('allDaySlot')) {
|
||||
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true);
|
||||
}
|
||||
}else{
|
||||
renderSlotSelection(startDate, endDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function renderSlotSelection(startDate, endDate) {
|
||||
var helperOption = opt('selectHelper');
|
||||
coordinateGrid.build();
|
||||
if (helperOption) {
|
||||
var col = dateToCell(startDate).col;
|
||||
if (col >= 0 && col < colCnt) { // only works when times are on same day
|
||||
var rect = coordinateGrid.rect(0, col, 0, col, slotContainer); // only for horizontal coords
|
||||
var top = timePosition(startDate, startDate);
|
||||
var bottom = timePosition(startDate, endDate);
|
||||
if (bottom > top) { // protect against selections that are entirely before or after visible range
|
||||
rect.top = top;
|
||||
rect.height = bottom - top;
|
||||
rect.left += 2;
|
||||
rect.width -= 5;
|
||||
if ($.isFunction(helperOption)) {
|
||||
var helperRes = helperOption(startDate, endDate);
|
||||
if (helperRes) {
|
||||
rect.position = 'absolute';
|
||||
rect.zIndex = 8;
|
||||
selectionHelper = $(helperRes)
|
||||
.css(rect)
|
||||
.appendTo(slotContainer);
|
||||
}
|
||||
}else{
|
||||
rect.isStart = true; // conside rect a "seg" now
|
||||
rect.isEnd = true; //
|
||||
selectionHelper = $(slotSegHtml(
|
||||
{
|
||||
title: '',
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
className: ['fc-select-helper'],
|
||||
editable: false
|
||||
},
|
||||
rect
|
||||
));
|
||||
selectionHelper.css('opacity', opt('dragOpacity'));
|
||||
}
|
||||
if (selectionHelper) {
|
||||
slotBind(selectionHelper);
|
||||
slotContainer.append(selectionHelper);
|
||||
setOuterWidth(selectionHelper, rect.width, true); // needs to be after appended
|
||||
setOuterHeight(selectionHelper, rect.height, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
renderSlotOverlay(startDate, endDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clearSelection() {
|
||||
clearOverlays();
|
||||
if (selectionHelper) {
|
||||
selectionHelper.remove();
|
||||
selectionHelper = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function slotSelectionMousedown(ev) {
|
||||
if (ev.which == 1 && opt('selectable')) { // ev.which==1 means left mouse button
|
||||
unselect(ev);
|
||||
var dates;
|
||||
hoverListener.start(function(cell, origCell) {
|
||||
clearSelection();
|
||||
if (cell && cell.col == origCell.col && !getIsCellAllDay(cell)) {
|
||||
var d1 = realCellToDate(origCell);
|
||||
var d2 = realCellToDate(cell);
|
||||
dates = [
|
||||
d1,
|
||||
addMinutes(cloneDate(d1), snapMinutes), // calculate minutes depending on selection slot minutes
|
||||
d2,
|
||||
addMinutes(cloneDate(d2), snapMinutes)
|
||||
].sort(dateCompare);
|
||||
renderSlotSelection(dates[0], dates[3]);
|
||||
}else{
|
||||
dates = null;
|
||||
}
|
||||
}, ev);
|
||||
$(document).one('mouseup', function(ev) {
|
||||
hoverListener.stop();
|
||||
if (dates) {
|
||||
if (+dates[0] == +dates[1]) {
|
||||
reportDayClick(dates[0], false, ev);
|
||||
}
|
||||
reportSelection(dates[0], dates[3], false, ev);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reportDayClick(date, allDay, ev) {
|
||||
trigger('dayClick', dayBodyCells[dateToCell(date).col], date, allDay, ev);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* External Dragging
|
||||
--------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
function dragStart(_dragElement, ev, ui) {
|
||||
hoverListener.start(function(cell) {
|
||||
clearOverlays();
|
||||
if (cell) {
|
||||
if (getIsCellAllDay(cell)) {
|
||||
renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
|
||||
}else{
|
||||
var d1 = realCellToDate(cell);
|
||||
var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
|
||||
renderSlotOverlay(d1, d2);
|
||||
}
|
||||
}
|
||||
}, ev);
|
||||
}
|
||||
|
||||
|
||||
function dragStop(_dragElement, ev, ui) {
|
||||
var cell = hoverListener.stop();
|
||||
clearOverlays();
|
||||
if (cell) {
|
||||
trigger('drop', _dragElement, realCellToDate(cell), getIsCellAllDay(cell), ev, ui);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user