restructured HTML/CSS of event elements. solves issues 327 and 395

This commit is contained in:
Adam Shaw 2011-02-19 15:47:23 -08:00
parent a2e1de5d3d
commit afadc63b78
9 changed files with 504 additions and 429 deletions

View file

@ -15,6 +15,9 @@ function AgendaEventRenderer() {
DayEventRenderer.call(t);
var opt = t.opt;
var trigger = t.trigger;
//var setOverflowHidden = t.setOverflowHidden;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var eventEnd = t.eventEnd;
var reportEvents = t.reportEvents;
var reportEventClear = t.reportEventClear;
@ -171,13 +174,6 @@ function AgendaEventRenderer() {
for (i=0; i<segCnt; i++) {
seg = segs[i];
event = seg.event;
classes = ['fc-event', 'fc-event-vert'];
if (seg.isStart) {
classes.push('fc-corner-top');
}
if (seg.isEnd) {
classes.push('fc-corner-bottom');
}
top = timePosition(seg.start, seg.start);
bottom = timePosition(seg.start, seg.end);
colI = seg.col;
@ -205,7 +201,7 @@ function AgendaEventRenderer() {
seg.left = left;
seg.outerWidth = outerWidth;
seg.outerHeight = bottom - top;
html += slotSegHtml(event, seg, classes);
html += slotSegHtml(event, seg);
}
slotSegmentContainer[0].innerHTML = html; // faster than html()
eventElements = slotSegmentContainer.children();
@ -278,16 +274,33 @@ function AgendaEventRenderer() {
}
function slotSegHtml(event, seg, classes) {
classes = classes.concat(event.className, event.source.className);
function slotSegHtml(event, seg) {
var html = "<";
var url = event.url;
var skinCss = getSkinCss(event, opt);
var skinCssAttr = (skinCss ? " style='" + skinCss + "'" : '');
return "<div class='" + classes.join(' ') + "' style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'>" +
"<a class='fc-event-inner'" +
(event.url ? " href='" + htmlEscape(event.url) + "'" : '') + // good for escaping quotes?
skinCssAttr +
var classes = ['fc-event', 'fc-event-skin', 'fc-event-vert'];
if (isEventDraggable(event)) {
classes.push('fc-event-draggable');
}
if (seg.isStart) {
classes.push('fc-corner-top');
}
if (seg.isEnd) {
classes.push('fc-corner-bottom');
}
classes = classes.concat(event.className, event.source.className);
if (url) {
html += "a href='" + htmlEscape(event.url) + "'";
}else{
html += "div";
}
html +=
" class='" + classes.join(' ') + "'" +
" style='position:absolute;z-index:8;top:" + seg.top + "px;left:" + seg.left + "px;" + skinCss + "'" +
">" +
"<div class='fc-event-head'" + skinCssAttr + ">" +
"<div class='fc-event-inner fc-event-skin'" + skinCssAttr + ">" +
"<div class='fc-event-head fc-event-skin'" + skinCssAttr + ">" +
"<div class='fc-event-time'>" +
htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
"</div>" +
@ -298,45 +311,38 @@ function AgendaEventRenderer() {
"</div>" +
"</div>" +
"<div class='fc-event-bg'></div>" +
"</a>" +
((seg.isEnd &&
isEventEditable(event) &&
!opt('disableResizing') && // TODO: make like other source properties
$.fn.resizable)
?
"<div class='ui-resizable-handle ui-resizable-s'>=</div>"
:
''
) +
"</div>";
"</div>"; // close inner
if (seg.isEnd && isEventResizable(event)) {
html +=
"<div class='ui-resizable-handle ui-resizable-s'>=</div>";
}
html +=
"</" + (url ? "a" : "div") + ">";
return html;
}
function bindDaySeg(event, eventElement, seg) {
eventElementHandlers(event, eventElement);
if (isEventEditable(event)) {
if (isEventDraggable(event)) {
draggableDayEvent(event, eventElement, seg.isStart);
if (seg.isEnd) {
}
if (seg.isEnd && isEventResizable(event)) {
resizableDayEvent(event, eventElement, seg);
}
}
eventElementHandlers(event, eventElement);
// needs to be after, because resizableDayEvent might stopImmediatePropagation on click
}
function bindSlotSeg(event, eventElement, seg) {
eventElementHandlers(event, eventElement);
if (isEventEditable(event)) {
var timeElement = eventElement.find('span.fc-event-time');
if (isEventDraggable(event)) {
draggableSlotEvent(event, eventElement, timeElement);
if (seg.isEnd) {
}
if (seg.isEnd && isEventResizable(event)) {
resizableSlotEvent(event, eventElement, timeElement);
}
}
}
function isEventEditable(event) {
return firstDefined(event.editable, event.source.editable, opt('editable'));
eventElementHandlers(event, eventElement);
}
@ -347,12 +353,7 @@ function AgendaEventRenderer() {
// when event starts out FULL-DAY
// TODO: bug when dragging an event that occupies first day, but is not the event's start (no rounded left side)
// TODO: bug when dragging from day to slot, outer container doesn't seem to change height
function draggableDayEvent(event, eventElement, isStart) {
if (!opt('disableDragging') && eventElement.draggable) {
var origWidth;
var revert;
var allDay=true;
@ -373,6 +374,7 @@ function AgendaEventRenderer() {
hoverListener.start(function(cell, origCell, rowDelta, colDelta) {
clearOverlays();
if (cell) {
//setOverflowHidden(true);
revert = false;
dayDelta = colDelta * dis;
if (!cell.row) {
@ -404,6 +406,8 @@ function AgendaEventRenderer() {
}
revert = revert || (allDay && !dayDelta);
}else{
resetElement();
//setOverflowHidden(false);
revert = true;
}
eventElement.draggable('option', 'revert', revert);
@ -422,7 +426,6 @@ function AgendaEventRenderer() {
showEvents(event, eventElement);
}else{
// changed!
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
var minuteDelta = 0;
if (!allDay) {
minuteDelta = Math.round((eventElement.offset().top - getBodyContent().offset().top) / slotHeight)
@ -432,6 +435,7 @@ function AgendaEventRenderer() {
}
eventDrop(this, event, dayDelta, minuteDelta, allDay, ev, ui);
}
//setOverflowHidden(false);
}
});
function resetElement() {
@ -444,13 +448,11 @@ function AgendaEventRenderer() {
}
}
}
}
// when event starts out IN TIMESLOTS
function draggableSlotEvent(event, eventElement, timeElement) {
if (!opt('disableDragging') && eventElement.draggable) {
var origPosition;
var allDay=false;
var dayDelta;
@ -548,7 +550,6 @@ function AgendaEventRenderer() {
}
}
}
}
@ -557,7 +558,6 @@ function AgendaEventRenderer() {
function resizableSlotEvent(event, eventElement, timeElement) {
if (!opt('disableResizing') && eventElement.resizable) {
var slotDelta, prevSlotDelta;
var slotHeight = getSlotHeight();
eventElement.resizable({
@ -601,7 +601,6 @@ function AgendaEventRenderer() {
}
});
}
}
}

View file

@ -695,6 +695,8 @@ function AgendaView(element, calendar, viewName) {
.appendTo(slotContent);
}
}else{
rect.isStart = true; // conside rect a "seg" now
rect.isEnd = true; //
selectionHelper = $(slotSegHtml(
{
title: '',
@ -704,11 +706,10 @@ function AgendaView(element, calendar, viewName) {
editable: false,
source: {}
},
rect,
['fc-event', 'fc-event-vert', 'fc-corner-top', 'fc-corner-bottom']
rect
));
if ($.browser.msie) {
selectionHelper.find('span.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
selectionHelper.find('div.fc-event-bg').hide(); // nested opacities mess up in IE, just hide
}
selectionHelper.css('opacity', opt('dragOpacity'));
}

View file

@ -14,6 +14,9 @@ function BasicEventRenderer() {
DayEventRenderer.call(t);
var opt = t.opt;
var trigger = t.trigger;
//var setOverflowHidden = t.setOverflowHidden;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var reportEvents = t.reportEvents;
var reportEventClear = t.reportEventClear;
var eventElementHandlers = t.eventElementHandlers;
@ -76,13 +79,14 @@ function BasicEventRenderer() {
function bindDaySeg(event, eventElement, seg) {
eventElementHandlers(event, eventElement);
if (firstDefined(event.editable, event.source.editable, opt('editable'))) {
if (isEventDraggable(event)) {
draggableDayEvent(event, eventElement);
if (seg.isEnd) {
}
if (seg.isEnd && isEventResizable(event)) {
resizableDayEvent(event, eventElement, seg);
}
}
eventElementHandlers(event, eventElement);
// needs to be after, because resizableDayEvent might stopImmediatePropagation on click
}
@ -92,7 +96,6 @@ function BasicEventRenderer() {
function draggableDayEvent(event, eventElement) {
if (!opt('disableDragging') && eventElement.draggable) {
var hoverListener = getHoverListener();
var dayDelta;
eventElement.draggable({
@ -107,12 +110,14 @@ function BasicEventRenderer() {
eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
clearOverlays();
if (cell) {
//setOverflowHidden(true);
dayDelta = rowDelta*7 + colDelta * (opt('isRTL') ? -1 : 1);
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
);
}else{
//setOverflowHidden(false);
dayDelta = 0;
}
}, ev, 'drag');
@ -122,7 +127,6 @@ function BasicEventRenderer() {
clearOverlays();
trigger('eventDragStop', eventElement, event, ev, ui);
if (dayDelta) {
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
}else{
if ($.browser.msie) {
@ -130,10 +134,10 @@ function BasicEventRenderer() {
}
showEvents(event, eventElement);
}
//setOverflowHidden(false);
}
});
}
}
}

View file

@ -11,6 +11,8 @@ function DayEventRenderer() {
// imports
var opt = t.opt;
var trigger = t.trigger;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var eventEnd = t.eventEnd;
var reportEventElement = t.reportEventElement;
var showEvents = t.showEvents;
@ -119,6 +121,7 @@ function DayEventRenderer() {
var segCnt=segs.length;
var seg;
var event;
var url;
var classes;
var bounds = allDayBounds();
var minLeft = bounds.left;
@ -132,7 +135,10 @@ function DayEventRenderer() {
for (i=0; i<segCnt; i++) {
seg = segs[i];
event = seg.event;
classes = ['fc-event', 'fc-event-hori'];
classes = ['fc-event', 'fc-event-skin', 'fc-event-hori'];
if (isEventDraggable(event)) {
classes.push('fc-event-draggable');
}
if (rtl) {
if (seg.isStart) {
classes.push('fc-corner-right');
@ -157,34 +163,39 @@ function DayEventRenderer() {
right = seg.isEnd ? colContentRight(cols[1]) : maxLeft;
}
classes = classes.concat(event.className, event.source.className);
url = event.url;
skinCss = getSkinCss(event, opt);
if (url) {
html += "<a href='" + htmlEscape(url) + "'";
}else{
html += "<div";
}
html +=
"<div class='" + classes.join(' ') + "' " +
"style='position:absolute;z-index:8;left:"+left+"px;" + skinCss + "'" +
" class='" + classes.join(' ') + "'" +
" style='position:absolute;z-index:8;left:"+left+"px;" + skinCss + "'" +
">" +
"<a class='fc-event-inner'" +
(event.url ? " href='" + htmlEscape(event.url) + "'" : '') +
"<div" +
" class='fc-event-inner fc-event-skin'" +
(skinCss ? " style='" + skinCss + "'" : '') +
">" +
(!event.allDay && seg.isStart ?
">";
if (!event.allDay && seg.isStart) {
html +=
"<span class='fc-event-time'>" +
htmlEscape(formatDates(event.start, event.end, opt('timeFormat'))) +
"</span>"
:'') +
"</span>";
}
html +=
"<span class='fc-event-title'>" + htmlEscape(event.title) + "</span>" +
"</a>" +
((seg.isEnd &&
firstDefined(event.editable, event.source.editable, opt('editable')) &&
!opt('disableResizing')) // TODO: make this like the other source options
?
"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>"
:
''
) +
"</div>";
if (seg.isEnd && isEventResizable(event)) {
html +=
"<div class='ui-resizable-handle ui-resizable-" + (rtl ? 'w' : 'e') + "'></div>";
}
html +=
"</" + (url ? "a" : "div" ) + ">";
seg.left = left;
seg.outerWidth = right - left;
cols.sort(cmp);
cols.sort(cmp); // is this still needed now that cols are always left-to-right?
seg.startCol = cols[0];
seg.endCol = cols[1] + 1;
}
@ -368,14 +379,30 @@ function DayEventRenderer() {
function resizableDayEvent(event, element, seg) {
if (!opt('disableResizing') && seg.isEnd) {
var rtl = opt('isRTL');
var direction = rtl ? 'w' : 'e';
var handle = element.find('div.ui-resizable-' + direction);
var isResizing = false;
// TODO: look into using jquery-ui mouse widget for this stuff
disableTextSelection(element); // prevent native <a> selection for IE
element
.mousedown(function(ev) { // prevent native <a> selection for others
ev.preventDefault();
})
.click(function(ev) {
if (isResizing) {
ev.preventDefault(); // prevent link from being visited (only method that worked in IE6)
ev.stopImmediatePropagation(); // prevent fullcalendar eventClick handler from being called
// (eventElementHandlers needs to be bound after resizableDayEvent)
}
});
handle.mousedown(function(ev) {
if (ev.which != 1) {
return; // needs to be left mouse button
}
isResizing = true;
var hoverListener = t.getHoverListener();
var rowCnt = getRowCnt();
var colCnt = getColCnt();
@ -427,9 +454,10 @@ function DayEventRenderer() {
renderDayOverlay(event.start, addDays(cloneDate(newEnd), 1)); // coordinate grid already rebuild at hoverListener.start
}
}, ev);
function mouseup(ev) {
trigger('eventResizeStop', this, event, ev);
$('body').css('cursor', 'auto');
$('body').css('cursor', '');
hoverListener.stop();
clearOverlays();
if (dayDelta) {
@ -437,10 +465,14 @@ function DayEventRenderer() {
// event redraw will clear helpers
}
// otherwise, the drag handler already restored the old events
setTimeout(function() { // make this happen after the element's click event
isResizing = false;
},0);
}
});
}
}
}

View file

@ -10,6 +10,9 @@ function View(element, calendar, viewName) {
t.name = viewName;
t.opt = opt;
t.trigger = trigger;
//t.setOverflowHidden = setOverflowHidden;
t.isEventDraggable = isEventDraggable;
t.isEventResizable = isEventResizable;
t.reportEvents = reportEvents;
t.eventEnd = eventEnd;
t.reportEventElement = reportEventElement;
@ -55,6 +58,28 @@ function View(element, calendar, viewName) {
}
/*
function setOverflowHidden(bool) {
element.css('overflow', bool ? 'hidden' : '');
}
*/
function isEventDraggable(event) {
return isEventEditable(event) && !opt('disableDragging');
}
function isEventResizable(event) { // but also need to make sure the seg.isEnd == true
return isEventEditable(event) && !opt('disableResizing');
}
function isEventEditable(event) {
return firstDefined(event.editable, event.source.editable, opt('editable'));
}
/* Event Data
------------------------------------------------------------------------------*/

View file

@ -134,33 +134,38 @@
/* Global Event Styles
------------------------------------------------------------------------*/
.fc-event, /* TODO: change this to a fc-event-skin class (and change print.css) */
.fc-event-inner,
.fc-event-head {
border-color: #36c; /* default BORDER color */
background-color: #36c; /* default BACKGROUND color */
color: #fff; /* default TEXT color */
}
.fc-event {
border-style: solid;
border-width: 0;
font-size: .85em;
cursor: default;
}
a.fc-event,
.fc-event-draggable {
cursor: pointer;
}
a.fc-event {
text-decoration: none;
}
.fc-rtl .fc-event {
text-align: right;
}
.fc-event-skin {
border-color: #36c; /* default BORDER color */
background-color: #36c; /* default BACKGROUND color */
color: #fff; /* default TEXT color */
}
.fc-event-inner {
position: relative;
display: block; /* might be an <a> tag */
width: 100%;
height: 100%;
border-style: solid;
border-width: 0;
text-decoration: none;
cursor: pointer;
}
.fc-event-time,

View file

@ -13,9 +13,7 @@
/* Events
-----------------------------------------------------*/
.fc-event,
.fc-event-inner,
.fc-event-head {
.fc-event-skin {
background: none !important;
color: #000 !important;
}

View file

@ -110,6 +110,7 @@ html .fc,
.fc-view {
width: 100%; /* needed for view switching (when view is absolute) */
overflow: hidden;
}

View file

@ -56,8 +56,11 @@ $(document).ready(function() {
right: 'month,agendaWeek,basicWeek,agendaDay,basicDay'
},
editable: true,
//disableResizing: true,
//disableDragging: true,
selectable: true,
selectHelper: true,
dragOpacity: .5,
eventSources: [
{
@ -67,6 +70,7 @@ $(document).ready(function() {
success: function(events) {
console.log('successfully loaded gcal event data!', events);
},
editable: true
},
/*
@ -143,7 +147,13 @@ $(document).ready(function() {
]
}
]
],
eventClick: function(event) {
if (event.url) {
window.open(event.url);
}
return false;
}
});
});