update fullcalendar to version 2.8.0

This commit is contained in:
Mario Vavti 2016-06-28 11:56:28 +02:00
parent 7d7f43c205
commit aff58934c0
10 changed files with 267 additions and 71 deletions

View file

@ -1,4 +1,24 @@
v2.8.0 (2016-06-19)
-------------------
- getEventSources method (#3103, #2433)
- getEventSourceById method (#3223)
- refetchEventSources method (#3103, #1328, #254)
- removeEventSources method (#3165, #948)
- prevent flicker when refetchEvents is called (#3123, #2558)
- fix for removing event sources that share same URL (#3209)
- jQuery 3 support (#3197, #3124)
- Travis CI integration (#3218)
- EditorConfig for promoting consistent code style (#141)
- use en dash when formatting ranges (#3077)
- height:auto always shows scrollbars in month view on FF (#3202)
- new languages:
- Basque (#2992)
- Galician (#194)
- Luxembourgish (#2979)
v2.7.3 (2016-06-02)
-------------------

View file

@ -1,5 +1,5 @@
/*!
* FullCalendar v2.7.3 Stylesheet
* FullCalendar v2.8.0 Stylesheet
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/
@ -367,6 +367,7 @@ hr.fc-divider {
.fc table {
width: 100%;
box-sizing: border-box; /* fix scrollbar issue in firefox */
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;

View file

@ -1,5 +1,5 @@
/*!
* FullCalendar v2.7.3
* FullCalendar v2.8.0
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/
@ -19,7 +19,7 @@
;;
var FC = $.fullCalendar = {
version: "2.7.3",
version: "2.8.0",
internalApiVersion: 4
};
var fcViews = FC.views = {};
@ -1054,6 +1054,20 @@ function debounce(func, wait, immediate) {
};
}
// HACK around jQuery's now A+ promises: execute callback synchronously if already resolved.
// thenFunc shouldn't accept args.
// similar to whenResources in Scheduler plugin.
function syncThen(promise, thenFunc) {
// not a promise, or an already-resolved promise?
if (!promise || !promise.then || promise.state() === 'resolved') {
return $.when(thenFunc()); // resolve immediately
}
else if (thenFunc) {
return promise.then(thenFunc);
}
}
;;
var ambigDateOfMonthRegex = /^\s*\d{4}-\d\d$/;
@ -3960,7 +3974,7 @@ var Grid = FC.Grid = Class.extend(ListenerMixin, MouseIgnorerMixin, {
fillSegTag: 'div', // subclasses can override
// Builds the HTML needed for one fill segment. Generic enought o work with different types.
// Builds the HTML needed for one fill segment. Generic enough to work with different types.
fillSegHtml: function(type, seg) {
// custom hooks per-type
@ -8106,15 +8120,14 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
this.calendar.freezeContentHeight();
return this.clear().then(function() { // clear the content first (async)
return syncThen(this.clear(), function() { // clear the content first
return (
_this.displaying =
$.when(_this.displayView(date)) // displayView might return a promise
.then(function() {
_this.forceScroll(_this.computeInitialScroll(scrollState));
_this.calendar.unfreezeContentHeight();
_this.triggerRender();
})
syncThen(_this.displayView(date), function() { // displayView might return a promise
_this.forceScroll(_this.computeInitialScroll(scrollState));
_this.calendar.unfreezeContentHeight();
_this.triggerRender();
})
);
});
},
@ -8128,7 +8141,7 @@ var View = FC.View = Class.extend(EmitterMixin, ListenerMixin, {
var displaying = this.displaying;
if (displaying) { // previously displayed, or in the process of being displayed?
return displaying.then(function() { // wait for the display to finish
return syncThen(displaying, function() { // wait for the display to finish
_this.displaying = null;
_this.clearEvents();
return _this.clearView(); // might return a promise. chain it
@ -9321,6 +9334,7 @@ function Calendar_constructor(element, overrides) {
t.render = render;
t.destroy = destroy;
t.refetchEvents = refetchEvents;
t.refetchEventSources = refetchEventSources;
t.reportEvents = reportEvents;
t.reportEventChange = reportEventChange;
t.rerenderEvents = renderEvents; // `renderEvents` serves as a rerender. an API method
@ -9511,6 +9525,7 @@ function Calendar_constructor(element, overrides) {
EventManager.call(t, options);
var isFetchNeeded = t.isFetchNeeded;
var fetchEvents = t.fetchEvents;
var fetchEventSources = t.fetchEventSources;
@ -9750,11 +9765,16 @@ function Calendar_constructor(element, overrides) {
function refetchEvents() { // can be called as an API method
destroyEvents(); // so that events are cleared before user starts waiting for AJAX
fetchAndRenderEvents();
}
// TODO: move this into EventManager?
function refetchEventSources(matchInputs) {
fetchEventSources(t.getEventSourcesByMatchArray(matchInputs));
}
function renderEvents() { // destroys old events if previously rendered
if (elementVisible()) {
freezeContentHeight();
@ -9762,13 +9782,6 @@ function Calendar_constructor(element, overrides) {
unfreezeContentHeight();
}
}
function destroyEvents() {
freezeContentHeight();
currentView.clearEvents();
unfreezeContentHeight();
}
function getAndRenderEvents() {
@ -9979,7 +9992,7 @@ function Calendar_constructor(element, overrides) {
Calendar.defaults = {
titleRangeSeparator: ' \u2014 ', // emphasized dash
titleRangeSeparator: ' \u2013 ', // en dash
monthYearFormat: 'MMMM YYYY', // required for en. other languages rely on datepicker computable option
defaultTimedEventDuration: '02:00:00',
@ -10528,14 +10541,14 @@ function Header(calendar, options) {
function disableButton(buttonName) {
el.find('.fc-' + buttonName + '-button')
.attr('disabled', 'disabled')
.prop('disabled', true)
.addClass(tm + '-state-disabled');
}
function enableButton(buttonName) {
el.find('.fc-' + buttonName + '-button')
.removeAttr('disabled')
.prop('disabled', false)
.removeClass(tm + '-state-disabled');
}
@ -10566,8 +10579,14 @@ function EventManager(options) { // assumed to be a calendar
// exports
t.isFetchNeeded = isFetchNeeded;
t.fetchEvents = fetchEvents;
t.fetchEventSources = fetchEventSources;
t.getEventSources = getEventSources;
t.getEventSourceById = getEventSourceById;
t.getEventSourcesByMatchArray = getEventSourcesByMatchArray;
t.getEventSourcesByMatch = getEventSourcesByMatch;
t.addEventSource = addEventSource;
t.removeEventSource = removeEventSource;
t.removeEventSources = removeEventSources;
t.updateEvent = updateEvent;
t.renderEvent = renderEvent;
t.removeEvents = removeEvents;
@ -10585,8 +10604,7 @@ function EventManager(options) { // assumed to be a calendar
var stickySource = { events: [] };
var sources = [ stickySource ];
var rangeStart, rangeEnd;
var currentFetchID = 0;
var pendingSourceCnt = 0;
var pendingSourceCnt = 0; // outstanding fetch requests, max one per source
var cache = []; // holds events that have already been expanded
@ -10616,23 +10634,58 @@ function EventManager(options) { // assumed to be a calendar
function fetchEvents(start, end) {
rangeStart = start;
rangeEnd = end;
cache = [];
var fetchID = ++currentFetchID;
var len = sources.length;
pendingSourceCnt = len;
for (var i=0; i<len; i++) {
fetchEventSource(sources[i], fetchID);
fetchEventSources(sources, 'reset');
}
// expects an array of event source objects (the originals, not copies)
// `specialFetchType` is an optimization parameter that affects purging of the event cache.
function fetchEventSources(specificSources, specialFetchType) {
var i, source;
if (specialFetchType === 'reset') {
cache = [];
}
else if (specialFetchType !== 'add') {
cache = excludeEventsBySources(cache, specificSources);
}
for (i = 0; i < specificSources.length; i++) {
source = specificSources[i];
// already-pending sources have already been accounted for in pendingSourceCnt
if (source._status !== 'pending') {
pendingSourceCnt++;
}
source._fetchId = (source._fetchId || 0) + 1;
source._status = 'pending';
}
for (i = 0; i < specificSources.length; i++) {
source = specificSources[i];
tryFetchEventSource(source, source._fetchId);
}
}
function fetchEventSource(source, fetchID) {
// fetches an event source and processes its result ONLY if it is still the current fetch.
// caller is responsible for incrementing pendingSourceCnt first.
function tryFetchEventSource(source, fetchId) {
_fetchEventSource(source, function(eventInputs) {
var isArraySource = $.isArray(source.events);
var i, eventInput;
var abstractEvent;
if (fetchID == currentFetchID) {
if (
// is this the source's most recent fetch?
// if not, rely on an upcoming fetch of this source to decrement pendingSourceCnt
fetchId === source._fetchId &&
// event source no longer valid?
source._status !== 'rejected'
) {
source._status = 'resolved';
if (eventInputs) {
for (i = 0; i < eventInputs.length; i++) {
@ -10654,13 +10707,29 @@ function EventManager(options) { // assumed to be a calendar
}
}
pendingSourceCnt--;
if (!pendingSourceCnt) {
reportEvents(cache);
}
decrementPendingSourceCnt();
}
});
}
function rejectEventSource(source) {
var wasPending = source._status === 'pending';
source._status = 'rejected';
if (wasPending) {
decrementPendingSourceCnt();
}
}
function decrementPendingSourceCnt() {
pendingSourceCnt--;
if (!pendingSourceCnt) {
reportEvents(cache);
}
}
function _fetchEventSource(source, callback) {
@ -10776,14 +10845,13 @@ function EventManager(options) { // assumed to be a calendar
/* Sources
-----------------------------------------------------------------------------*/
function addEventSource(sourceInput) {
var source = buildEventSource(sourceInput);
if (source) {
sources.push(source);
pendingSourceCnt++;
fetchEventSource(source, currentFetchID); // will eventually call reportEvents
fetchEventSources([ source ], 'add'); // will eventually call reportEvents
}
}
@ -10833,19 +10901,120 @@ function EventManager(options) { // assumed to be a calendar
}
function removeEventSource(source) {
sources = $.grep(sources, function(src) {
return !isSourcesEqual(src, source);
});
// remove all client events from that source
cache = $.grep(cache, function(e) {
return !isSourcesEqual(e.source, source);
});
function removeEventSource(matchInput) {
removeSpecificEventSources(
getEventSourcesByMatch(matchInput)
);
}
// if called with no arguments, removes all.
function removeEventSources(matchInputs) {
if (matchInputs == null) {
removeSpecificEventSources(sources, true); // isAll=true
}
else {
removeSpecificEventSources(
getEventSourcesByMatchArray(matchInputs)
);
}
}
function removeSpecificEventSources(targetSources, isAll) {
var i;
// cancel pending requests
for (i = 0; i < targetSources.length; i++) {
rejectEventSource(targetSources[i]);
}
if (isAll) { // an optimization
sources = [];
cache = [];
}
else {
// remove from persisted source list
sources = $.grep(sources, function(source) {
for (i = 0; i < targetSources.length; i++) {
if (source === targetSources[i]) {
return false; // exclude
}
}
return true; // include
});
cache = excludeEventsBySources(cache, targetSources);
}
reportEvents(cache);
}
function isSourcesEqual(source1, source2) {
function getEventSources() {
return sources.slice(1); // returns a shallow copy of sources with stickySource removed
}
function getEventSourceById(id) {
return $.grep(sources, function(source) {
return source.id && source.id === id;
})[0];
}
// like getEventSourcesByMatch, but accepts multple match criteria (like multiple IDs)
function getEventSourcesByMatchArray(matchInputs) {
// coerce into an array
if (!matchInputs) {
matchInputs = [];
}
else if (!$.isArray(matchInputs)) {
matchInputs = [ matchInputs ];
}
var matchingSources = [];
var i;
// resolve raw inputs to real event source objects
for (i = 0; i < matchInputs.length; i++) {
matchingSources.push.apply( // append
matchingSources,
getEventSourcesByMatch(matchInputs[i])
);
}
return matchingSources;
}
// matchInput can either by a real event source object, an ID, or the function/URL for the source.
// returns an array of matching source objects.
function getEventSourcesByMatch(matchInput) {
var i, source;
// given an proper event source object
for (i = 0; i < sources.length; i++) {
source = sources[i];
if (source === matchInput) {
return [ source ];
}
}
// an ID match
source = getEventSourceById(matchInput);
if (source) {
return [ source ];
}
return $.grep(sources, function(source) {
return isSourcesEquivalent(matchInput, source);
});
}
function isSourcesEquivalent(source1, source2) {
return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
}
@ -10858,6 +11027,20 @@ function EventManager(options) { // assumed to be a calendar
) ||
source; // the given argument *is* the primitive
}
// util
// returns a filtered array without events that are part of any of the given sources
function excludeEventsBySources(specificEvents, specificSources) {
return $.grep(specificEvents, function(event) {
for (var i = 0; i < specificSources.length; i++) {
if (event.source === specificSources[i]) {
return false; // exclude
}
}
return true; // keep
});
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
/*!
* FullCalendar v2.7.3 Print Stylesheet
* FullCalendar v2.8.0 Print Stylesheet
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/

View file

@ -1,5 +1,5 @@
/*!
* FullCalendar v2.7.3 Google Calendar Plugin
* FullCalendar v2.8.0 Google Calendar Plugin
* Docs & License: http://fullcalendar.io/
* (c) 2016 Adam Shaw
*/

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,3 @@
.fc-scroller {
overflow: hidden !important;
}
/* fix borders */
.fc th:first-child,

View file

@ -1,7 +1,3 @@
.fc-scroller {
overflow: hidden !important;
}
/* fix borders */
.fc th:first-child,