[2.0] source file changes

This commit is contained in:
Adam Shaw
2014-01-24 23:10:00 -08:00
parent 7bd4e08bd0
commit d0d0a97c64
25 changed files with 1883 additions and 1151 deletions
+41 -24
View File
@@ -14,7 +14,6 @@ function DayEventRenderer() {
var trigger = t.trigger;
var isEventDraggable = t.isEventDraggable;
var isEventResizable = t.isEventResizable;
var eventEnd = t.eventEnd;
var reportEventElement = t.reportEventElement;
var eventElementHandlers = t.eventElementHandlers;
var showEvents = t.showEvents;
@@ -31,7 +30,6 @@ function DayEventRenderer() {
var colContentRight = t.colContentRight;
var dateToCell = t.dateToCell;
var getDaySegmentContainer = t.getDaySegmentContainer;
var formatDates = t.calendar.formatDates;
var renderDayOverlay = t.renderDayOverlay;
var clearOverlays = t.clearOverlays;
var clearSelection = t.clearSelection;
@@ -42,6 +40,9 @@ function DayEventRenderer() {
var cellOffsetToDayOffset = t.cellOffsetToDayOffset;
var dateToDayOffset = t.dateToDayOffset;
var dayOffsetToCellOffset = t.dayOffsetToCellOffset;
var calendar = t.calendar;
var getEventEnd = calendar.getEventEnd;
var formatDate = calendar.formatDate;
// Render `events` onto the calendar, attach mouse event handlers, and call the `eventAfterRender` callback for each.
@@ -181,9 +182,7 @@ function DayEventRenderer() {
// A "segment" is the same data structure that View.rangeToSegments produces,
// with the addition of the `event` property being set to reference the original event.
function buildSegmentsForEvent(event) {
var startDate = event.start;
var endDate = exclEndDay(event);
var segments = rangeToSegments(startDate, endDate);
var segments = rangeToSegments(event.start, getEventEnd(event));
for (var i=0; i<segments.length; i++) {
segments[i].event = event;
}
@@ -246,7 +245,7 @@ function DayEventRenderer() {
classNames.push('fc-event-end');
}
// use the event's configured classNames
// guaranteed to be an array via `normalizeEvent`
// guaranteed to be an array via `buildEvent`
classNames = classNames.concat(event.className);
if (event.source) {
// use the event's source's classNames, if specified
@@ -276,7 +275,7 @@ function DayEventRenderer() {
html +=
"<span class='fc-event-time'>" +
htmlEscape(
formatDates(event.start, event.end, opt('timeFormat'))
formatDate(event.start, opt('timeFormat'))
) +
"</span>";
}
@@ -285,7 +284,7 @@ function DayEventRenderer() {
htmlEscape(event.title || '') +
"</span>" +
"</div>";
if (segment.isEnd && isEventResizable(event)) {
if (event.allDay && segment.isEnd && isEventResizable(event)) {
html +=
"<div class='ui-resizable-handle ui-resizable-" + (isRTL ? 'w' : 'e') + "'>" +
"&nbsp;&nbsp;&nbsp;" + // makes hit area a lot better for IE6/7
@@ -554,6 +553,7 @@ function DayEventRenderer() {
}
if (
event.allDay &&
segment.isEnd && // only allow resizing on the final segment for an event
isEventResizable(event)
) {
@@ -565,10 +565,11 @@ function DayEventRenderer() {
eventElementHandlers(event, eventElement);
}
function draggableDayEvent(event, eventElement) {
var hoverListener = getHoverListener();
var dayDelta;
var eventStart;
eventElement.draggable({
delay: 50,
opacity: opt('dragOpacity'),
@@ -580,14 +581,16 @@ function DayEventRenderer() {
eventElement.draggable('option', 'revert', !cell || !rowDelta && !colDelta);
clearOverlays();
if (cell) {
var origDate = cellToDate(origCell);
var date = cellToDate(cell);
dayDelta = dayDiff(date, origDate);
var origCellDate = cellToDate(origCell);
var cellDate = cellToDate(cell);
dayDelta = cellDate.diff(origCellDate, 'days');
eventStart = event.start.clone().add('days', dayDelta);
renderDayOverlay(
addDays(cloneDate(event.start), dayDelta),
addDays(exclEndDay(event), dayDelta)
eventStart,
getEventEnd(event).add('days', dayDelta)
);
}else{
}
else {
dayDelta = 0;
}
}, ev, 'drag');
@@ -597,8 +600,15 @@ function DayEventRenderer() {
clearOverlays();
trigger('eventDragStop', eventElement, event, ev, ui);
if (dayDelta) {
eventDrop(this, event, dayDelta, 0, event.allDay, ev, ui);
}else{
eventDrop(
this, // el
event,
eventStart,
ev,
ui
);
}
else {
eventElement.css('filter', ''); // clear IE opacity side-effects
showEvents(event, eventElement);
}
@@ -637,6 +647,7 @@ function DayEventRenderer() {
var colCnt = getColCnt();
var elementTop = element.css('top');
var dayDelta;
var eventEnd;
var helpers;
var eventCopy = $.extend({}, event);
var minCellOffset = dayOffsetToCellOffset( dateToDayOffset(event.start) );
@@ -658,18 +669,17 @@ function DayEventRenderer() {
cellOffsetToDayOffset(cellOffset) -
cellOffsetToDayOffset(origCellOffset);
if (dayDelta) {
eventCopy.end = addDays(eventEnd(event), dayDelta, true);
var oldHelpers = helpers;
eventEnd = getEventEnd(event).add('days', dayDelta); // assumed to already have a stripped time
if (dayDelta) {
eventCopy.end = eventEnd;
var oldHelpers = helpers;
helpers = renderTempDayEvent(eventCopy, segment.row, elementTop);
helpers = $(helpers); // turn array into a jQuery object
helpers.find('*').css('cursor', direction + '-resize');
if (oldHelpers) {
oldHelpers.remove();
}
hideEvents(event);
}
else {
@@ -679,10 +689,11 @@ function DayEventRenderer() {
helpers = null;
}
}
clearOverlays();
renderDayOverlay( // coordinate grid already rebuilt with hoverListener.start()
event.start,
addDays( exclEndDay(event), dayDelta )
eventEnd
// TODO: instead of calling renderDayOverlay() with dates,
// call _renderDayOverlay (or whatever) with cell offsets.
);
@@ -694,8 +705,14 @@ function DayEventRenderer() {
$('body').css('cursor', '');
hoverListener.stop();
clearOverlays();
if (dayDelta) {
eventResize(this, event, dayDelta, 0, ev);
eventResize(
this, // el
event,
eventEnd,
ev
);
// event redraw will clear helpers
}
// otherwise, the drag handler already restored the old events
+25 -10
View File
@@ -13,6 +13,7 @@ function SelectionManager() {
// imports
var calendar = t.calendar;
var opt = t.opt;
var trigger = t.trigger;
var defaultSelectionEnd = t.defaultSelectionEnd;
@@ -39,13 +40,19 @@ function SelectionManager() {
}
function select(startDate, endDate, allDay) {
function select(start, end) {
unselect();
if (!endDate) {
endDate = defaultSelectionEnd(startDate, allDay);
start = calendar.moment(start);
if (end) {
end = calendar.moment(end);
}
renderSelection(startDate, endDate, allDay);
reportSelection(startDate, endDate, allDay);
else {
end = defaultSelectionEnd(start);
}
renderSelection(start, end);
reportSelection(start, end);
}
@@ -58,9 +65,9 @@ function SelectionManager() {
}
function reportSelection(startDate, endDate, allDay, ev) {
function reportSelection(start, end, ev) {
selected = true;
trigger('select', null, startDate, endDate, allDay, ev);
trigger('select', null, start, end, ev);
}
@@ -69,6 +76,7 @@ function SelectionManager() {
var getIsCellAllDay = t.getIsCellAllDay;
var hoverListener = t.getHoverListener();
var reportDayClick = t.reportDayClick; // this is hacky and sort of weird
if (ev.which == 1 && opt('selectable')) { // which==1 means left mouse button
unselect(ev);
var _mousedownElement = this;
@@ -77,7 +85,10 @@ function SelectionManager() {
clearSelection();
if (cell && getIsCellAllDay(cell)) {
dates = [ cellToDate(origCell), cellToDate(cell) ].sort(dateCompare);
renderSelection(dates[0], dates[1], true);
renderSelection(
dates[0],
dates[1].clone().add('days', 1) // make exclusive
);
}else{
dates = null;
}
@@ -86,9 +97,13 @@ function SelectionManager() {
hoverListener.stop();
if (dates) {
if (+dates[0] == +dates[1]) {
reportDayClick(dates[0], true, ev);
reportDayClick(dates[0], ev);
}
reportSelection(dates[0], dates[1], true, ev);
reportSelection(
dates[0],
dates[1].clone().add('days', 1), // make exclusive
ev
);
}
});
}
+203 -83
View File
@@ -14,7 +14,6 @@ function View(element, calendar, viewName) {
t.isEventResizable = isEventResizable;
t.setEventData = setEventData;
t.clearEventData = clearEventData;
t.eventEnd = eventEnd;
t.reportEventElement = reportEventElement;
t.triggerEventDestroy = triggerEventDestroy;
t.eventElementHandlers = eventElementHandlers;
@@ -22,15 +21,14 @@ function View(element, calendar, viewName) {
t.hideEvents = hideEvents;
t.eventDrop = eventDrop;
t.eventResize = eventResize;
// t.title
// t.start, t.end
// t.visStart, t.visEnd
t.mutateEvent = mutateEvent;
// t.start, t.end // moments with ambiguous-time
// t.intervalStart, t.intervalEnd // moments with ambiguous-time
// imports
var defaultEventEnd = t.defaultEventEnd;
var normalizeEvent = calendar.normalizeEvent; // in EventManager
var reportEventChange = calendar.reportEventChange;
var getEventEnd = calendar.getEventEnd;
// locals
@@ -38,12 +36,14 @@ function View(element, calendar, viewName) {
var eventElementsByID = {}; // eventID mapped to array of jQuery elements
var eventElementCouples = []; // array of objects, { event, element } // TODO: unify with segment system
var options = calendar.options;
var nextDayThreshold = moment.duration(options.nextDayThreshold);
function opt(name, viewNameOverride) {
var v = options[name];
if ($.isPlainObject(v)) {
if ($.isPlainObject(v) && !isForcedAtomicOption(name)) {
return smartProperty(v, viewNameOverride || viewName);
}
return v;
@@ -72,8 +72,7 @@ function View(element, calendar, viewName) {
event.editable,
source.editable,
opt('editable')
)
&& !opt('disableDragging'); // deprecated
);
}
@@ -86,8 +85,7 @@ function View(element, calendar, viewName) {
event.editable,
source.editable,
opt('editable')
)
&& !opt('disableResizing'); // deprecated
);
}
@@ -117,12 +115,6 @@ function View(element, calendar, viewName) {
}
// returns a Date object for an event's end
function eventEnd(event) {
return event.end ? cloneDate(event.end) : defaultEventEnd(event);
}
/* Event Elements
------------------------------------------------------------------------------*/
@@ -194,83 +186,206 @@ function View(element, calendar, viewName) {
/* Event Modification Reporting
---------------------------------------------------------------------------------*/
function eventDrop(e, event, dayDelta, minuteDelta, allDay, ev, ui) {
var oldAllDay = event.allDay;
var eventId = event._id;
moveEvents(eventsByID[eventId], dayDelta, minuteDelta, allDay);
function eventDrop(el, event, newStart, ev, ui) {
var undoMutation = mutateEvent(event, newStart, null);
trigger(
'eventDrop',
e,
el,
event,
dayDelta,
minuteDelta,
allDay,
function() {
// TODO: investigate cases where this inverse technique might not work
moveEvents(eventsByID[eventId], -dayDelta, -minuteDelta, oldAllDay);
reportEventChange(eventId);
undoMutation();
reportEventChange(event._id);
},
ev,
ui
);
reportEventChange(eventId);
reportEventChange(event._id);
}
function eventResize(e, event, dayDelta, minuteDelta, ev, ui) {
var eventId = event._id;
elongateEvents(eventsByID[eventId], dayDelta, minuteDelta);
function eventResize(el, event, newEnd, ev, ui) {
var undoMutation = mutateEvent(event, null, newEnd);
trigger(
'eventResize',
e,
el,
event,
dayDelta,
minuteDelta,
function() {
// TODO: investigate cases where this inverse technique might not work
elongateEvents(eventsByID[eventId], -dayDelta, -minuteDelta);
reportEventChange(eventId);
undoMutation();
reportEventChange(event._id);
},
ev,
ui
);
reportEventChange(eventId);
reportEventChange(event._id);
}
/* Event Modification Math
---------------------------------------------------------------------------------*/
function moveEvents(events, dayDelta, minuteDelta, allDay) {
minuteDelta = minuteDelta || 0;
for (var e, len=events.length, i=0; i<len; i++) {
e = events[i];
if (allDay !== undefined) {
e.allDay = allDay;
}
addMinutes(addDays(e.start, dayDelta, true), minuteDelta);
if (e.end) {
e.end = addMinutes(addDays(e.end, dayDelta, true), minuteDelta);
}
normalizeEvent(e, options);
-----------------------------------------------------------------------------------------*/
// Modify the date(s) of an event and make this change propagate to all other events with
// the same ID (related repeating events).
//
// If `newStart`/`newEnd` are not specified, the "new" dates are assumed to be `event.start` and `event.end`.
// The "old" dates to be compare against are always `event._start` and `event._end` (set by EventManager).
//
// Returns a function that can be called to undo all the operations.
//
function mutateEvent(event, newStart, newEnd) {
var oldAllDay = event._allDay;
var oldStart = event._start;
var oldEnd = event._end;
var clearEnd = false;
var newAllDay;
var dateDelta;
var durationDelta;
// if no new dates were passed in, compare against the event's existing dates
if (!newStart && !newEnd) {
newStart = event.start;
newEnd = event.end;
}
}
function elongateEvents(events, dayDelta, minuteDelta) {
minuteDelta = minuteDelta || 0;
for (var e, len=events.length, i=0; i<len; i++) {
e = events[i];
e.end = addMinutes(addDays(eventEnd(e), dayDelta, true), minuteDelta);
normalizeEvent(e, options);
// NOTE: throughout this function, the initial values of `newStart` and `newEnd` are
// preserved. These values may be undefined.
// detect new allDay
if (event.allDay != oldAllDay) { // if value has changed, use it
newAllDay = event.allDay;
}
else { // otherwise, see if any of the new dates are allDay
newAllDay = !(newStart || newEnd).hasTime();
}
// normalize the new dates based on allDay
if (newAllDay) {
if (newStart) {
newStart = newStart.clone().stripTime();
}
if (newEnd) {
newEnd = newEnd.clone().stripTime();
}
}
// compute dateDelta
if (newStart) {
if (newAllDay) {
dateDelta = dayishDiff(newStart, oldStart.clone().stripTime()); // treat oldStart as allDay
}
else {
dateDelta = dayishDiff(newStart, oldStart);
}
}
if (newAllDay != oldAllDay) {
// if allDay has changed, always throw away the end
clearEnd = true;
}
else if (newEnd) {
durationDelta = dayishDiff(
// new duration
newEnd || calendar.getDefaultEventEnd(newAllDay, newStart || oldStart),
newStart || oldStart
).subtract(dayishDiff(
// subtract old duration
oldEnd || calendar.getDefaultEventEnd(oldAllDay, oldStart),
oldStart
));
}
return mutateEvents(eventsByID[event._id], clearEnd, newAllDay, dateDelta, durationDelta);
}
// Modifies an array of events in the following ways (operations are in order):
// - clear the event's `end`
// - convert the event to allDay
// - add `dateDelta` to the start and end
// - add `durationDelta` to the event's duration
//
// Returns a function that can be called to undo all the operations.
//
function mutateEvents(events, clearEnd, forceAllDay, dateDelta, durationDelta) {
var isAmbigTimezone = calendar.getIsAmbigTimezone();
var undoFunctions = [];
$.each(events, function(i, event) {
var oldAllDay = event._allDay;
var oldStart = event._start;
var oldEnd = event._end;
var newAllDay = forceAllDay != null ? forceAllDay : oldAllDay;
var newStart = oldStart.clone();
var newEnd = (!clearEnd && oldEnd) ? oldEnd.clone() : null;
// NOTE: this function is responsible for transforming `newStart` and `newEnd`,
// which were initialized to the OLD values first. `newEnd` may be null.
// normlize newStart/newEnd to be consistent with newAllDay
if (newAllDay) {
newStart.stripTime();
if (newEnd) {
newEnd.stripTime();
}
}
else {
if (!newStart.hasTime()) {
newStart = calendar.rezoneDate(newStart);
}
if (newEnd && !newEnd.hasTime()) {
newEnd = calendar.rezoneDate(newEnd);
}
}
// ensure we have an end date if necessary
if (!newEnd && (options.forceEventDuration || +durationDelta)) {
newEnd = calendar.getDefaultEventEnd(newAllDay, newStart);
}
// translate the dates
newStart.add(dateDelta);
if (newEnd) {
newEnd.add(dateDelta).add(durationDelta);
}
// if the dates have changed, and we know it is impossible to recompute the
// timezone offsets, strip the zone.
if (isAmbigTimezone) {
if (+dateDelta) {
newStart.stripZone();
}
if (newEnd && (+dateDelta || +durationDelta)) {
newEnd.stripZone();
}
}
event.allDay = newAllDay;
event.start = newStart;
event.end = newEnd;
backupEventDates(event);
undoFunctions.push(function() {
event.allDay = oldAllDay;
event.start = oldStart;
event.end = oldEnd;
backupEventDates(event);
});
});
return function() {
for (var i=0; i<undoFunctions.length; i++) {
undoFunctions[i]();
}
};
}
// ====================================================================================================
// Utilities for day "cells"
@@ -289,7 +404,7 @@ function View(element, calendar, viewName) {
//
// 2. Convert the "cell offset" to a "day offset" (the # of days since the first visible day in the view).
//
// 3. Convert the "day offset" into a "date" (a JavaScript Date object).
// 3. Convert the "day offset" into a "date" (a Moment).
//
// The reverse transformation happens when transforming a date into a cell.
@@ -345,10 +460,10 @@ function View(element, calendar, viewName) {
// Is the current day hidden?
// `day` is a day-of-week index (0-6), or a Date object
// `day` is a day-of-week index (0-6), or a Moment
function isHiddenDay(day) {
if (typeof day == 'object') {
day = day.getDay();
if (moment.isMoment(day)) {
day = day.day();
}
return isHiddenDayHash[day];
}
@@ -359,17 +474,19 @@ function View(element, calendar, viewName) {
}
// Keep incrementing the current day until it is no longer a hidden day.
// Incrementing the current day until it is no longer a hidden day, returning a copy.
// If the initial value of `date` is not a hidden day, don't do anything.
// Pass `isExclusive` as `true` if you are dealing with an end date.
// `inc` defaults to `1` (increment one day forward each time)
function skipHiddenDays(date, inc, isExclusive) {
var out = date.clone();
inc = inc || 1;
while (
isHiddenDayHash[ ( date.getDay() + (isExclusive ? inc : 0) + 7 ) % 7 ]
isHiddenDayHash[ ( out.day() + (isExclusive ? inc : 0) + 7 ) % 7 ]
) {
addDays(date, inc);
out.add('days', inc);
}
return out;
}
@@ -410,7 +527,7 @@ function View(element, calendar, viewName) {
// cell offset -> day offset
function cellOffsetToDayOffset(cellOffset) {
var day0 = t.visStart.getDay(); // first date's day of week
var day0 = t.start.day(); // first date's day of week
cellOffset += dayToCellMap[day0]; // normlize cellOffset to beginning-of-week
return Math.floor(cellOffset / cellsPerWeek) * 7 // # of days from full weeks
+ cellToDayMap[ // # of days from partial last week
@@ -419,11 +536,9 @@ function View(element, calendar, viewName) {
- day0; // adjustment for beginning-of-week normalization
}
// day offset -> date (JavaScript Date object)
// day offset -> date
function dayOffsetToDate(dayOffset) {
var date = cloneDate(t.visStart);
addDays(date, dayOffset);
return date;
return t.start.clone().add('days', dayOffset);
}
@@ -441,12 +556,12 @@ function View(element, calendar, viewName) {
// date -> day offset
function dateToDayOffset(date) {
return dayDiff(date, t.visStart);
return date.clone().stripTime().diff(t.start, 'days');
}
// day offset -> cell offset
function dayOffsetToCellOffset(dayOffset) {
var day0 = t.visStart.getDay(); // first date's day of week
var day0 = t.start.day(); // first date's day of week
dayOffset += day0; // normalize dayOffset to beginning-of-week
return Math.floor(dayOffset / 7) * cellsPerWeek // # of cells from full weeks
+ dayToCellMap[ // # of cells from partial last week
@@ -481,14 +596,19 @@ function View(element, calendar, viewName) {
// - isStart
// - isEnd
//
function rangeToSegments(startDate, endDate) {
function rangeToSegments(start, end) {
var rowCnt = t.getRowCnt();
var colCnt = t.getColCnt();
var segments = []; // array of segments to return
// day offset for given date range
var rangeDayOffsetStart = dateToDayOffset(startDate);
var rangeDayOffsetEnd = dateToDayOffset(endDate); // exclusive
var rangeDayOffsetStart = dateToDayOffset(start);
var rangeDayOffsetEnd = dateToDayOffset(end); // an exclusive value
if (end.time() > nextDayThreshold) {
rangeDayOffsetEnd++;
}
rangeDayOffsetEnd = Math.max(rangeDayOffsetEnd, rangeDayOffsetStart + 1);
// first and last cell offset for the given date range
// "last" implies inclusivity
+19 -13
View File
@@ -52,32 +52,38 @@
and we'll try to make them look good cross-browser.
*/
.fc-text-arrow {
.fc-button .fc-icon {
margin: 0 .1em;
font-size: 2em;
font-family: "Courier New", Courier, monospace;
vertical-align: baseline; /* for IE7 */
}
.fc-button-prev .fc-text-arrow,
.fc-button-next .fc-text-arrow { /* for &lsaquo; &rsaquo; */
.fc-icon-left-single-arrow:after {
content: "\02039";
font-weight: bold;
}
.fc-icon-right-single-arrow:after {
content: "\0203A";
font-weight: bold;
}
.fc-icon-left-double-arrow:after {
content: "\000AB";
}
.fc-icon-right-double-arrow:after {
content: "\000BB";
}
/* icon (for jquery ui) */
.fc-button .fc-icon-wrap {
position: relative;
float: left;
top: 50%;
}
.fc-button .ui-icon {
position: relative;
top: 50%;
float: left;
margin-top: -50%;
*margin-top: 0;
*top: -50%;
margin-top: -8px; /* we know jqui icons are always 16px tall */
}
/*