added droppable/drop, refactored selectable code, fixed issue 406

This commit is contained in:
Adam Shaw 2010-06-28 21:51:13 -07:00
parent 5fb7644920
commit 8ebda5094f
9 changed files with 600 additions and 457 deletions

View file

@ -88,10 +88,6 @@ function Agenda(element, options, methods) {
return bg.find('td:eq(' + col + ') div div');
}),
slotTopCache = {},
daySelectionManager,
slotSelectionManager,
selectionHelper,
selectionMatrix,
// ...
view = $.extend(this, viewMethods, methods, {
@ -669,38 +665,27 @@ function Agenda(element, options, methods) {
function draggableDayEvent(event, eventElement, isStart) {
if (!options.disableDragging && eventElement.draggable) {
var origPosition, origWidth,
resetElement,
allDay=true,
matrix;
var origWidth;
var allDay=true;
var dayDelta;
eventElement.draggable({
zIndex: 9,
opacity: view.option('dragOpacity', 'month'), // use whatever the month view was using
revertDuration: options.dragRevertDuration,
start: function(ev, ui) {
view.hideEvents(event, eventElement);
view.trigger('eventDragStart', eventElement, event, ev, ui);
origPosition = eventElement.position();
view.hideEvents(event, eventElement);
origWidth = eventElement.width();
resetElement = function() {
if (!allDay) {
eventElement
.width(origWidth)
.height('')
.draggable('option', 'grid', null);
allDay = true;
}
};
matrix = buildDayMatrix(function(cell) {
eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
view.clearOverlays();
hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
clearOverlay();
if (cell) {
dayDelta = colDelta * dis;
if (!cell.row) {
// on full-days
renderDayOverlay(
matrix,
addDays(cloneDate(event.start), cell.colDelta),
addDays(exclEndDay(event), cell.colDelta)
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
resetElement();
}else{
@ -710,50 +695,51 @@ function Agenda(element, options, methods) {
setOuterHeight(
eventElement.width(colWidth - 10), // don't use entire width
slotHeight * Math.round(
(event.end ? ((event.end - event.start)/MINUTE_MS) : options.defaultEventMinutes)
/options.slotMinutes)
(event.end ? ((event.end - event.start) / MINUTE_MS) : options.defaultEventMinutes)
/ options.slotMinutes
)
);
eventElement.draggable('option', 'grid', [colWidth, 1]);
allDay = false;
}
}
}
},true);
matrix.mouse(ev);
},
drag: function(ev, ui) {
matrix.mouse(ev);
}, ev, 'drag');
},
stop: function(ev, ui) {
var cell = hoverListener.stop();
clearOverlay();
view.trigger('eventDragStop', eventElement, event, ev, ui);
view.clearOverlays();
var cell = matrix.cell;
var dayDelta = dis * (
allDay ? // can't trust cell.colDelta when using slot grid
(cell ? cell.colDelta : 0) :
Math.floor((ui.position.left - origPosition.left) / colWidth)
);
if (!cell || !dayDelta && !cell.rowDelta) {
// over nothing (has reverted)
if (cell && (!allDay || dayDelta)) {
// changed!
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
var minuteDelta = 0;
if (!allDay) {
minuteDelta = Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight)
* options.slotMinutes
+ minMinute
- (event.start.getHours() * 60 + event.start.getMinutes());
}
view.eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
}else{
// hasn't moved or is out of bounds (draggable has already reverted)
resetElement();
if ($.browser.msie) {
eventElement.css('filter', ''); // clear IE opacity side-effects
}
view.showEvents(event, eventElement);
}else{
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
view.eventDrop(
this, event, dayDelta,
allDay ? 0 : // minute delta
Math.round((eventElement.offset().top - bodyContent.offset().top) / slotHeight)
* options.slotMinutes
+ minMinute
- (event.start.getHours() * 60 + event.start.getMinutes()),
allDay, ev, ui
);
}
}
});
function resetElement() {
if (!allDay) {
eventElement
.width(origWidth)
.height('')
.draggable('option', 'grid', null);
allDay = true;
}
}
}
}
@ -763,11 +749,11 @@ function Agenda(element, options, methods) {
function draggableSlotEvent(event, eventElement, timeElement) {
if (!options.disableDragging && eventElement.draggable) {
var origPosition,
resetElement,
prevSlotDelta, slotDelta,
allDay=false,
matrix;
var origPosition;
var allDay=false;
var dayDelta;
var minuteDelta;
var prevMinuteDelta;
eventElement.draggable({
zIndex: 9,
scroll: false,
@ -776,26 +762,20 @@ function Agenda(element, options, methods) {
opacity: view.option('dragOpacity'),
revertDuration: options.dragRevertDuration,
start: function(ev, ui) {
view.hideEvents(event, eventElement);
view.trigger('eventDragStart', eventElement, event, ev, ui);
view.hideEvents(event, eventElement);
if ($.browser.msie) {
eventElement.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
}
origPosition = eventElement.position();
resetElement = function() {
// convert back to original slot-event
if (allDay) {
timeElement.css('display', ''); // show() was causing display=inline
eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
allDay = false;
}
};
prevSlotDelta = 0;
matrix = buildDayMatrix(function(cell) {
minuteDelta = prevMinuteDelta = 0;
hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
eventElement.draggable('option', 'revert', !cell);
view.clearOverlays();
clearOverlay();
if (cell) {
if (!cell.row && options.allDaySlot) { // over full days
dayDelta = colDelta * dis;
if (options.allDaySlot && !cell.row) {
// over full days
if (!allDay) {
// convert to temporary all-day event
allDay = true;
@ -803,61 +783,63 @@ function Agenda(element, options, methods) {
eventElement.draggable('option', 'grid', null);
}
renderDayOverlay(
matrix,
addDays(cloneDate(event.start), cell.colDelta),
addDays(exclEndDay(event), cell.colDelta)
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
}else{ // on slots
}else{
// on slots
resetElement();
}
}
},true);
matrix.mouse(ev);
}, ev, 'drag');
},
drag: function(ev, ui) {
slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight);
if (slotDelta != prevSlotDelta) {
minuteDelta = Math.round((ui.position.top - origPosition.top) / slotHeight) * options.slotMinutes;
if (minuteDelta != prevMinuteDelta) {
if (!allDay) {
// update time header
var minuteDelta = slotDelta*options.slotMinutes,
newStart = addMinutes(cloneDate(event.start), minuteDelta),
newEnd;
updateTimeText(minuteDelta);
}
prevMinuteDelta = minuteDelta;
}
},
stop: function(ev, ui) {
var cell = hoverListener.stop();
clearOverlay();
view.trigger('eventDragStop', eventElement, event, ev, ui);
if (cell && (dayDelta || minuteDelta || allDay)) {
// changed!
view.eventDrop(this, event, dayDelta, allDay ? 0 : minuteDelta, allDay, ev, ui);
}else{
// either no change or out-of-bounds (draggable has already reverted)
resetElement();
eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
updateTimeText(0);
if ($.browser.msie) {
eventElement
.css('filter', '') // clear IE opacity side-effects
.find('span.fc-event-bg')
.css('display', ''); // .show() made display=inline
}
view.showEvents(event, eventElement);
}
}
});
function updateTimeText(minuteDelta) {
var newStart = addMinutes(cloneDate(event.start), minuteDelta);
var newEnd;
if (event.end) {
newEnd = addMinutes(cloneDate(event.end), minuteDelta);
}
timeElement.text(formatDates(newStart, newEnd, view.option('timeFormat')));
}
prevSlotDelta = slotDelta;
}
matrix.mouse(ev);
},
stop: function(ev, ui) {
view.clearOverlays();
view.trigger('eventDragStop', eventElement, event, ev, ui);
var cell = matrix.cell,
dayDelta = dis * (
allDay ? // can't trust cell.colDelta when using slot grid
(cell ? cell.colDelta : 0) :
Math.floor((ui.position.left - origPosition.left) / colWidth)
);
if (!cell || !slotDelta && !dayDelta) {
resetElement();
if ($.browser.msie) {
eventElement
.css('filter', '') // clear IE opacity side-effects
.find('span.fc-event-bg').css('display', ''); // .show() made display=inline
}
eventElement.css(origPosition); // sometimes fast drags make event revert to wrong position
view.showEvents(event, eventElement);
}else{
view.eventDrop(
this, event, dayDelta,
allDay ? 0 : slotDelta * options.slotMinutes, // minute delta
allDay, ev, ui
);
function resetElement() {
// convert back to original slot-event
if (allDay) {
timeElement.css('display', ''); // show() was causing display=inline
eventElement.draggable('option', 'grid', [colWidth, slotHeight]);
allDay = false;
}
}
});
}
}
@ -918,114 +900,152 @@ function Agenda(element, options, methods) {
/* Coordinate Utilities
-----------------------------------------------------------------------------*/
var 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 (options.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))
]);
}
});
var hoverListener = new HoverListener(coordinateGrid);
// 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 = options.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)
));
}
/* Selecting
-----------------------------------------------------------------------------*/
daySelectionManager = new SelectionManager(
view,
unselect,
function(startDate, endDate, allDay) {
renderDayOverlay(
selectionMatrix,
startDate,
addDays(cloneDate(endDate), 1)
);
},
clearSelection
);
function daySelectionMousedown(ev) {
if (view.option('selectable')) {
selectionMatrix = buildDayMatrix(function(cell) {
if (cell) {
var d = dayColDate(cell.col);
daySelectionManager.drag(d, d, true);
}else{
daySelectionManager.drag();
}
});
documentDragHelp(
function(ev) {
selectionMatrix.mouse(ev);
},
function(ev) {
daySelectionManager.dragStop(ev);
}
);
daySelectionManager.dragStart(ev);
selectionMatrix.mouse(ev);
return false; // prevent auto-unselect and text selection
}
}
slotSelectionManager = new SelectionManager(
view,
unselect,
renderSlotSelection,
clearSelection
var selected = false;
var daySelectionMousedown = selection_dayMousedown(
view, hoverListener, cellDate, renderDayOverlay, clearOverlay, reportSelection, unselect
);
function slotSelectionMousedown(ev) {
if (view.option('selectable')) {
selectionMatrix = buildSlotMatrix(function(cell) {
if (cell) {
var d = slotCellDate(cell.row, cell.origCol);
slotSelectionManager.drag(d, addMinutes(cloneDate(d), options.slotMinutes), false);
unselect();
var dates;
hoverListener.start(function(cell, origCell) {
clearSelection();
if (cell && cell.col == origCell.col) {
var d1 = cellDate(origCell);
var d2 = cellDate(cell);
dates = [
d1,
addMinutes(cloneDate(d1), options.slotMinutes),
d2,
addMinutes(cloneDate(d2), options.slotMinutes)
].sort(cmp);
renderSlotSelection(dates[0], dates[3]);
}else{
slotSelectionManager.drag();
dates = null;
}
}, ev);
$(document).one('mouseup', function() {
hoverListener.stop();
if (dates) {
reportSelection(dates[0], dates[3], false);
}
});
documentDragHelp(
function(ev) {
selectionMatrix.mouse(ev);
},
function(ev) {
slotSelectionManager.dragStop(ev);
}
);
slotSelectionManager.dragStart(ev);
selectionMatrix.mouse(ev);
return false; // prevent auto-unselect and text selection
}
}
documentUnselectAuto(view, unselect);
this.select = function(start, end, allDay) {
view.select = function(startDate, endDate, allDay) {
coordinateGrid.build();
unselect();
if (allDay) {
if (options.allDaySlot) {
if (!end) {
end = cloneDate(start);
if (!endDate) {
endDate = cloneDate(startDate);
}
selectionMatrix = buildDayMatrix();
daySelectionManager.select(start, end, allDay);
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1));
}
}else{
if (!end) {
end = addMinutes(cloneDate(start), options.slotMinutes);
if (!endDate) {
endDate = addMinutes(cloneDate(startDate), options.slotMinutes);
}
selectionMatrix = buildSlotMatrix();
slotSelectionManager.select(start, end, allDay);
renderSlotSelection(startDate, endDate);
}
reportSelection(startDate, endDate, allDay);
};
function unselect() {
slotSelectionManager.unselect();
daySelectionManager.unselect();
function reportSelection(startDate, endDate, allDay) {
selected = true;
view.trigger('select', view, startDate, endDate, allDay);
}
this.unselect = unselect;
function unselect() {
if (selected) {
clearSelection();
selected = false;
view.trigger('unselect', view);
}
}
view.unselect = unselect;
selection_unselectAuto(view, unselect);
/* Selecting drawing utils
-----------------------------------------------------------------------------*/
var selectionHelper;
function renderSlotSelection(startDate, endDate) {
var helperOption = view.option('selectHelper');
if (helperOption) {
var col = dayDiff(startDate, view.visStart);
var col = dayDiff(startDate, view.visStart) * dis + dit;
if (col >= 0 && col < colCnt) { // only works when times are on same day
var rect = selectionMatrix.rect(0, col*dis+dit, 1, col*dis+dit+1, bodyContent); // only for horizontal coords
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
@ -1068,12 +1088,12 @@ function Agenda(element, options, methods) {
}
}
}else{
renderSlotOverlay(selectionMatrix, startDate, endDate);
renderSlotOverlay(startDate, endDate);
}
}
function clearSelection() {
clearOverlays();
clearOverlay();
if (selectionHelper) {
selectionHelper.remove();
selectionHelper = null;
@ -1083,11 +1103,10 @@ function Agenda(element, options, methods) {
/* Semi-transparent Overlay Helpers
-----------------------------------------------------*/
function renderDayOverlay(matrix, startDate, endDate) {
function renderDayOverlay(startDate, endDate) {
var startCol, endCol;
if (rtl) {
startCol = dayDiff(endDate, view.visStart)*dis+dit+1;
@ -1099,21 +1118,26 @@ function Agenda(element, options, methods) {
startCol = Math.max(0, startCol);
endCol = Math.min(colCnt, endCol);
if (startCol < endCol) {
var rect = matrix.rect(0, startCol, 1, endCol, head);
dayBind(
view.renderOverlay(rect, head)
_renderDayOverlay(0, startCol, 0, endCol-1)
);
}
}
function renderSlotOverlay(matrix, overlayStart, overlayEnd) {
function _renderDayOverlay(col0, row0, col1, row1) {
var rect = coordinateGrid.rect(col0, row0, col1, row1, head);
return view.renderOverlay(rect, head);
}
function renderSlotOverlay(overlayStart, overlayEnd) {
var dayStart = cloneDate(view.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 rect = matrix.rect(0, i*dis+dit, 1, i*dis+dit+1, bodyContent); // only use it for horizontal coords
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;
@ -1127,48 +1151,42 @@ function Agenda(element, options, methods) {
}
}
function clearOverlays() {
function clearOverlay() {
view.clearOverlays();
}
/* Coordinate Utilities
-----------------------------------------------------------------------------*/
/* External dragging
-----------------------------------------------------*/
// 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 = options.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)
));
}
view.isExternalDraggable = function(_element) {
return _element.parentNode != daySegmentContainer[0] && _element.parentNode != slotSegmentContainer[0];
};
function buildDayMatrix(changeCallback, includeSlotArea) {
var rowElements = options.allDaySlot ? head.find('td') : $([]);
if (includeSlotArea) {
rowElements = rowElements.add(body);
view.dragStart = function(ev) {
hoverListener.start(function(cell) {
clearOverlay();
if (cell) {
if (cellIsAllDay(cell)) {
_renderDayOverlay(cell.row, cell.col, cell.row, cell.col);
}else{
var d1 = cellDate(cell);
var d2 = addMinutes(cloneDate(d1), options.slotMinutes); //options.defaultEventMinutes);
renderSlotOverlay(d1, d2);
}
return new HoverMatrix(rowElements, bg.find('td'), changeCallback);
}
}, ev);
};
function buildSlotMatrix(changeCallback) {
return new HoverMatrix(bodyTable.find('td'), bg.find('td'), changeCallback);
view.dragStop = function(ev, ui) {
var cell = hoverListener.stop();
clearOverlay();
if (cell) {
view.trigger('drop', view, cellDate(cell), cellIsAllDay(cell), ev, ui);
}
};
@ -1188,17 +1206,20 @@ function Agenda(element, options, methods) {
return ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt)*dis+dit;
}
// generating dates from cell row & columns
function dayColDate(col) {
return addDays(cloneDate(view.visStart), col*dis+dit);
function cellDate(cell) {
var d = addDays(cloneDate(view.visStart), cell.col*dis+dit);
var slotIndex = cell.row;
if (options.allDaySlot) {
slotIndex--;
}
if (slotIndex >= 0) {
addMinutes(d, minMinute + slotIndex*options.slotMinutes);
}
return d;
}
function slotCellDate(row, col) {
var d = dayColDate(col);
addMinutes(d, minMinute + row*options.slotMinutes);
return d;
function cellIsAllDay(cell) {
return options.allDaySlot && !cell.row;
}

View file

@ -121,8 +121,6 @@ function Grid(element, options, methods) {
dayContentPositions = new HorizontalPositionCache(function(dayOfWeek) {
return tbody.find('td:eq(' + ((dayOfWeek - Math.max(firstDay,nwe)+colCnt) % colCnt) + ') div div');
}),
selectionManager,
selectionMatrix,
// ...
// initialize superclass
@ -425,37 +423,32 @@ function Grid(element, options, methods) {
function draggableEvent(event, eventElement) {
if (!options.disableDragging && eventElement.draggable) {
var matrix,
dayDelta = 0;
var dayDelta;
eventElement.draggable({
zIndex: 9,
delay: 50,
opacity: view.option('dragOpacity'),
revertDuration: options.dragRevertDuration,
start: function(ev, ui) {
view.hideEvents(event, eventElement);
view.trigger('eventDragStart', eventElement, event, ev, ui);
matrix = buildDayMatrix(function(cell) {
eventElement.draggable('option', 'revert', !cell || !cell.rowDelta && !cell.colDelta);
clearOverlays();
view.hideEvents(event, eventElement);
hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
clearOverlay();
if (cell) {
dayDelta = cell.rowDelta*7 + cell.colDelta*dis;
renderDayOverlays(
matrix,
dayDelta = rowDelta*7 + colDelta*dis;
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
}else{
dayDelta = 0;
}
});
matrix.mouse(ev);
},
drag: function(ev) {
matrix.mouse(ev);
}, ev, 'drag');
},
stop: function(ev, ui) {
clearOverlays();
hoverListener.stop();
clearOverlay();
view.trigger('eventDragStop', eventElement, event, ev, ui);
if (dayDelta) {
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
@ -495,68 +488,108 @@ function Grid(element, options, methods) {
/* Coordinate Utilities
--------------------------------------------------------*/
var coordinateGrid = new CoordinateGrid(function(rows, cols) {
var e, n, p;
var tds = tbody.find('tr:first td');
if (rtl) {
tds = $(tds.get().reverse());
}
tds.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();
tbody.find('tr').each(function(i, _e) {
e = $(_e);
n = e.offset().top;
if (i) {
p[1] = n;
}
p = [n];
rows[i] = p;
});
p[1] = n + e.outerHeight();
});
var hoverListener = new HoverListener(coordinateGrid);
/* Selecting
--------------------------------------------------------*/
selectionManager = new SelectionManager(
view,
unselect,
function(startDate, endDate, allDay) {
renderDayOverlays(
selectionMatrix,
startDate,
addDays(cloneDate(endDate), 1)
);
},
clearOverlays
var selected = false;
var selectionMousedown = selection_dayMousedown(
view, hoverListener, cellDate, renderDayOverlay, clearOverlay, reportSelection, unselect
);
function selectionMousedown(ev) {
if (view.option('selectable')) {
selectionMatrix = buildDayMatrix(function(cell) {
if (cell) {
var d = cellDate(cell.row, cell.col);
selectionManager.drag(d, d, true);
}else{
selectionManager.drag();
view.select = function(startDate, endDate, allDay) {
coordinateGrid.build();
unselect();
if (!endDate) {
endDate = cloneDate(startDate);
}
});
documentDragHelp(
function(ev) {
selectionMatrix.mouse(ev);
},
function(ev) {
selectionManager.dragStop(ev);
}
);
selectionManager.dragStart(ev);
selectionMatrix.mouse(ev);
return false; // prevent auto-unselect and text selection
}
}
documentUnselectAuto(view, unselect);
view.select = function(start, end, allDay) {
if (!end) {
end = cloneDate(start);
}
selectionMatrix = buildDayMatrix();
selectionManager.select(start, end, allDay);
renderDayOverlay(startDate, addDays(cloneDate(endDate), 1));
reportSelection(startDate, endDate, allDay);
};
function reportSelection(startDate, endDate, allDay) {
selected = true;
view.trigger('select', view, startDate, endDate, allDay);
}
function unselect() {
selectionManager.unselect();
if (selected) {
clearOverlay();
selected = false;
view.trigger('unselect', view);
}
}
view.unselect = unselect;
selection_unselectAuto(view, unselect);
/* External dragging
------------------------------------------------------*/
view.isExternalDraggable = function(_element) {
return _element.parentNode != segmentContainer[0];
};
view.dragStart = function(ev, ui) {
hoverListener.start(function(cell) {
clearOverlay();
if (cell) {
_renderDayOverlay(cell.row, cell.col, cell.row, cell.col);
}
}, ev);
};
view.dragStop = function(ev, ui) {
var cell = hoverListener.stop();
clearOverlay();
if (cell) {
var d = cellDate(cell);
view.trigger('drop', view, d, true, ev, ui);
}
};
/* Semi-transparent Overlay Helpers
------------------------------------------------------*/
function renderDayOverlays(matrix, overlayStart, overlayEnd) { // overlayEnd is exclusive
function renderDayOverlay(overlayStart, overlayEnd) { // overlayEnd is exclusive
var rowStart = cloneDate(view.visStart);
var rowEnd = addDays(cloneDate(rowStart), colCnt);
for (var i=0; i<rowCnt; i++) {
@ -571,9 +604,8 @@ function Grid(element, options, methods) {
colStart = dayDiff(stretchStart, rowStart);
colEnd = dayDiff(stretchEnd, rowStart);
}
var rect = matrix.rect(i, colStart, i+1, colEnd, element);
dayBind(
view.renderOverlay(rect, element)
_renderDayOverlay(i, colStart, i, colEnd-1)
);
}
addDays(rowStart, 7);
@ -581,28 +613,23 @@ function Grid(element, options, methods) {
}
}
function clearOverlays() {
function _renderDayOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
return view.renderOverlay(rect, element);
}
function clearOverlay() {
view.clearOverlays();
}
/* Utils
/* Date Utils
---------------------------------------------------*/
function buildDayMatrix(changeCallback) {
var tds = tbody.find('tr:first td');
if (rtl) {
tds = $(tds.get().reverse());
}
return new HoverMatrix(tbody.find('tr'), tds, changeCallback);
}
function cellDate(r, c) { // convert r,c to date
return addDays(cloneDate(view.visStart), r*7 + c*dis+dit);
function cellDate(cell) {
return addDays(cloneDate(view.visStart), cell.row*7 + cell.col*dis+dit);
// TODO: what about weekends in middle of week?
}

View file

@ -840,6 +840,21 @@ $.fn.fullCalendar = function(options) {
$(window).resize(windowResize);
if (options.droppable) {
$(document)
.bind('dragstart', function(ev, ui) {
if (view.isExternalDraggable(ev.target)) {
view.dragStart(ev, ui);
}
})
.bind('dragstop', function(ev, ui) {
if (view.isExternalDraggable(ev.target)) {
view.dragStop(ev, ui);
}
});
}
// let's begin...
changeView(options.defaultView);

View file

@ -16,5 +16,5 @@
*
*/
(function($) {
(function($, undefined) {

View file

@ -1,96 +1,28 @@
function SelectionManager(view, initFunc, displayFunc, clearFunc) {
var t = this;
var selected = false;
var initialElement;
var initialRange;
var start;
var end;
var allDay;
t.dragStart = function(ev) {
initFunc();
start = end = undefined;
initialRange = undefined;
initialElement = ev.currentTarget;
};
t.drag = function(currentStart, currentEnd, currentAllDay) {
if (currentStart) {
var range = [currentStart, currentEnd];
if (!initialRange) {
initialRange = range;
function selection_dayMousedown(view, hoverListener, cellDate, renderSelection, clearSelection, reportSelection, unselect) {
return function(ev) {
if (view.option('selectable')) {
unselect();
var dates;
hoverListener.start(function(cell, origCell) {
clearSelection();
if (cell) {
dates = [ cellDate(origCell), cellDate(cell) ].sort(cmp);
renderSelection(dates[0], addDays(cloneDate(dates[1]), 1), true);
}
var dates = initialRange.concat(range).sort(cmp);
start = dates[0];
end = dates[3];
allDay = currentAllDay;
clearFunc();
displayFunc(cloneDate(start), cloneDate(end), allDay);
}else{
// called with no arguments
start = end = undefined;
clearFunc();
}, ev);
$(document).one('mouseup', function(ev) {
if (hoverListener.stop()) { // over a cell?
reportSelection(dates[0], dates[1], true);
}
};
t.dragStop = function(ev) {
if (start) {
if (+initialRange[0] == +start && +initialRange[1] == +end) {
view.trigger('dayClick', initialElement, start, allDay, ev);
}
_select();
}
};
t.select = function(newStart, newEnd, newAllDay) {
initFunc();
start = newStart;
end = newEnd;
allDay = newAllDay;
displayFunc(cloneDate(start), cloneDate(end), allDay);
_select();
};
function _select() { // just set the selected flag, and trigger
selected = true;
view.trigger('select', view, start, end, allDay);
}
function unselect() {
if (selected) {
selected = false;
start = end = undefined;
clearFunc();
view.trigger('unselect', view);
});
}
}
t.unselect = unselect;
}
function documentDragHelp(mousemove, mouseup) {
function _mouseup(ev) {
mouseup(ev);
$(document)
.unbind('mousemove', mousemove)
.unbind('mouseup', _mouseup);
}
$(document)
.mousemove(mousemove)
.mouseup(_mouseup);
}
function documentUnselectAuto(view, unselectFunc) {
function selection_unselectAuto(view, unselect) {
if (view.option('selectable') && view.option('unselectAuto')) {
$(document).mousedown(function(ev) {
var ignore = view.option('unselectCancel');
@ -99,10 +31,7 @@ function documentUnselectAuto(view, unselectFunc) {
return;
}
}
unselectFunc();
unselect();
});
}
}

View file

@ -378,72 +378,47 @@ function topCorrect(tr) { // tr/th/td or anything else
/* Hover Matrix
/* Coordinate Grid
-----------------------------------------------------------------------------*/
function HoverMatrix(rowElements, colElements, changeCallback) {
function CoordinateGrid(buildFunc) {
var t=this,
tops=[], lefts=[],
origRow, origCol,
currRow, currCol,
e;
var t = this;
var rows;
var cols;
$.each(rowElements, function(i, _e) {
e = $(_e);
tops.push(e.offset().top + topCorrect(e));
});
tops.push(tops[tops.length-1] + e.outerHeight());
$.each(colElements, function(i, _e) {
e = $(_e);
lefts.push(e.offset().left);
});
lefts.push(lefts[lefts.length-1] + e.outerWidth());
t.mouse = function(ev) {
var x = ev.pageX;
var y = ev.pageY;
var r, c;
for (r=0; r<tops.length && y>=tops[r]; r++) {}
for (c=0; c<lefts.length && x>=lefts[c]; c++) {}
r = r >= tops.length ? -1 : r - 1;
c = c >= lefts.length ? -1 : c - 1;
if (r != currRow || c != currCol) {
currRow = r;
currCol = c;
if (r == -1 || c == -1) {
t.cell = null;
}else{
if (origRow === undefined) {
origRow = r;
origCol = c;
}
t.cell = {
row: r,
col: c,
top: tops[r],
left: lefts[c],
width: lefts[c+1] - lefts[c],
height: tops[r+1] - tops[r],
origRow: origRow,
origCol: origCol,
isOrig: r==origRow && c==origCol,
rowDelta: r-origRow,
colDelta: c-origCol
};
}
changeCallback(t.cell);
}
t.build = function() {
rows = [];
cols = [];
buildFunc(rows, cols);
};
t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 are exclusive
t.cell = function(x, y) {
var rowCnt = rows.length;
var colCnt = cols.length;
var i, r=-1, c=-1;
for (i=0; i<rowCnt; i++) {
if (y >= rows[i][0] && y < rows[i][1]) {
r = i;
break;
}
}
for (i=0; i<colCnt; i++) {
if (x >= cols[i][0] && x < cols[i][1]) {
c = i;
break;
}
}
return (r>=0 && c>=0) ? { row:r, col:c } : null;
};
t.rect = function(row0, col0, row1, col1, originElement) { // row1,col1 is inclusive
var origin = originElement.offset();
return {
top: tops[row0] - origin.top,
left: lefts[col0] - origin.left,
width: lefts[col1] - lefts[col0],
height: tops[row1] - tops[row0]
top: rows[row0][0] - origin.top,
left: cols[col0][0] - origin.left,
width: cols[col1][1] - cols[col0][0],
height: rows[row1][1] - rows[row0][0]
};
};
@ -451,11 +426,54 @@ function HoverMatrix(rowElements, colElements, changeCallback) {
/* Hover Listener
-----------------------------------------------------------------------------*/
function HoverListener(coordinateGrid) {
var t = this;
var bindType;
var change;
var firstCell;
var cell;
t.start = function(_change, ev, _bindType) {
change = _change;
firstCell = cell = null;
coordinateGrid.build();
mouse(ev);
bindType = _bindType || 'mousemove';
$(document).bind(bindType, mouse);
};
function mouse(ev) {
var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
if (newCell) {
if (!firstCell) {
firstCell = newCell;
}
change(newCell, firstCell, newCell.row-firstCell.row, newCell.col-firstCell.col);
}else{
change(newCell, firstCell);
}
cell = newCell;
}
}
t.stop = function() {
$(document).unbind(bindType, mouse);
return cell;
};
}
/* Misc Utils
-----------------------------------------------------------------------------*/
var undefined,
dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
function zeroPad(n) {
return (n < 10 ? '0' : '') + n;

132
tests/droppable.html Normal file
View file

@ -0,0 +1,132 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<link rel='stylesheet' type='text/css' href='../examples/redmond/theme.css' />
<script type='text/javascript' src='loader.js'></script>
<script type='text/javascript'>
$(document).ready(function() {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
$('#calendar').fullCalendar({
droppable: true,
drop: function(date, allDay) {
console.log('drop', date, allDay);
},
//defaultView: 'agendaWeek',
//isRTL: true,
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
},
editable: true,
events: [
{
title: 'All Day Event',
start: new Date(y, m, 1)
},
{
title: 'Long Event',
start: new Date(y, m, d-5),
end: new Date(y, m, d-2)
},
{
id: 999,
title: 'Repeating Event',
start: new Date(y, m, d-3, 16, 0),
allDay: false
},
{
id: 999,
title: 'Repeating Event',
start: new Date(y, m, d+4, 16, 0),
allDay: false
},
{
title: 'Meeting',
start: new Date(y, m, d, 10, 30),
allDay: false
},
{
title: 'Lunch',
start: new Date(y, m, d, 12, 5),
end: new Date(y, m, d, 14, 43),
allDay: false
},
{
title: 'Birthday Party',
start: new Date(y, m, d+1, 19, 0),
end: new Date(y, m, d+1, 22, 30),
allDay: false
},
{
title: 'Click for Google',
start: new Date(y, m, 28),
end: new Date(y, m, 29),
url: 'http://google.com/'
}
]
});
$('.external-event').draggable({
revert: true,
revertDuration: 0
});
});
</script>
<style type='text/css'>
body {
margin-top: 40px;
text-align: center;
font-size: 13px;
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
}
#calendar {
width: 900px;
float: left;
}
#external-events {
position: relative;
left: 50px;
text-align: left;
float: left;
width: 140px;
padding: 10px;
border: 1px solid #aaa;
background: #ccc;
}
.external-event {
height: 20px;
line-height: 20px;
color: #fff;
background: blue;
margin-bottom: 10px;
padding-left: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<div id='calendar'></div>
<div id='external-events'>
<div class='external-event'>Draggable 1</div>
<div class='external-event'>Draggable 2</div>
<div class='external-event'>Draggable 3</div>
</div>
</body>
</html>

View file

@ -19,6 +19,7 @@
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
},
editable: true,
//isRTL: true,
events: [
{
title: 'All Day Event',

View file

@ -1 +1 @@
1.4.6
1.4.7