getting closer to 1.4

This commit is contained in:
Adam Shaw 2009-10-10 08:12:40 +00:00
parent 8e0312a750
commit 20208deb66
8 changed files with 870 additions and 786 deletions

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,6 @@
.fc .fc-axis { .fc .fc-axis {
width: 50px; width: 50px;
height: 1.6em;
padding: 0 4px 0 0; padding: 0 4px 0 0;
vertical-align: middle; vertical-align: middle;
white-space: nowrap; white-space: nowrap;
@ -28,7 +27,7 @@
} }
.fc-agenda-head tr.fc-all-day th { .fc-agenda-head tr.fc-all-day th {
height: 2em; height: 35px;
} }
.fc-agenda tr.fc-first th, .fc-agenda tr.fc-first th,
@ -47,6 +46,10 @@
.fc .fc-agenda-body td { .fc .fc-agenda-body td {
background: none; background: none;
} }
.fc .fc-agenda-body td div {
height: 20px;
}
@ -57,15 +60,34 @@
} }
.fc .fc-divider th { .fc .fc-divider div {
height: 3px; font-size: 1px; /* for IE6/7 */
border-bottom-width: 1px; height: 2px;
} }
.fc .fc-divider .fc-state-default { .fc .fc-divider .fc-state-default {
background: #eee; background: #eee;
} }
.fc-agenda-head tr.fc-last th {
border-bottom-width: 1px;
}
.fc-agenda .fc-day-content {
padding: 2px 2px 0;
}
.fc-agenda-head .fc-day-content {
padding-bottom: 10px;
}
@ -205,6 +227,7 @@
_white-space: normal; _white-space: normal;
overflow: hidden; overflow: hidden;
font-size: 10px; font-size: 10px;
border: 0;
} }
.fc-event-vert span.fc-event-title { .fc-event-vert span.fc-event-title {

View file

@ -224,6 +224,7 @@ table.fc-header {
------------------------------------------------------------------------*/ ------------------------------------------------------------------------*/
.fc-event, .fc-event,
.fc-agenda .fc-event-time,
.fc-event a { .fc-event a {
border-style: solid; border-style: solid;
border-color: #36c; /* default BORDER color (probably the same as background-color) */ border-color: #36c; /* default BORDER color (probably the same as background-color) */

View file

@ -18,7 +18,7 @@ views.month = function(element, options) {
this.title = formatDates( this.title = formatDates(
start, start,
addDays(cloneDate(this.end = addMonths(cloneDate(start), 1)), -1), addDays(cloneDate(this.end = addMonths(cloneDate(start), 1)), -1),
strProp(options.titleFormat, 'month'), this.option('titleFormat'),
options options
); );
addDays(this.visStart = cloneDate(start), -((start.getDay() - options.firstDay + 7) % 7)); addDays(this.visStart = cloneDate(start), -((start.getDay() - options.firstDay + 7) % 7));
@ -28,7 +28,7 @@ views.month = function(element, options) {
addDays(this.visEnd, (6 - rowCnt) * 7); addDays(this.visEnd, (6 - rowCnt) * 7);
rowCnt = 6; rowCnt = 6;
} }
this.renderGrid(rowCnt, 7, strProp(options.columnFormat, 'month'), true, fetchEvents); this.renderGrid(rowCnt, 7, this.option('columnFormat'), true, fetchEvents);
} }
}); });
} }
@ -42,10 +42,10 @@ views.basicWeek = function(element, options) {
this.title = formatDates( this.title = formatDates(
this.start = this.visStart = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)), this.start = this.visStart = addDays(cloneDate(date), -((date.getDay() - options.firstDay + 7) % 7)),
addDays(cloneDate(this.end = this.visEnd = addDays(cloneDate(this.start), 7)), -1), addDays(cloneDate(this.end = this.visEnd = addDays(cloneDate(this.start), 7)), -1),
strProp(options.titleFormat, 'week'), this.option('titleFormat'),
options options
); );
this.renderGrid(1, 7, strProp(options.columnFormat, 'week'), false, fetchEvents); this.renderGrid(1, 7, this.option('columnFormat'), false, fetchEvents);
} }
}); });
}; };
@ -56,10 +56,10 @@ views.basicDay = function(element, options) {
if (delta) { if (delta) {
addDays(date, delta); addDays(date, delta);
} }
this.title = formatDate(date, strProp(options.titleFormat, 'day'), options); this.title = formatDate(date, this.option('titleFormat'), options);
this.start = this.visStart = cloneDate(date, true); this.start = this.visStart = cloneDate(date, true);
this.end = this.visEnd = addDays(cloneDate(this.start), 1); this.end = this.visEnd = addDays(cloneDate(this.start), 1);
this.renderGrid(1, 1, strProp(options.columnFormat, 'day'), false, fetchEvents); this.renderGrid(1, 1, this.option('columnFormat'), false, fetchEvents);
} }
}); });
} }
@ -326,10 +326,11 @@ function Grid(element, options, methods) {
function compileSegs(events) { function compileSegs(events) {
var d1 = cloneDate(view.visStart); var d1 = cloneDate(view.visStart),
var d2 = addDays(cloneDate(d1), colCnt); d2 = addDays(cloneDate(d1), colCnt),
var rows = []; rows = [],
for (var i=0; i<rowCnt; i++) { i=0;
for (; i<rowCnt; i++) {
rows.push(stackSegs(view.sliceSegs(events, d1, d2))); rows.push(stackSegs(view.sliceSegs(events, d1, d2)));
addDays(d1, 7); addDays(d1, 7);
addDays(d2, 7); addDays(d2, 7);
@ -350,7 +351,7 @@ function Grid(element, options, methods) {
event, event,
eventClasses, eventClasses,
startElm, endElm, startElm, endElm,
left1, left2, left, right,
eventElement, eventAnchor, eventElement, eventAnchor,
triggerRes; triggerRes;
for (i=0; i<len; i++) { for (i=0; i<len; i++) {
@ -387,14 +388,14 @@ function Grid(element, options, methods) {
} }
eventClasses.push('fc-event', 'fc-event-hori'); eventClasses.push('fc-event', 'fc-event-hori');
startElm = seg.isStart ? startElm = seg.isStart ?
tr.find('td:eq('+((seg.start.getDay()-firstDay+colCnt)%colCnt)+') div.fc-day-content div') : tr.find('td:eq('+((seg.start.getDay()-firstDay+colCnt)%colCnt)+') div div') :
tbody; tbody;
endElm = seg.isEnd ? endElm = seg.isEnd ?
tr.find('td:eq('+((seg.end.getDay()-firstDay+colCnt-1)%colCnt)+') div.fc-day-content div') : tr.find('td:eq('+((seg.end.getDay()-firstDay+colCnt-1)%colCnt)+') div div') :
tbody; tbody;
if (rtl) { if (rtl) {
left1 = endElm.position().left; left = endElm.position().left;
left2 = startElm.position().left + startElm.width(); right = startElm.position().left + startElm.width();
if (seg.isStart) { if (seg.isStart) {
eventClasses.push('fc-corner-right'); eventClasses.push('fc-corner-right');
} }
@ -402,8 +403,8 @@ function Grid(element, options, methods) {
eventClasses.push('fc-corner-left'); eventClasses.push('fc-corner-left');
} }
}else{ }else{
left1 = startElm.position().left; left = startElm.position().left;
left2 = endElm.position().left + endElm.width(); right = endElm.position().left + endElm.width();
if (seg.isStart) { if (seg.isStart) {
eventClasses.push('fc-corner-left'); eventClasses.push('fc-corner-left');
} }
@ -415,7 +416,7 @@ function Grid(element, options, methods) {
.append(eventAnchor = $("<a/>") .append(eventAnchor = $("<a/>")
.append(event.allDay || !seg.isStart ? null : .append(event.allDay || !seg.isStart ? null :
$("<span class='fc-event-time'/>") $("<span class='fc-event-time'/>")
.html(formatDates(event.start, event.end, options.timeFormat, options))) .html(formatDates(event.start, event.end, view.option('timeFormat'), options)))
.append($("<span class='fc-event-title'/>") .append($("<span class='fc-event-title'/>")
.text(event.title))); .text(event.title)));
if (event.url) { if (event.url) {
@ -430,23 +431,23 @@ function Grid(element, options, methods) {
.css({ .css({
position: 'absolute', position: 'absolute',
top: top, top: top,
left: left1 + (rtlLeftDiff||0), left: left + (rtlLeftDiff||0),
zIndex: 2 zIndex: 8
}) })
.appendTo(element); .appendTo(element);
setOuterWidth(eventElement, left2-left1, true); setOuterWidth(eventElement, right-left, true);
if (rtl && rtlLeftDiff == undefined) { if (rtl && rtlLeftDiff == undefined) {
// bug in IE6 where offsets are miscalculated with direction:rtl // bug in IE6 where offsets are miscalculated with direction:rtl
rtlLeftDiff = left1 - eventElement.position().left; rtlLeftDiff = left - eventElement.position().left;
if (rtlLeftDiff) { if (rtlLeftDiff) {
eventElement.css('left', left1 + rtlLeftDiff); eventElement.css('left', left + rtlLeftDiff);
} }
} }
eventElementHandlers(event, eventElement); eventElementHandlers(event, eventElement);
if (event.editable || event.editable == undefined && options.editable) { if (event.editable || event.editable == undefined && options.editable) {
draggableEvent(event, eventElement); draggableEvent(event, eventElement);
if (seg.isEnd) { if (seg.isEnd) {
resizableEvent(event, eventElement); view.resizableDayEvent(event, eventElement, colWidth);
} }
} }
view.reportEventElement(event, eventElement); view.reportEventElement(event, eventElement);
@ -479,7 +480,7 @@ function Grid(element, options, methods) {
/* Draggable /* Event Dragging
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
@ -487,9 +488,9 @@ function Grid(element, options, methods) {
if (!options.disableDragging && eventElement.draggable) { if (!options.disableDragging && eventElement.draggable) {
var matrix; var matrix;
eventElement.draggable({ eventElement.draggable({
zIndex: 3, zIndex: 9,
delay: 50, delay: 50,
opacity: options.dragOpacity, opacity: view.option('dragOpacity'),
revertDuration: options.dragRevertDuration, revertDuration: options.dragRevertDuration,
start: function(ev, ui) { start: function(ev, ui) {
matrix = new HoverMatrix(function(cell) { matrix = new HoverMatrix(function(cell) {
@ -518,20 +519,17 @@ function Grid(element, options, methods) {
matrix.mouse(ev.pageX, ev.pageY); matrix.mouse(ev.pageX, ev.pageY);
}, },
stop: function(ev, ui) { stop: function(ev, ui) {
if ($.browser.msie) {
eventElement.css('filter', ''); // clear IE opacity side-effects
}
view.hideOverlay(); view.hideOverlay();
view.trigger('eventDragStop', eventElement, event, ev, ui); view.trigger('eventDragStop', eventElement, event, ev, ui);
var cell = matrix.cell; var cell = matrix.cell;
if (!cell || !cell.rowDelta && !cell.colDelta) { if (!cell || !cell.rowDelta && !cell.colDelta) {
view.showEvents(event, eventElement); view.showEvents(event, eventElement);
}else{ }else{
var dayDelta = cell.rowDelta*7 + cell.colDelta*dis;
view.moveEvent(event, dayDelta);
view.trigger('eventDrop', this, event, dayDelta, 0, function() {
view.moveEvent(event, -dayDelta);
rerenderEvents();
}, ev, ui);
eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link eventElement.find('a').removeAttr('href'); // prevents safari from visiting the link
rerenderEvents(); view.eventDrop(this, event, cell.rowDelta*7+cell.colDelta*dis, 0, event.allDay, ev, ui);
} }
} }
}); });
@ -539,42 +537,7 @@ function Grid(element, options, methods) {
} }
// event resizing w/ 'view' methods...
/* Resizable
-----------------------------------------------------------------------------*/
function resizableEvent(event, eventElement) {
if (!options.disableResizing && eventElement.resizable) {
eventElement.resizable({
handles: rtl ? 'w' : 'e',
grid: colWidth,
minWidth: colWidth/2, // need this or else IE throws errors when too small
containment: element,
start: function(ev, ui) {
eventElement.css('z-index', 3);
view.hideEvents(event, eventElement);
view.trigger('eventResizeStart', this, event, ev, ui);
},
stop: function(ev, ui) {
view.trigger('eventResizeStop', this, event, ev, ui);
// ui.size.width wasn't working with grid correctly, use .width()
var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth);
if (dayDelta) {
view.resizeEvent(event, dayDelta);
view.trigger('eventResize', this, event, dayDelta, 0, function() {
view.resizeEvent(event, -dayDelta);
rerenderEvents();
}, ev, ui);
rerenderEvents();
}else{
view.showEvents(event, eventElement);
}
eventElement.css('z-index', 2);
}
});
}
}
}; };

View file

@ -30,7 +30,6 @@ var defaults = {
cacheParam: '_', cacheParam: '_',
// time formats // time formats
timeFormat: 'h(:mm)t', // for events
titleFormat: { titleFormat: {
month: 'MMMM yyyy', month: 'MMMM yyyy',
week: "MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}", week: "MMM d[ yyyy]{ '&#8212;'[ MMM] d yyyy}",
@ -41,6 +40,9 @@ var defaults = {
week: 'ddd M/d', week: 'ddd M/d',
day: 'dddd M/d' day: 'dddd M/d'
}, },
timeFormat: { // for event elements
'': 'h(:mm)t' // default
},
// locale // locale
isRTL: false, isRTL: false,
@ -166,7 +168,7 @@ $.fn.fullCalendar = function(options) {
function changeView(v) { function changeView(v) {
if (v != viewName) { if (v != viewName) {
lockContentSize(); fixContentSize();
if (view) { if (view) {
if (view.eventsChanged) { if (view.eventsChanged) {
eventsDirtyExcept(view); eventsDirtyExcept(view);
@ -188,14 +190,14 @@ $.fn.fullCalendar = function(options) {
} }
view.name = viewName = v; view.name = viewName = v;
render(); render();
unlockContentSize(); unfixContentSize();
} }
} }
function render(inc) { function render(inc) {
if (_element.offsetWidth !== 0) { // visible on the screen if (_element.offsetWidth !== 0) { // visible on the screen
if (inc || !view.date || +view.date != +date) { // !view.date means it hasn't been rendered yet if (inc || !view.date || +view.date != +date) { // !view.date means it hasn't been rendered yet
ignoreWindowResizes = true; fixContentSize();
view.render(date, inc || 0, function(callback) { view.render(date, inc || 0, function(callback) {
// dont refetch if new view contains the same events (or a subset) // dont refetch if new view contains the same events (or a subset)
if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) { if (!eventStart || view.visStart < eventStart || view.visEnd > eventEnd) {
@ -204,7 +206,7 @@ $.fn.fullCalendar = function(options) {
callback(events); // no refetching callback(events); // no refetching
} }
}); });
ignoreWindowResizes = false; unfixContentSize();
view.date = cloneDate(date); view.date = cloneDate(date);
if (header) { if (header) {
// enable/disable 'today' button // enable/disable 'today' button
@ -620,35 +622,47 @@ $.fn.fullCalendar = function(options) {
/* Resizing /* Resizing
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
function lockContentSize() {
content.css({
overflow: 'hidden',
height: Math.round(content.width() / options.aspectRatio)
});
}
function unlockContentSize() {
content.css({
overflow: '',
height: ($.browser.msie && $.browser.version == '6.0') ? 1 : ''
});
}
var elementWidth, var elementWidth,
ignoreWindowResizes = false, contentSizeFixed = false,
resizeCnt = 0; resizeCnt = 0;
function fixContentSize() {
if (!contentSizeFixed) {
contentSizeFixed = true;
content.css({
overflow: 'hidden',
height: Math.round(content.width() / options.aspectRatio)
});
}
}
function unfixContentSize() {
if (contentSizeFixed) {
content.css({
overflow: 'visible',
height: ''
});
if ($.browser.msie && ($.browser.version=='6.0' || $.browser.version=='7.0')) {
// in IE6/7 the inside of the content div was invisible
// bizarre hack to get this work... need both lines
content[0].clientHeight;
content.hide().show();
}
contentSizeFixed = false;
}
}
$(window).resize(function() { $(window).resize(function() {
if (!ignoreWindowResizes && view.date) { // view.date means the view has been rendered if (!contentSizeFixed && view.date) { // view.date means the view has been rendered
var rcnt = ++resizeCnt; // add a delay var rcnt = ++resizeCnt; // add a delay
setTimeout(function() { setTimeout(function() {
if (rcnt == resizeCnt && !ignoreWindowResizes) { if (rcnt == resizeCnt && !contentSizeFixed) {
var newWidth = element.width(); var newWidth = element.width();
if (newWidth != elementWidth) { if (newWidth != elementWidth) {
elementWidth = newWidth; elementWidth = newWidth;
lockContentSize(); fixContentSize();
view.updateSize(); view.updateSize();
unlockContentSize(); unfixContentSize();
view.rerenderEvents(true); view.rerenderEvents(true);
sizesDirtyExcept(view); sizesDirtyExcept(view);
view.trigger('windowResize', _element); view.trigger('windowResize', _element);
@ -686,7 +700,7 @@ function normalizeEvent(event, options) {
} }
event._start = cloneDate(event.start = parseDate(event.start)); event._start = cloneDate(event.start = parseDate(event.start));
event.end = parseDate(event.end); event.end = parseDate(event.end);
if (event.end && event.end < event.start) { if (event.end && event.end <= event.start) {
event.end = null; event.end = null;
} }
event._end = event.end ? cloneDate(event.end) : null; event._end = event.end ? cloneDate(event.end) : null;

View file

@ -3,7 +3,8 @@
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var DAY_MS = 86400000, var DAY_MS = 86400000,
HOUR_MS = 3600000; HOUR_MS = 3600000,
MINUTE_MS = 60000;
function addYears(d, n, keepTime) { function addYears(d, n, keepTime) {
d.setFullYear(d.getFullYear() + n); d.setFullYear(d.getFullYear() + n);
@ -340,7 +341,3 @@ function zeroPad(n) {
return (n < 10 ? '0' : '') + n; return (n < 10 ? '0' : '') + n;
} }
function strProp(s, prop) {
return typeof s == 'string' ? s : s[prop];
}

View file

@ -1,264 +1,346 @@
/* Methods & Utilities for All Views /* Methods & Utilities for All Views
-----------------------------------------------------------------------------*/ -----------------------------------------------------------------------------*/
var viewMethods = { var viewMethods = {
/* // TODO: maybe change the 'vis' variables to 'excl'
* Objects inheriting these methods must implement the following properties/methods:
* - title /*
* - start * Objects inheriting these methods must implement the following properties/methods:
* - end * - title
* - visStart * - start
* - visEnd * - end
* - defaultEventEnd(event) * - visStart
* - visEventEnd(event) * - visEnd
* - render(events) * - defaultEventEnd(event)
* - rerenderEvents() * - visEventEnd(event)
* * - render(events)
* * - rerenderEvents()
* z-index reservations: *
* 1. day-overlay *
* 2. events * z-index reservations:
* 3. dragging/resizing events * 3 - day-overlay
* * 8 - events
*/ * 9 - dragging/resizing events
*
*/
init: function(element, options) {
this.element = element;
this.options = options; init: function(element, options) {
this.cachedEvents = []; this.element = element;
this.eventsByID = {}; this.options = options;
this.eventElements = []; this.cachedEvents = [];
this.eventElementsByID = {}; this.eventsByID = {};
}, this.eventElements = [];
this.eventElementsByID = {};
},
// triggers an event handler, always append view as last arg
trigger: function(name, thisObj) { // triggers an event handler, always append view as last arg
if (this.options[name]) {
return this.options[name].apply(thisObj || this, Array.prototype.slice.call(arguments, 2).concat([this])); trigger: function(name, thisObj) {
} if (this.options[name]) {
}, return this.options[name].apply(thisObj || this, Array.prototype.slice.call(arguments, 2).concat([this]));
}
},
// returns a Date object for an event's end
eventEnd: function(event) { // returns a Date object for an event's end
return event.end || this.defaultEventEnd(event);
}, eventEnd: function(event) {
return event.end ? cloneDate(event.end) : this.defaultEventEnd(event); // TODO: make sure always using copies
},
// report when view receives new events
reportEvents: function(events) { // events are already normalized at this point // report when view receives new events
var i, len=events.length, event,
eventsByID = this.eventsByID = {}, reportEvents: function(events) { // events are already normalized at this point
cachedEvents = this.cachedEvents = []; var i, len=events.length, event,
for (i=0; i<len; i++) { eventsByID = this.eventsByID = {},
event = events[i]; cachedEvents = this.cachedEvents = [];
if (eventsByID[event._id]) { for (i=0; i<len; i++) {
eventsByID[event._id].push(event); event = events[i];
}else{ if (eventsByID[event._id]) {
eventsByID[event._id] = [event]; eventsByID[event._id].push(event);
} }else{
cachedEvents.push(event); eventsByID[event._id] = [event];
} }
}, cachedEvents.push(event);
}
},
// report when view creates an element for an event
reportEventElement: function(event, element) { // report when view creates an element for an event
this.eventElements.push(element);
var eventElementsByID = this.eventElementsByID; reportEventElement: function(event, element) {
if (eventElementsByID[event._id]) { this.eventElements.push(element);
eventElementsByID[event._id].push(element); var eventElementsByID = this.eventElementsByID;
}else{ if (eventElementsByID[event._id]) {
eventElementsByID[event._id] = [element]; eventElementsByID[event._id].push(element);
} }else{
}, eventElementsByID[event._id] = [element];
}
},
// event element manipulation
clearEvents: function() { // only remove ELEMENTS // event element manipulation
$.each(this.eventElements, function() {
this.remove(); clearEvents: function() { // only remove ELEMENTS
}); $.each(this.eventElements, function() {
this.eventElements = []; this.remove();
this.eventElementsByID = {}; });
}, this.eventElements = [];
this.eventElementsByID = {};
showEvents: function(event, exceptElement) { },
this._eee(event, exceptElement, 'show');
}, showEvents: function(event, exceptElement) {
this._eee(event, exceptElement, 'show');
hideEvents: function(event, exceptElement) { },
this._eee(event, exceptElement, 'hide');
}, hideEvents: function(event, exceptElement) {
this._eee(event, exceptElement, 'hide');
_eee: function(event, exceptElement, funcName) { // event-element-each },
var elements = this.eventElementsByID[event._id],
i, len = elements.length; _eee: function(event, exceptElement, funcName) { // event-element-each
for (i=0; i<len; i++) { var elements = this.eventElementsByID[event._id],
if (elements[i] != exceptElement) { i, len = elements.length;
elements[i][funcName](); for (i=0; i<len; i++) {
} if (elements[i] != exceptElement) {
} elements[i][funcName]();
}, }
}
},
// event modification reporting
moveEvent: function(event, days, minutes) { // actually DO the date changes // event modification reporting
minutes = minutes || 0;
var events = this.eventsByID[event._id], eventDrop: function(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
i, len=events.length, e; var view = this,
for (i=0; i<len; i++) { oldAllDay = event.allDay;
e = events[i]; view.moveEvents(view.eventsByID[event._id], dayDelta, minuteDelta, allDay);
e.allDay = event.allDay; view.trigger('eventDrop', e, event, dayDelta, minuteDelta, allDay, function() { // TODO: change docs
addMinutes(addDays(e.start, days, true), minutes); // TODO: investigate cases where this inverse technique might not work
if (e.end) { view.moveEvents(view.eventsByID[event._id], -dayDelta, -minuteDelta, oldAllDay);
e.end = addMinutes(addDays(e.end, days, true), minutes); view.rerenderEvents();
} }, ev, ui);
normalizeEvent(e, this.options); view.eventsChanged = true;
} view.rerenderEvents();
this.eventsChanged = true; },
},
eventResize: function(e, event, dayDelta, minuteDelta, ev, ui) {
resizeEvent: function(event, days, minutes) { // actually DO the date changes var view = this;
minutes = minutes || 0; view.elongateEvents(view.eventsByID[event._id], dayDelta, minuteDelta);
var events = this.eventsByID[event._id], view.trigger('eventResize', e, event, dayDelta, minuteDelta, function() {
i, len=events.length, e; // TODO: investigate cases where this inverse technique might not work
for (i=0; i<len; i++) { view.elongateEvents(view.eventsByID[event._id], -dayDelta, -minuteDelta);
e = events[i]; view.rerenderEvents();
e.end = addMinutes(addDays(this.eventEnd(e), days, true), minutes); }, ev, ui);
normalizeEvent(e, this.options); view.eventsChanged = true;
} view.rerenderEvents();
this.eventsChanged = true; },
},
// event modification
// semi-transparent overlay (while dragging)
moveEvents: function(events, dayDelta, minuteDelta, allDay) {
showOverlay: function(props) { minuteDelta = minuteDelta || 0;
if (!this.dayOverlay) { for (var e, len=events.length, i=0; i<len; i++) {
this.dayOverlay = $("<div class='fc-cell-overlay' style='position:absolute;z-index:1;display:none'/>") e = events[i];
.appendTo(this.element); if (allDay != undefined) {
} e.allDay = allDay;
var o = this.element.offset(); }
this.dayOverlay addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
.css({ if (e.end) {
top: props.top - o.top, e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
left: props.left - o.left, }
width: props.width, normalizeEvent(e, this.options);
height: props.height }
}) },
.show();
}, elongateEvents: function(events, dayDelta, minuteDelta) {
minuteDelta = minuteDelta || 0;
hideOverlay: function() { for (var e, len=events.length, i=0; i<len; i++) {
if (this.dayOverlay) { e = events[i];
this.dayOverlay.hide(); e.end = addMinutes(addDays(this.eventEnd(e), dayDelta, true), minuteDelta);
} normalizeEvent(e, this.options);
}, }
},
// event rendering utilities
// semi-transparent overlay (while dragging)
sliceSegs: function(events, start, end) {
var segs = [], showOverlay: function(props) {
i, len=events.length, event, if (!this.dayOverlay) {
eventStart, eventEnd, this.dayOverlay = $("<div class='fc-cell-overlay' style='position:absolute;z-index:3;display:none'/>")
segStart, segEnd, .appendTo(this.element);
isStart, isEnd; }
for (i=0; i<len; i++) { var o = this.element.offset();
event = events[i]; this.dayOverlay
eventStart = event.start; .css({
eventEnd = this.visEventEnd(event); top: props.top - o.top,
if (eventEnd > start && eventStart < end) { left: props.left - o.left,
if (eventStart < start) { width: props.width,
segStart = cloneDate(start); height: props.height
isStart = false; })
}else{ .show();
segStart = eventStart; },
isStart = true;
} hideOverlay: function() {
if (eventEnd > end) { if (this.dayOverlay) {
segEnd = cloneDate(end); this.dayOverlay.hide();
isEnd = false; }
}else{ },
segEnd = eventEnd;
isEnd = true;
}
segs.push({ // common horizontal event resizing
event: event,
start: segStart, resizableDayEvent: function(event, eventElement, colWidth) {
end: segEnd, var view = this;
isStart: isStart, if (!view.options.disableResizing && eventElement.resizable) {
isEnd: isEnd, eventElement.resizable({
msLength: segEnd - segStart handles: view.options.isRTL ? 'w' : 'e',
}); grid: colWidth,
} minWidth: colWidth/2, // need this or else IE throws errors when too small
} containment: view.element,
return segs.sort(segCmp); start: function(ev, ui) {
} eventElement.css('z-index', 9);
view.hideEvents(event, eventElement);
}; view.trigger('eventResizeStart', this, event, ev, ui);
},
stop: function(ev, ui) {
// more event rendering utilities view.trigger('eventResizeStop', this, event, ev, ui);
// ui.size.width wasn't working with grid correctly, use .width()
function stackSegs(segs) { var dayDelta = Math.round((eventElement.width() - ui.originalSize.width) / colWidth);
var levels = [], if (dayDelta) {
i, len = segs.length, seg, view.eventResize(this, event, dayDelta, 0, ev, ui);
j, collide, k; }else{
for (i=0; i<len; i++) { eventElement.css('z-index', 8);
seg = segs[i]; view.showEvents(event, eventElement);
j = 0; // the level index where seg should belong }
while (true) { }
collide = false; });
if (levels[j]) { }
for (k=0; k<levels[j].length; k++) { },
if (segsCollide(levels[j][k], seg)) {
collide = true;
break;
} // get a property from the 'options' object, using smart view naming
}
} option: function(name) {
if (collide) { var v = this.options[name];
j++; if (typeof v == 'object') {
}else{ var parts = this.name.split(/(?=[A-Z])/),
break; i=parts.length-1, res;
} for (; i>=0; i--) {
} res = v[parts[i].toLowerCase()];
if (levels[j]) { if (res != undefined) {
levels[j].push(seg); return res;
}else{ }
levels[j] = [seg]; }
} return v[''];
//seg.after = 0; }
} return v;
return levels; },
}
function segCmp(a, b) {
return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start); // event rendering utilities
}
sliceSegs: function(events, start, end) {
function segsCollide(seg1, seg2) { var segs = [],
return seg1.end > seg2.start && seg1.start < seg2.end; i, len=events.length, event,
} eventStart, eventEnd,
segStart, segEnd,
isStart, isEnd;
for (i=0; i<len; i++) {
event = events[i];
eventStart = event.start;
eventEnd = this.visEventEnd(event);
if (eventEnd > start && eventStart < end) {
if (eventStart < start) {
segStart = cloneDate(start);
isStart = false;
}else{
segStart = eventStart;
isStart = true;
}
if (eventEnd > end) {
segEnd = cloneDate(end);
isEnd = false;
}else{
segEnd = eventEnd;
isEnd = true;
}
segs.push({
event: event,
start: segStart,
end: segEnd,
isStart: isStart,
isEnd: isEnd,
msLength: segEnd - segStart
});
}
}
return segs.sort(segCmp);
}
};
// event rendering calculation utilities
function stackSegs(segs) {
var levels = [],
i, len = segs.length, seg,
j, collide, k;
for (i=0; i<len; i++) {
seg = segs[i];
j = 0; // the level index where seg should belong
while (true) {
collide = false;
if (levels[j]) {
for (k=0; k<levels[j].length; k++) {
if (segsCollide(levels[j][k], seg)) {
collide = true;
break;
}
}
}
if (collide) {
j++;
}else{
break;
}
}
if (levels[j]) {
levels[j].push(seg);
}else{
levels[j] = [seg];
}
//seg.after = 0;
}
return levels;
}
function segCmp(a, b) {
return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
}
function segsCollide(seg1, seg2) {
return seg1.end > seg2.start && seg1.start < seg2.end;
}

View file

@ -12,10 +12,14 @@
var m = d.getMonth(); var m = d.getMonth();
$('#calendar').fullCalendar({ $('#calendar').fullCalendar({
slotMinutes: 30,
//allDayHeader: false,
//weekMode: 'variable', //weekMode: 'variable',
theme: true, //theme: true,
//firstDay: 1,
//isRTL: true, //isRTL: true,
editable: true, editable: true,
//dragOpacity: .5,
defaultView: 'agendaWeek', defaultView: 'agendaWeek',
header: { header: {
left: 'prev,next today', left: 'prev,next today',
@ -27,20 +31,26 @@
id: 1, id: 1,
title: "Long Event", title: "Long Event",
start: new Date(y, m, 6, 14, 0), start: new Date(y, m, 6, 14, 0),
end: new Date(y, m, 11), end: new Date(y, m, 11)
//allDay: false
},
{
id: 2,
title: "Repeating Event111",
start: new Date(y, m, 8),
allDay: true
},
{
id: 2,
title: "Repeating Event222",
start: new Date(y, m, 9, 5, 0),
allDay: false allDay: false
}, },
{ {
id: 2, id: 345,
title: "Repeating Event", title: "Hey Hey",
start: new Date(y, m, 2), start: new Date(y, m, 9, 4, 0),
allDay: true allDay: false
},
{
id: 2,
title: "Repeating Event",
start: new Date(y, m, 9),
allDay: true
}, },
{ {
id: 3, id: 3,