2010-09-19 07:54:35 +02:00
|
|
|
|
2011-02-11 07:40:16 +01:00
|
|
|
fc.sourceNormalizers = [];
|
|
|
|
fc.sourceFetchers = [];
|
|
|
|
|
|
|
|
var ajaxDefaults = {
|
|
|
|
dataType: 'json',
|
2011-03-20 02:48:20 +01:00
|
|
|
cache: false
|
2011-02-11 07:40:16 +01:00
|
|
|
};
|
|
|
|
|
2010-09-19 07:54:35 +02:00
|
|
|
var eventGUID = 1;
|
|
|
|
|
2011-03-20 02:48:20 +01:00
|
|
|
|
2011-02-11 07:40:16 +01:00
|
|
|
function EventManager(options, _sources) {
|
2010-09-19 07:54:35 +02:00
|
|
|
var t = this;
|
|
|
|
|
|
|
|
|
|
|
|
// exports
|
|
|
|
t.isFetchNeeded = isFetchNeeded;
|
2010-10-24 22:32:08 +02:00
|
|
|
t.fetchEvents = fetchEvents;
|
2010-09-19 07:54:35 +02:00
|
|
|
t.addEventSource = addEventSource;
|
|
|
|
t.removeEventSource = removeEventSource;
|
|
|
|
t.updateEvent = updateEvent;
|
|
|
|
t.renderEvent = renderEvent;
|
|
|
|
t.removeEvents = removeEvents;
|
|
|
|
t.clientEvents = clientEvents;
|
|
|
|
t.normalizeEvent = normalizeEvent;
|
|
|
|
|
|
|
|
|
|
|
|
// imports
|
|
|
|
var trigger = t.trigger;
|
2010-10-24 22:32:08 +02:00
|
|
|
var getView = t.getView;
|
|
|
|
var reportEvents = t.reportEvents;
|
|
|
|
|
2010-09-19 07:54:35 +02:00
|
|
|
|
|
|
|
// locals
|
2011-02-11 07:40:16 +01:00
|
|
|
var stickySource = { events: [] };
|
|
|
|
var sources = [ stickySource ];
|
2010-10-24 22:32:08 +02:00
|
|
|
var rangeStart, rangeEnd;
|
2011-10-24 14:23:31 +02:00
|
|
|
var offsetStart, offsetEnd;
|
2010-10-24 22:32:08 +02:00
|
|
|
var currentFetchID = 0;
|
|
|
|
var pendingSourceCnt = 0;
|
2010-09-19 07:54:35 +02:00
|
|
|
var loadingLevel = 0;
|
2010-10-24 22:32:08 +02:00
|
|
|
var cache = [];
|
2010-09-19 07:54:35 +02:00
|
|
|
|
|
|
|
|
2011-02-11 07:40:16 +01:00
|
|
|
for (var i=0; i<_sources.length; i++) {
|
|
|
|
_addEventSource(_sources[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-19 07:54:35 +02:00
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
/* Fetching
|
2010-09-19 07:54:35 +02:00
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
function isFetchNeeded(start, end) {
|
|
|
|
return !rangeStart || start < rangeStart || end > rangeEnd;
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
function fetchEvents(start, end) {
|
|
|
|
rangeStart = start;
|
|
|
|
rangeEnd = end;
|
2011-10-24 14:23:31 +02:00
|
|
|
offsetStart = start.getTimezoneOffset()*60*1000;
|
|
|
|
offsetEnd = end.getTimezoneOffset()*60*1000;
|
2010-10-24 22:32:08 +02:00
|
|
|
cache = [];
|
2010-11-20 07:45:44 +01:00
|
|
|
var fetchID = ++currentFetchID;
|
|
|
|
var len = sources.length;
|
|
|
|
pendingSourceCnt = len;
|
|
|
|
for (var i=0; i<len; i++) {
|
|
|
|
fetchEventSource(sources[i], fetchID);
|
2010-10-24 22:32:08 +02:00
|
|
|
}
|
2010-10-11 23:16:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
function fetchEventSource(source, fetchID) {
|
|
|
|
_fetchEventSource(source, function(events) {
|
|
|
|
if (fetchID == currentFetchID) {
|
2011-02-11 07:40:16 +01:00
|
|
|
if (events) {
|
|
|
|
for (var i=0; i<events.length; i++) {
|
|
|
|
events[i].source = source;
|
2011-03-20 02:48:20 +01:00
|
|
|
normalizeEvent(events[i]);
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
|
|
|
cache = cache.concat(events);
|
2010-10-11 23:16:34 +02:00
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
pendingSourceCnt--;
|
|
|
|
if (!pendingSourceCnt) {
|
|
|
|
reportEvents(cache);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
2010-10-11 23:16:34 +02:00
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
});
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
function _fetchEventSource(source, callback) {
|
2011-02-11 07:40:16 +01:00
|
|
|
var i;
|
|
|
|
var fetchers = fc.sourceFetchers;
|
|
|
|
var res;
|
|
|
|
for (i=0; i<fetchers.length; i++) {
|
|
|
|
res = fetchers[i](source, rangeStart, rangeEnd, callback);
|
|
|
|
if (res === true) {
|
|
|
|
// the fetcher is in charge. made its own async request
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (typeof res == 'object') {
|
|
|
|
// the fetcher returned a new source. process it
|
|
|
|
_fetchEventSource(res, callback);
|
|
|
|
return;
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
}
|
2011-02-11 07:40:16 +01:00
|
|
|
var events = source.events;
|
|
|
|
if (events) {
|
|
|
|
if ($.isFunction(events)) {
|
|
|
|
pushLoading();
|
|
|
|
events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
|
|
|
|
callback(events);
|
|
|
|
popLoading();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if ($.isArray(events)) {
|
2010-10-24 22:32:08 +02:00
|
|
|
callback(events);
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
var url = source.url;
|
|
|
|
if (url) {
|
|
|
|
var success = source.success;
|
|
|
|
var error = source.error;
|
|
|
|
var complete = source.complete;
|
|
|
|
var data = $.extend({}, source.data || {});
|
|
|
|
var startParam = firstDefined(source.startParam, options.startParam);
|
|
|
|
var endParam = firstDefined(source.endParam, options.endParam);
|
|
|
|
if (startParam) {
|
2011-10-24 14:23:31 +02:00
|
|
|
var tmpstart = rangeStart;
|
|
|
|
if (source.startParamUTC) {
|
|
|
|
tmpstart -= offsetStart;
|
|
|
|
}
|
|
|
|
data[startParam] = Math.round(+tmpstart / 1000);
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
|
|
|
if (endParam) {
|
2011-10-24 14:23:31 +02:00
|
|
|
var tmpend = rangeEnd;
|
|
|
|
if (source.endParamUTC) {
|
|
|
|
tmpend -= offsetEnd;
|
|
|
|
}
|
|
|
|
data[endParam] = Math.round(+tmpend / 1000);
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
|
|
|
pushLoading();
|
|
|
|
$.ajax($.extend({}, ajaxDefaults, source, {
|
|
|
|
data: data,
|
|
|
|
success: function(events) {
|
|
|
|
events = events || [];
|
|
|
|
var res = applyAll(success, this, arguments);
|
|
|
|
if ($.isArray(res)) {
|
|
|
|
events = res;
|
|
|
|
}
|
|
|
|
callback(events);
|
|
|
|
},
|
|
|
|
error: function() {
|
|
|
|
applyAll(error, this, arguments);
|
|
|
|
callback();
|
|
|
|
},
|
|
|
|
complete: function() {
|
|
|
|
applyAll(complete, this, arguments);
|
|
|
|
popLoading();
|
|
|
|
}
|
|
|
|
}));
|
2011-03-20 02:48:20 +01:00
|
|
|
}else{
|
|
|
|
callback();
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
|
|
|
|
/* Sources
|
|
|
|
-----------------------------------------------------------------------------*/
|
2010-10-11 23:16:34 +02:00
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
|
|
|
|
function addEventSource(source) {
|
2011-02-11 07:40:16 +01:00
|
|
|
source = _addEventSource(source);
|
|
|
|
if (source) {
|
|
|
|
pendingSourceCnt++;
|
|
|
|
fetchEventSource(source, currentFetchID); // will eventually call reportEvents
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _addEventSource(source) {
|
|
|
|
if ($.isFunction(source) || $.isArray(source)) {
|
|
|
|
source = { events: source };
|
|
|
|
}
|
|
|
|
else if (typeof source == 'string') {
|
|
|
|
source = { url: source };
|
|
|
|
}
|
|
|
|
if (typeof source == 'object') {
|
|
|
|
normalizeSource(source);
|
|
|
|
sources.push(source);
|
|
|
|
return source;
|
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
}
|
2010-09-19 07:54:35 +02:00
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
|
|
|
|
function removeEventSource(source) {
|
|
|
|
sources = $.grep(sources, function(src) {
|
2011-02-11 07:40:16 +01:00
|
|
|
return !isSourcesEqual(src, source);
|
2010-10-24 22:32:08 +02:00
|
|
|
});
|
|
|
|
// remove all client events from that source
|
|
|
|
cache = $.grep(cache, function(e) {
|
2011-02-11 07:40:16 +01:00
|
|
|
return !isSourcesEqual(e.source, source);
|
2010-10-24 22:32:08 +02:00
|
|
|
});
|
|
|
|
reportEvents(cache);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Manipulation
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function updateEvent(event) { // update an existing event
|
2010-10-24 22:32:08 +02:00
|
|
|
var i, len = cache.length, e,
|
|
|
|
defaultEventEnd = getView().defaultEventEnd, // getView???
|
2010-09-19 07:54:35 +02:00
|
|
|
startDelta = event.start - event._start,
|
|
|
|
endDelta = event.end ?
|
|
|
|
(event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
|
|
|
|
: 0; // was null and event was just resized
|
|
|
|
for (i=0; i<len; i++) {
|
2010-10-24 22:32:08 +02:00
|
|
|
e = cache[i];
|
2010-09-19 07:54:35 +02:00
|
|
|
if (e._id == event._id && e != event) {
|
|
|
|
e.start = new Date(+e.start + startDelta);
|
|
|
|
if (event.end) {
|
|
|
|
if (e.end) {
|
|
|
|
e.end = new Date(+e.end + endDelta);
|
|
|
|
}else{
|
|
|
|
e.end = new Date(+defaultEventEnd(e) + endDelta);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
e.end = null;
|
|
|
|
}
|
|
|
|
e.title = event.title;
|
|
|
|
e.url = event.url;
|
|
|
|
e.allDay = event.allDay;
|
|
|
|
e.className = event.className;
|
|
|
|
e.editable = event.editable;
|
2011-03-20 02:48:20 +01:00
|
|
|
e.color = event.color;
|
|
|
|
e.backgroudColor = event.backgroudColor;
|
|
|
|
e.borderColor = event.borderColor;
|
|
|
|
e.textColor = event.textColor;
|
|
|
|
normalizeEvent(e);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
}
|
2011-03-20 02:48:20 +01:00
|
|
|
normalizeEvent(event);
|
2010-10-24 22:32:08 +02:00
|
|
|
reportEvents(cache);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-24 22:32:08 +02:00
|
|
|
function renderEvent(event, stick) {
|
2011-03-20 02:48:20 +01:00
|
|
|
normalizeEvent(event);
|
2010-09-19 07:54:35 +02:00
|
|
|
if (!event.source) {
|
|
|
|
if (stick) {
|
2011-02-11 07:40:16 +01:00
|
|
|
stickySource.events.push(event);
|
|
|
|
event.source = stickySource;
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
cache.push(event);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
reportEvents(cache);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function removeEvents(filter) {
|
|
|
|
if (!filter) { // remove all
|
2010-10-24 22:32:08 +02:00
|
|
|
cache = [];
|
2010-09-19 07:54:35 +02:00
|
|
|
// clear all array sources
|
2010-10-24 22:32:08 +02:00
|
|
|
for (var i=0; i<sources.length; i++) {
|
2011-02-11 07:40:16 +01:00
|
|
|
if ($.isArray(sources[i].events)) {
|
|
|
|
sources[i].events = [];
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if (!$.isFunction(filter)) { // an event ID
|
|
|
|
var id = filter + '';
|
|
|
|
filter = function(e) {
|
|
|
|
return e._id == id;
|
|
|
|
};
|
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
cache = $.grep(cache, filter, true);
|
2010-09-19 07:54:35 +02:00
|
|
|
// remove events from array sources
|
2010-10-24 22:32:08 +02:00
|
|
|
for (var i=0; i<sources.length; i++) {
|
2011-02-11 07:40:16 +01:00
|
|
|
if ($.isArray(sources[i].events)) {
|
|
|
|
sources[i].events = $.grep(sources[i].events, filter, true);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
reportEvents(cache);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function clientEvents(filter) {
|
|
|
|
if ($.isFunction(filter)) {
|
2010-10-24 22:32:08 +02:00
|
|
|
return $.grep(cache, filter);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
else if (filter) { // an event ID
|
|
|
|
filter += '';
|
2010-10-24 22:32:08 +02:00
|
|
|
return $.grep(cache, function(e) {
|
2010-09-19 07:54:35 +02:00
|
|
|
return e._id == filter;
|
|
|
|
});
|
|
|
|
}
|
2010-10-24 22:32:08 +02:00
|
|
|
return cache; // else, return all
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Loading State
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function pushLoading() {
|
|
|
|
if (!loadingLevel++) {
|
|
|
|
trigger('loading', null, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function popLoading() {
|
|
|
|
if (!--loadingLevel) {
|
|
|
|
trigger('loading', null, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Event Normalization
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
2011-03-20 02:48:20 +01:00
|
|
|
function normalizeEvent(event) {
|
|
|
|
var source = event.source || {};
|
|
|
|
var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
|
2010-09-19 07:54:35 +02:00
|
|
|
event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
|
|
|
|
if (event.date) {
|
|
|
|
if (!event.start) {
|
|
|
|
event.start = event.date;
|
|
|
|
}
|
|
|
|
delete event.date;
|
|
|
|
}
|
2011-03-20 02:48:20 +01:00
|
|
|
event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
|
|
|
|
event.end = parseDate(event.end, ignoreTimezone);
|
2010-09-19 07:54:35 +02:00
|
|
|
if (event.end && event.end <= event.start) {
|
|
|
|
event.end = null;
|
|
|
|
}
|
|
|
|
event._end = event.end ? cloneDate(event.end) : null;
|
|
|
|
if (event.allDay === undefined) {
|
2011-02-11 07:40:16 +01:00
|
|
|
event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
|
2010-09-19 07:54:35 +02:00
|
|
|
}
|
|
|
|
if (event.className) {
|
|
|
|
if (typeof event.className == 'string') {
|
|
|
|
event.className = event.className.split(/\s+/);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
event.className = [];
|
|
|
|
}
|
|
|
|
// TODO: if there is no start date, return false to indicate an invalid event
|
|
|
|
}
|
2011-02-11 07:40:16 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Utils
|
|
|
|
------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
function normalizeSource(source) {
|
|
|
|
if (source.className) {
|
2011-03-20 02:48:20 +01:00
|
|
|
// TODO: repeat code, same code for event classNames
|
2011-02-11 07:40:16 +01:00
|
|
|
if (typeof source.className == 'string') {
|
|
|
|
source.className = source.className.split(/\s+/);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
source.className = [];
|
|
|
|
}
|
|
|
|
var normalizers = fc.sourceNormalizers;
|
|
|
|
for (var i=0; i<normalizers.length; i++) {
|
|
|
|
normalizers[i](source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isSourcesEqual(source1, source2) {
|
2011-03-20 02:48:20 +01:00
|
|
|
return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
|
2011-02-11 07:40:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getSourcePrimitive(source) {
|
|
|
|
return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
|
|
|
|
}
|
2010-09-19 07:54:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
}
|