265 lines
5.7 KiB
JavaScript
Executable File
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;
|
|
}
|
|
|