fullcalendar/src/view.js

265 lines
5.7 KiB
JavaScript
Executable File

/* Methods & Utilities for All Views
-----------------------------------------------------------------------------*/
var viewMethods = {
/*
* Objects inheriting these methods must implement the following properties/methods:
* - title
* - start
* - end
* - visStart
* - visEnd
* - defaultEventEnd(event)
* - visEventEnd(event)
* - render(events)
* - rerenderEvents()
*
*
* z-index reservations:
* 1. day-overlay
* 2. events
* 3. dragging/resizing events
*
*/
init: function(element, options) {
this.element = element;
this.options = options;
this.cachedEvents = [];
this.eventsByID = {};
this.eventElements = [];
this.eventElementsByID = {};
},
// triggers an event handler, always append view as last arg
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) {
return event.end || this.defaultEventEnd(event);
},
// report when view receives new events
reportEvents: function(events) { // events are already normalized at this point
var i, len=events.length, event,
eventsByID = this.eventsByID = {},
cachedEvents = this.cachedEvents = [];
for (i=0; i<len; i++) {
event = events[i];
if (eventsByID[event._id]) {
eventsByID[event._id].push(event);
}else{
eventsByID[event._id] = [event];
}
cachedEvents.push(event);
}
},
// report when view creates an element for an event
reportEventElement: function(event, element) {
this.eventElements.push(element);
var eventElementsByID = this.eventElementsByID;
if (eventElementsByID[event._id]) {
eventElementsByID[event._id].push(element);
}else{
eventElementsByID[event._id] = [element];
}
},
// event element manipulation
clearEvents: function() { // only remove ELEMENTS
$.each(this.eventElements, function() {
this.remove();
});
this.eventElements = [];
this.eventElementsByID = {};
},
showEvents: function(event, exceptElement) {
this._eee(event, exceptElement, 'show');
},
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;
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
minutes = minutes || 0;
var events = this.eventsByID[event._id],
i, len=events.length, e;
for (i=0; i<len; i++) {
e = events[i];
e.allDay = event.allDay;
addMinutes(addDays(e.start, days, true), minutes);
if (e.end) {
e.end = addMinutes(addDays(e.end, days, true), minutes);
}
normalizeEvent(e, this.options);
}
this.eventsChanged = true;
},
resizeEvent: function(event, days, minutes) { // actually DO the date changes
minutes = minutes || 0;
var events = this.eventsByID[event._id],
i, len=events.length, e;
for (i=0; i<len; i++) {
e = events[i];
e.end = addMinutes(addDays(this.eventEnd(e), days, true), minutes);
normalizeEvent(e, this.options);
}
this.eventsChanged = true;
},
// semi-transparent overlay (while dragging)
showOverlay: function(props) {
if (!this.dayOverlay) {
this.dayOverlay = $("<div class='fc-cell-overlay' style='position:absolute;z-index:1;display:none'/>")
.appendTo(this.element);
}
var o = this.element.offset();
this.dayOverlay
.css({
top: props.top - o.top,
left: props.left - o.left,
width: props.width,
height: props.height
})
.show();
},
hideOverlay: function() {
if (this.dayOverlay) {
this.dayOverlay.hide();
}
},
// event rendering utilities
sliceSegs: function(events, start, end) {
var segs = [],
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);
}
};
// more event rendering 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;
}