moved selectable into own plugin file. selectable bugfixes

v1.4.x
Adam Shaw 2010-05-02 21:47:23 -07:00
parent b75d05c6d5
commit 1b24a5f63b
8 changed files with 434 additions and 288 deletions

View File

@ -37,6 +37,9 @@ zip:
@cat ${SRC_DIR}/gcal.js \
| ${VER_SED} | ${DATE_SED} \
> ${BUILD_DIR}/fullcalendar/gcal.js
@cat ${SRC_DIR}/selectable.js \
| ${VER_SED} | ${DATE_SED} \
> ${BUILD_DIR}/fullcalendar/selectable.js
@echo "compressing js..."
@java -jar ${BUILD_DIR}/compiler.jar --js ${BUILD_DIR}/fullcalendar/fullcalendar.js \

View File

@ -14,6 +14,7 @@
<script type='text/javascript' src='../src/agenda.js'></script>
<script type='text/javascript' src='../src/view.js'></script>
<script type='text/javascript' src='../src/util.js'></script>
<script type='text/javascript' src='../src/selectable.js'></script>
<!--</src>-->
<!--
<dist>
@ -23,6 +24,7 @@
<script type='text/javascript' src='../jquery/ui.draggable.js'></script>
<script type='text/javascript' src='../jquery/ui.resizable.js'></script>
<script type='text/javascript' src='../fullcalendar.min.js'></script>
<script type='text/javascript' src='../selectable.js'></script>
</dist>
-->
<script type='text/javascript'>
@ -34,36 +36,38 @@
var m = date.getMonth();
var y = date.getFullYear();
$('#calendar').fullCalendar({
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
editable: true,
/********** new selecting options **********/
selectable: true, // activate selecting!
unselectable: false, // automatically hide the selection when user clicks elsewhere? (defaults to true)
selectHelper: true, // use a "fake" event for selecting? (only works in agenda views right now)
select: function(start, end, allDay, view) {
alert(
'---- selection ----\n' +
'start: ' + start + '\n' +
'end: ' + end + '\n' + // exclusive!!
'allDay: ' + allDay
);
if (confirm("clear the selection?")) {
$('#calendar').fullCalendar('unselect'); // 'unselect' method to manually clear selection
// a 'select' method coming soon...
}
},
unselect: function() {
//console.log('unselected');
},
/******************************************/
editable: true,
/********** new selecting options **********/
selectable: true, // activate selecting!
unselectable: true, // automatically hide the selection when user clicks elsewhere? (defaults to true)
selectHelper: true, // use a "fake" event for selecting? (only works in agenda views right now)
select: function(start, end, allDay, view) {
console.log(
'---- selection ----\n' +
'start: ' + start + '\n' +
'end: ' + end + '\n' + // exclusive!!
'allDay: ' + allDay
);
/*
if (confirm("clear the selection?")) {
$('#calendar').fullCalendar('unselect'); // 'unselect' method to manually clear selection
// a 'select' method coming soon...
}
*/
},
unselect: function() {
console.log('unselected');
},
/******************************************/
events: [
{
@ -135,4 +139,4 @@
<body>
<div id='calendar'></div>
</body>
</html>
</html>

View File

@ -125,6 +125,11 @@ function Agenda(element, options, methods) {
}
function renderAgenda(c, colFormat) {
if (view.beforeRender) {
view.beforeRender();
}
colCnt = c;
// update option-derived variables
@ -202,7 +207,7 @@ function Agenda(element, options, methods) {
}
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(view.bodyContent = bodyContent = $("<div style='position:relative;overflow:hidden'>")
.append(bodyTable = $(s)))
.appendTo(element);
bindSlotHandlers(body.find('td')); // .click(slotClick);
@ -266,8 +271,6 @@ function Agenda(element, options, methods) {
}
unselect();
}
@ -341,12 +344,27 @@ function Agenda(element, options, methods) {
/* Slot/Day clicking and selecting
/* Slot/Day clicking and binding
-----------------------------------------------------------------------*/
var selected=false,
selectHelper,
selectMatrix;
function bindDayHandlers(tds) {
tds.click(slotClick);
if ($.fullCalendar.bindBgHandlers) {
$.fullCalendar.bindBgHandlers(view, tds, true);
}
}
view.bindDayHandlers = bindDayHandlers;
function bindSlotHandlers(tds) {
tds.click(slotClick);
if ($.fullCalendar.bindBgHandlers) {
$.fullCalendar.bindBgHandlers(view, tds, false);
}
}
view.bindSlotHandlers = bindSlotHandlers;
function slotClick(ev) {
var col = Math.floor((ev.pageX - bg.offset().left) / colWidth),
@ -363,173 +381,6 @@ function Agenda(element, options, methods) {
}
}
function unselect() {
if (selected) {
if (selectHelper) {
selectHelper.remove();
selectHelper = null;
}
view.clearOverlays();
view.trigger('unselect', view);
selected = false;
}
}
view.unselect = unselect;
if (view.option('selectable') && view.option('unselectable')) {
$(document).mousedown(function() {
unselect();
});
}
// all-day
var daySelectStart, // the "column" of the day
daySelectEnd, // the "column" of the day
daySelectRange;
function bindDayHandlers(tds) {
tds.click(slotClick);
if (view.option('selectable')) {
tds.mousedown(daySelectMousedown);
}
}
function daySelectMousedown(ev) {
daySelectStart = undefined;
selectMatrix = buildMainMatrix(function(cell) {
view.clearOverlays();
if (selectHelper) {
selectHelper.remove(); // todo: turn these lines into _unselect()
selectHelper = null;
}
if (cell) {
selected = true;
daySelectEnd = cell.col;
if (daySelectStart === undefined) {
daySelectStart = daySelectEnd;
}
daySelectRange = [daySelectStart, daySelectEnd].sort(cmp);
renderDayOverlay(selectMatrix, daySelectRange[0], daySelectRange[1]+1);
bindDayHandlers(view.overlays[0]);
}else{
selected = false;
}
});
$(document)
.mousemove(daySelectMousemove)
.mouseup(daySelectMouseup);
selectMatrix.mouse(ev.pageX, ev.pageY);
ev.stopPropagation();
}
function daySelectMousemove(ev) {
selectMatrix.mouse(ev.pageX, ev.pageY);
}
function daySelectMouseup(ev) {
$(document)
.unbind('mousemove', daySelectMousemove)
.unbind('mouseup', daySelectMouseup);
if (selected) {
view.trigger(
'select',
view,
addDays(cloneDate(view.visStart), daySelectRange[0]),
addDays(cloneDate(view.visStart), daySelectRange[1]+1),
true
);
}
}
// slot
function bindSlotHandlers(tds) {
tds.click(slotClick);
if (view.option('selectable')) {
tds.mousedown(slotSelectMousedown);
}
}
var slotSelectDay,
slotSelectStart, // the "row" of the slot
slotSelectEnd, // the "row" of the slot
slotSelectRange;
function slotSelectMousedown(ev) {
slotSelectDay = undefined;
selectMatrix = buildSlotMatrix(function(cell) {
view.clearOverlays();
if (slotSelectDay === undefined) {
slotSelectDay = cell.col;
slotSelectStart = cell.row;
}
if (selectHelper) {
selectHelper.remove();
selectHelper = null;
}
if (cell) {
selected = true;
slotSelectEnd = cell.row;
slotSelectRange = [slotSelectStart, slotSelectEnd].sort(cmp);
if (view.option('selectHelper')) {
var rect = selectMatrix.rect(slotSelectRange[0], slotSelectDay, slotSelectRange[1]+1, slotSelectDay+1, bodyContent);
selectHelper =
$(segHtml(
{ title: '', start: slotTime(slotSelectDay, slotSelectRange[0]), end: slotTime(slotSelectDay, slotSelectRange[1]+1), className: [], editable:false },
{ top: rect.top, left: rect.left+2 },
'fc-event fc-event-vert fc-corner-top fc-corner-bottom '
));
if (!$.browser.msie) {
// IE makes the event completely clear!!?
selectHelper.css('opacity', view.option('dragOpacity'));
}
// TODO: change cursor
bindSlotHandlers(selectHelper);
bodyContent.append(selectHelper);
setOuterWidth(selectHelper, rect.width-5, true);
setOuterHeight(selectHelper, rect.height, true);
}else{
view.renderOverlay(
selectMatrix.rect(slotSelectRange[0], slotSelectDay, slotSelectRange[1]+1, slotSelectDay+1, bodyContent),
bodyContent
);
bindSlotHandlers(view.overlays[0]);
}
}else{
selected = false;
slotSelectEnd = undefined;
}
});
selectMatrix.mouse(ev.pageX, ev.pageY);
$(document)
.mousemove(slotSelectMousemove)
.mouseup(slotSelectMouseup);
ev.stopPropagation();
}
function slotSelectMousemove(ev) {
selectMatrix.mouse(ev.pageX, ev.pageY);
}
function slotSelectMouseup(ev) {
$(document)
.unbind('mousemove', slotSelectMousemove)
.unbind('mouseup', slotSelectMouseup);
if (selected) {
view.trigger('select',
view,
slotTime(slotSelectDay, slotSelectRange[0]),
slotTime(slotSelectDay, slotSelectRange[1]+1),
false
);
}
}
/* Event Rendering
@ -787,6 +638,7 @@ function Agenda(element, options, methods) {
: '') +
"</div>";
}
view.segHtml = segHtml;
function visEventEnd(event) { // returns exclusive 'visible' end, for rendering
@ -1150,6 +1002,7 @@ function Agenda(element, options, methods) {
}
return d;
}
view.slotTime = slotTime;
// matrix building
@ -1165,6 +1018,7 @@ function Agenda(element, options, methods) {
matrix.row(body);
return matrix;
}
view.buildMainMatrix = buildMainMatrix;
function buildSlotMatrix(changeCallback) {
var matrix = new HoverMatrix(changeCallback);
@ -1176,6 +1030,7 @@ function Agenda(element, options, methods) {
});
return matrix;
}
view.buildSlotMatrix = buildSlotMatrix;
// overlay for dropping and selecting
@ -1186,6 +1041,7 @@ function Agenda(element, options, methods) {
head
);
}
view.renderDayOverlay = renderDayOverlay;
}

View File

@ -149,8 +149,13 @@ function Grid(element, options, methods) {
}
function renderGrid(r, c, colFormat, showNumbers) {
if (view.beforeRender) {
view.beforeRender();
}
rowCnt = r;
colCnt = c;
colCnt = view.colCnt = c;
// update option-derived variables
tm = options.theme ? 'ui' : 'fc';
@ -298,8 +303,6 @@ function Grid(element, options, methods) {
}
unselect();
}
@ -487,16 +490,17 @@ function Grid(element, options, methods) {
/* Day clicking and selecting
/* Day clicking and day event binding
---------------------------------------------------------*/
function bindDayHandlers(days) {
days.click(dayClick)
if (view.option('selectable')) {
days.mousedown(selectMousedown);
days.click(dayClick);
if ($.fullCalendar.bindBgHandlers) {
$.fullCalendar.bindBgHandlers(view, days, true);
}
}
view.bindDayHandlers = bindDayHandlers;
function dayClick(ev) {
@ -509,74 +513,6 @@ function Grid(element, options, methods) {
}
var selected=false,
selectMatrix,
selectStart, // the "offset" (row*colCnt+col) of the cell
selectEnd, // the "offset" (row*colCnt+col) of the cell (inclusive)
selectRange;
function selectMousedown(ev) {
selectStart = undefined;
selectMatrix = buildMatrix(function(cell) {
view.clearOverlays();
if (cell) {
selected = true;
selectEnd = cell.row * colCnt + cell.col;
if (selectStart === undefined) {
selectStart = selectEnd;
}
selectRange = [selectStart, selectEnd].sort(cmp);
renderOverlays(selectMatrix, selectRange[0], selectRange[1]+1);
$.each(view.overlays, function() {
bindDayHandlers(this);
});
}else{
selected = false;
}
});
$(document)
.mousemove(selectMousemove)
.mouseup(selectMouseup);
selectMatrix.mouse(ev.pageX, ev.pageY);
ev.stopPropagation();
}
function selectMousemove(ev) {
selectMatrix.mouse(ev.pageX, ev.pageY);
}
function selectMouseup(ev) {
$(document)
.unbind('mousemove', selectMousemove)
.unbind('mouseup', selectMouseup);
if (selected) {
view.trigger(
'select',
view,
offset2date(selectRange[0]),
offset2date(selectRange[1]+1),
true
);
}
// todo: if a selection was made before this one, and this one ended up unselected, fire unselect
}
function unselect() {
if (selected) {
view.clearOverlays();
selected = false;
view.trigger('unselect', view);
}
}
view.unselect = unselect;
if (view.option('selectable') && view.option('unselectable')) {
$(document).mousedown(function() {
unselect();
});
}
/* Utilities
@ -605,6 +541,7 @@ function Grid(element, options, methods) {
function offset2date(cellOffset) {
return addDays(cloneDate(view.visStart), cellOffset);
}
view.offset2date = offset2date;
function renderOverlays(matrix, offset, endOffset) {
@ -624,6 +561,7 @@ function Grid(element, options, methods) {
c = 0;
}
}
view.renderOverlays = renderOverlays;
function buildMatrix(changeCallback) {
@ -640,6 +578,7 @@ function Grid(element, options, methods) {
});
return matrix;
}
view.buildMatrix = buildMatrix;
}

View File

@ -113,9 +113,15 @@ $.fn.fullCalendar = function(options) {
this.each(function() {
var data = $.data(this, 'fullCalendar');
if (data) {
var r = data[options].apply(this, args);
if (res === undefined) {
res = r;
var meth = data[options];
if (!meth) {
meth = $.fullCalendar.publicMethods[options];
}
if (meth) {
var r = meth.apply(this, args);
if (res === undefined) {
res = r;
}
}
}
});
@ -195,6 +201,8 @@ $.fn.fullCalendar = function(options) {
function changeView(v) {
if (v != viewName) {
ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached
viewUpdate();
var oldView = view,
newViewElement;
@ -249,6 +257,8 @@ $.fn.fullCalendar = function(options) {
function render(inc) {
if (elementVisible()) {
ignoreWindowResize++; // because view.renderEvents might temporarily change the height before setSize is reached
viewUpdate();
if (suggestedViewHeight === undefined) {
calcSize();
@ -303,6 +313,13 @@ $.fn.fullCalendar = function(options) {
function bodyVisible() {
return $('body')[0].offsetWidth !== 0;
}
function viewUpdate() {
// this function is ONLY for the ghetto plugin archicture
if (view && view.viewUpdate) {
view.viewUpdate();
}
}
// called when any event objects have been added/removed/changed, rerenders
@ -650,14 +667,6 @@ $.fn.fullCalendar = function(options) {
refetchEvents: function() {
fetchEvents(eventsChanged);
},
unselect: function() {
for (n in viewInstances) {
viewInstances[n].unselect();
}
}
};

328
src/selectable.js Normal file
View File

@ -0,0 +1,328 @@
(function($, undefined) {
var fc = $.fullCalendar,
setOuterWidth = fc.setOuterWidth,
setOuterHeight = fc.setOuterHeight,
addDays = fc.addDays,
cloneDate = fc.cloneDate,
cmp = fc.cmp;
var selectionManagers = {};
$.fullCalendar.bindBgHandlers = function(view, elements, allDay) {
if (view.option('selectable')) {
getSelectionManager(view).bind(elements, allDay);
}
};
function getSelectionManager(view) {
var name = view.name,
manager = selectionManagers[name];
if (!manager) {
if (name.indexOf('agenda') == 0) {
manager = new AgendaSelectionManager(view);
}else{
manager = new GridSelectionManager(view);
}
selectionManagers[name] = manager;
}
return manager;
}
/* methods
---------------------------------------------------*/
$.extend($.fullCalendar.publicMethods, {
select: function(start, end, allDay) {
getSelectionManager($(this).fullCalendar('getView')).select(start, end, allDay);
// not yet implemented...
},
unselect: function() {
for (var name in selectionManagers) {
selectionManagers[name].unselect();
}
}
});
/* month, basicWeek, basicDay
---------------------------------------------------*/
function GridSelectionManager(view) {
var selected=false,
matrix,
start, // the "offset" (row*colCnt+col) of the cell
end, // the "offset" (row*colCnt+col) of the cell (inclusive)
range; // sorted array
this.bind = function(elements) {
elements.mousedown(mousedown);
};
function mousedown(ev) {
start = undefined;
matrix = view.buildMatrix(function(cell) {
view.clearOverlays();
if (cell) {
end = cell.row * view.colCnt + cell.col;
if (start === undefined) {
unselect();
start = end;
}
range = [start, end].sort(cmp);
view.renderOverlays(matrix, range[0], range[1]+1);
$.each(view.overlays, function() {
view.bindDayHandlers(this);
});
selected = true;
}else{
selected = false;
}
});
$(document)
.mousemove(mousemove)
.mouseup(mouseup);
matrix.mouse(ev.pageX, ev.pageY);
ev.stopPropagation();
}
function mousemove(ev) {
matrix.mouse(ev.pageX, ev.pageY);
}
function mouseup(ev) {
$(document)
.unbind('mousemove', mousemove)
.unbind('mouseup', mouseup);
if (selected) {
view.trigger(
'select',
view,
view.offset2date(range[0]),
view.offset2date(range[1]+1),
true
);
}
}
function unselect() {
if (selected) {
view.clearOverlays();
view.trigger('unselect', view);
selected = false;
}
}
this.unselect = unselect;
view.viewUpdate = unselect;
if (view.option('unselectable')) {
$(document).mousedown(function() {
unselect();
});
}
}
/* agenda views
-----------------------------------------------*/
function AgendaSelectionManager(view) {
var selected=false,
matrix,
start, // for all-day, the COLUMN of the day. for slot, the ROW of the slot
end, // for all-day, the COLUMN of the day. for slot, the ROW of the slot
day, // only used for slots. the COLUMN of the day
range, // start & end, sorted array
helper;
this.bind = function(elements, allDay) {
if (allDay) {
dayBind(elements);
}else{
slotBind(elements);
}
};
// all-day
function dayBind(elements) {
elements.mousedown(dayMousedown);
}
function dayMousedown(ev) {
start = undefined;
matrix = view.buildMainMatrix(function(cell) {
clear();
if (cell) {
end = cell.col;
if (start === undefined) {
unselect();
start = end;
}
range = [start, end].sort(cmp);
view.renderDayOverlay(matrix, range[0], range[1]+1);
view.bindDayHandlers(view.overlays[0]);
selected = true;
}else{
selected = false;
}
});
$(document)
.mousemove(dayMousemove)
.mouseup(dayMouseup);
matrix.mouse(ev.pageX, ev.pageY);
ev.stopPropagation();
}
function dayMousemove(ev) {
matrix.mouse(ev.pageX, ev.pageY);
}
function dayMouseup(ev) {
$(document)
.unbind('mousemove', dayMousemove)
.unbind('mouseup', dayMouseup);
if (selected) {
view.trigger(
'select',
view,
addDays(cloneDate(view.visStart), range[0]),
addDays(cloneDate(view.visStart), range[1]+1),
true
);
}
}
// slot
function slotBind(elements) {
elements.mousedown(slotMousedown);
}
function slotMousedown(ev) {
day = undefined;
matrix = view.buildSlotMatrix(function(cell) {
clear();
if (cell) {
if (day === undefined) {
unselect();
day = cell.col;
start = cell.row;
}
end = cell.row;
range = [start, end].sort(cmp);
var helperOption = view.option('selectHelper'),
bodyContent = view.bodyContent;
if (helperOption) {
var rect = matrix.rect(range[0], day, range[1]+1, day+1, bodyContent);
rect.left += 2;
rect.width -= 5;
if ($.isFunction(helperOption)) {
helper = helperOption();
if (helper) {
helper.css(rect);
}
}else{
helper = $(view.segHtml(
{
title: '',
start: view.slotTime(day, range[0]),
end: view.slotTime(day, range[1]+1),
className: [],
editable: false
},
rect,
'fc-event fc-event-vert fc-corner-top fc-corner-bottom '
));
if (!$.browser.msie) {
// IE makes the event completely clear!!?
helper.css('opacity', view.option('dragOpacity'));
}
}
if (helper) {
// TODO: change cursor
view.bindSlotHandlers(helper);
bodyContent.append(helper);
setOuterWidth(helper, rect.width, true);
setOuterHeight(helper, rect.height, true);
}
}else{
view.renderOverlay(
matrix.rect(range[0], day, range[1]+1, day+1, bodyContent),
bodyContent
);
view.bindSlotHandlers(view.overlays[0]);
}
selected = true;
}else{
selected = false;
}
});
matrix.mouse(ev.pageX, ev.pageY);
$(document)
.mousemove(slotMousemove)
.mouseup(slotMouseup);
ev.stopPropagation();
}
function slotMousemove(ev) {
matrix.mouse(ev.pageX, ev.pageY);
}
function slotMouseup(ev) {
$(document)
.unbind('mousemove', slotMousemove)
.unbind('mouseup', slotMouseup);
if (selected) {
view.trigger('select',
view,
view.slotTime(day, range[0]),
view.slotTime(day, range[1]+1),
false
);
}
}
// common
function unselect() {
if (selected) {
clear();
view.trigger('unselect', view);
selected = false;
}
}
this.unselect = unselect;
view.viewUpdate = unselect;
function clear() {
if (helper) {
helper.remove();
helper = null;
}
view.clearOverlays();
}
if (view.option('unselectable')) {
$(document).mousedown(function() {
unselect();
});
}
}
})(jQuery);

View File

@ -74,6 +74,7 @@ function cloneDate(d, dontKeepTime) {
}
return new Date(+d);
}
fc.cloneDate = cloneDate;
function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
var i=0, d;
@ -304,12 +305,14 @@ function setOuterWidth(element, width, includeMargins) {
_element.style.width = width - hsides(_element, includeMargins) + 'px';
});
}
fc.setOuterWidth = setOuterWidth;
function setOuterHeight(element, height, includeMargins) {
element.each(function(i, _element) {
_element.style.height = height - vsides(_element, includeMargins) + 'px';
});
}
fc.setOuterHeight = setOuterHeight;
function hsides(_element, includeMargins) {
@ -516,9 +519,13 @@ function cssKey(_element) {
}
function cmp(a,b) {
function cmp(a, b) {
return a - b;
}
fc.cmp = cmp;
fc.publicMethods = {};

View File

@ -1 +1 @@
1.4.5
1.4.6