wrapper around the
var topEl; // the element we want to match the top coordinate of
var options;
if (this.rowCnt === 1) {
topEl = view.el; // will cause the popover to cover any sort of header
}
else {
topEl = this.rowEls[row]; // will align with top of row
}
options = {
className: 'fc-more-popover ' + theme.getClass('popover'),
parentEl: view.el,
top: core.computeRect(topEl).top,
autoHide: true,
content: function (el) {
_this.segPopoverTile = new DayTile(el);
_this.updateSegPopoverTile(_this.props.cells[row][_col].date, segs);
},
hide: function () {
_this.segPopoverTile.destroy();
_this.segPopoverTile = null;
_this.segPopover.destroy();
_this.segPopover = null;
}
};
// Determine horizontal coordinate.
// We use the moreWrap instead of the to avoid border confusion.
if (isRtl) {
options.right = core.computeRect(moreWrap).right + 1; // +1 to be over cell border
}
else {
options.left = core.computeRect(moreWrap).left - 1; // -1 to be over cell border
}
this.segPopover = new Popover(options);
this.segPopover.show();
calendar.releaseAfterSizingTriggers(); // hack for eventPositioned
};
// Given the events within an array of segment objects, reslice them to be in a single day
DayGrid.prototype.resliceDaySegs = function (segs, dayDate) {
var dayStart = dayDate;
var dayEnd = core.addDays(dayStart, 1);
var dayRange = { start: dayStart, end: dayEnd };
var newSegs = [];
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
var seg = segs_1[_i];
var eventRange = seg.eventRange;
var origRange = eventRange.range;
var slicedRange = core.intersectRanges(origRange, dayRange);
if (slicedRange) {
newSegs.push(__assign({}, seg, { eventRange: {
def: eventRange.def,
ui: __assign({}, eventRange.ui, { durationEditable: false }),
instance: eventRange.instance,
range: slicedRange
}, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() }));
}
}
return newSegs;
};
// Generates the text that should be inside a "more" link, given the number of events it represents
DayGrid.prototype.getMoreLinkText = function (num) {
var opt = this.context.options.eventLimitText;
if (typeof opt === 'function') {
return opt(num);
}
else {
return '+' + num + ' ' + opt;
}
};
// Returns segments within a given cell.
// If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs.
DayGrid.prototype.getCellSegs = function (row, col, startLevel) {
var segMatrix = this.eventRenderer.rowStructs[row].segMatrix;
var level = startLevel || 0;
var segs = [];
var seg;
while (level < segMatrix.length) {
seg = segMatrix[level][col];
if (seg) {
segs.push(seg);
}
level++;
}
return segs;
};
return DayGrid;
}(core.DateComponent));
var WEEK_NUM_FORMAT$1 = core.createFormatter({ week: 'numeric' });
/* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
----------------------------------------------------------------------------------------------------------------------*/
// It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
// It is responsible for managing width/height.
var AbstractDayGridView = /** @class */ (function (_super) {
__extends(AbstractDayGridView, _super);
function AbstractDayGridView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.processOptions = core.memoize(_this._processOptions);
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
/* Header Rendering
------------------------------------------------------------------------------------------------------------------*/
// Generates the HTML that will go before the day-of week header cells
_this.renderHeadIntroHtml = function () {
var _a = _this.context, theme = _a.theme, options = _a.options;
if (_this.colWeekNumbersVisible) {
return '' +
' | ';
}
return '';
};
/* Day Grid Rendering
------------------------------------------------------------------------------------------------------------------*/
// Generates the HTML that will go before content-skeleton cells that display the day/week numbers
_this.renderDayGridNumberIntroHtml = function (row, dayGrid) {
var _a = _this.context, options = _a.options, dateEnv = _a.dateEnv;
var weekStart = dayGrid.props.cells[row][0].date;
if (_this.colWeekNumbersVisible) {
return '' +
'
' +
core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths
options, dateEnv, { date: weekStart, type: 'week', forceOff: dayGrid.colCnt === 1 }, dateEnv.format(weekStart, WEEK_NUM_FORMAT$1) // inner HTML
) +
' | ';
}
return '';
};
// Generates the HTML that goes before the day bg cells for each day-row
_this.renderDayGridBgIntroHtml = function () {
var theme = _this.context.theme;
if (_this.colWeekNumbersVisible) {
return '
| ';
}
return '';
};
// Generates the HTML that goes before every other type of row generated by DayGrid.
// Affects mirror-skeleton and highlight-skeleton rows.
_this.renderDayGridIntroHtml = function () {
if (_this.colWeekNumbersVisible) {
return '
| ';
}
return '';
};
return _this;
}
AbstractDayGridView.prototype._processOptions = function (options) {
if (options.weekNumbers) {
if (options.weekNumbersWithinDays) {
this.cellWeekNumbersVisible = true;
this.colWeekNumbersVisible = false;
}
else {
this.cellWeekNumbersVisible = false;
this.colWeekNumbersVisible = true;
}
}
else {
this.colWeekNumbersVisible = false;
this.cellWeekNumbersVisible = false;
}
};
AbstractDayGridView.prototype.render = function (props, context) {
_super.prototype.render.call(this, props, context);
this.processOptions(context.options);
this.renderSkeleton(context);
};
AbstractDayGridView.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.renderSkeleton.unrender();
};
AbstractDayGridView.prototype._renderSkeleton = function (context) {
this.el.classList.add('fc-dayGrid-view');
this.el.innerHTML = this.renderSkeletonHtml();
this.scroller = new core.ScrollComponent('hidden', // overflow x
'auto' // overflow y
);
var dayGridContainerEl = this.scroller.el;
this.el.querySelector('.fc-body > tr > td').appendChild(dayGridContainerEl);
dayGridContainerEl.classList.add('fc-day-grid-container');
var dayGridEl = core.createElement('div', { className: 'fc-day-grid' });
dayGridContainerEl.appendChild(dayGridEl);
this.dayGrid = new DayGrid(dayGridEl, {
renderNumberIntroHtml: this.renderDayGridNumberIntroHtml,
renderBgIntroHtml: this.renderDayGridBgIntroHtml,
renderIntroHtml: this.renderDayGridIntroHtml,
colWeekNumbersVisible: this.colWeekNumbersVisible,
cellWeekNumbersVisible: this.cellWeekNumbersVisible
});
};
AbstractDayGridView.prototype._unrenderSkeleton = function () {
this.el.classList.remove('fc-dayGrid-view');
this.dayGrid.destroy();
this.scroller.destroy();
};
// Builds the HTML skeleton for the view.
// The day-grid component will render inside of a container defined by this HTML.
AbstractDayGridView.prototype.renderSkeletonHtml = function () {
var _a = this.context, theme = _a.theme, options = _a.options;
return '' +
'
' +
(options.columnHeader ?
'' +
'' +
'' +
'
' +
'' :
'') +
'' +
'' +
' | ' +
'
' +
'' +
'
';
};
// Generates an HTML attribute string for setting the width of the week number column, if it is known
AbstractDayGridView.prototype.weekNumberStyleAttr = function () {
if (this.weekNumberWidth != null) {
return 'style="width:' + this.weekNumberWidth + 'px"';
}
return '';
};
// Determines whether each row should have a constant height
AbstractDayGridView.prototype.hasRigidRows = function () {
var eventLimit = this.context.options.eventLimit;
return eventLimit && typeof eventLimit !== 'number';
};
/* Dimensions
------------------------------------------------------------------------------------------------------------------*/
AbstractDayGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
_super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
this.dayGrid.updateSize(isResize);
};
// Refreshes the horizontal dimensions of the view
AbstractDayGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
var dayGrid = this.dayGrid;
var eventLimit = this.context.options.eventLimit;
var headRowEl = this.header ? this.header.el : null; // HACK
var scrollerHeight;
var scrollbarWidths;
// hack to give the view some height prior to dayGrid's columns being rendered
// TODO: separate setting height from scroller VS dayGrid.
if (!dayGrid.rowEls) {
if (!isAuto) {
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.scroller.setHeight(scrollerHeight);
}
return;
}
if (this.colWeekNumbersVisible) {
// Make sure all week number cells running down the side have the same width.
this.weekNumberWidth = core.matchCellWidths(core.findElements(this.el, '.fc-week-number'));
}
// reset all heights to be natural
this.scroller.clear();
if (headRowEl) {
core.uncompensateScroll(headRowEl);
}
dayGrid.removeSegPopover(); // kill the "more" popover if displayed
// is the event limit a constant level number?
if (eventLimit && typeof eventLimit === 'number') {
dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
}
// distribute the height to the rows
// (viewHeight is a "recommended" value if isAuto)
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.setGridHeight(scrollerHeight, isAuto);
// is the event limit dynamically calculated?
if (eventLimit && typeof eventLimit !== 'number') {
dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
}
if (!isAuto) { // should we force dimensions of the scroll container?
this.scroller.setHeight(scrollerHeight);
scrollbarWidths = this.scroller.getScrollbarWidths();
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
if (headRowEl) {
core.compensateScroll(headRowEl, scrollbarWidths);
}
// doing the scrollbar compensation might have created text overflow which created more height. redo
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.scroller.setHeight(scrollerHeight);
}
// guarantees the same scrollbar widths
this.scroller.lockOverflow(scrollbarWidths);
}
};
// given a desired total height of the view, returns what the height of the scroller should be
AbstractDayGridView.prototype.computeScrollerHeight = function (viewHeight) {
return viewHeight -
core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
};
// Sets the height of just the DayGrid component in this view
AbstractDayGridView.prototype.setGridHeight = function (height, isAuto) {
if (this.context.options.monthMode) {
// if auto, make the height of each row the height that it would be if there were 6 weeks
if (isAuto) {
height *= this.dayGrid.rowCnt / 6;
}
core.distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows
}
else {
if (isAuto) {
core.undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
}
else {
core.distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
}
}
};
/* Scroll
------------------------------------------------------------------------------------------------------------------*/
AbstractDayGridView.prototype.computeDateScroll = function (duration) {
return { top: 0 };
};
AbstractDayGridView.prototype.queryDateScroll = function () {
return { top: this.scroller.getScrollTop() };
};
AbstractDayGridView.prototype.applyDateScroll = function (scroll) {
if (scroll.top !== undefined) {
this.scroller.setScrollTop(scroll.top);
}
};
return AbstractDayGridView;
}(core.View));
AbstractDayGridView.prototype.dateProfileGeneratorClass = DayGridDateProfileGenerator;
var SimpleDayGrid = /** @class */ (function (_super) {
__extends(SimpleDayGrid, _super);
function SimpleDayGrid(dayGrid) {
var _this = _super.call(this, dayGrid.el) || this;
_this.slicer = new DayGridSlicer();
_this.dayGrid = dayGrid;
return _this;
}
SimpleDayGrid.prototype.firstContext = function (context) {
context.calendar.registerInteractiveComponent(this, { el: this.dayGrid.el });
};
SimpleDayGrid.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.context.calendar.unregisterInteractiveComponent(this);
};
SimpleDayGrid.prototype.render = function (props, context) {
var dayGrid = this.dayGrid;
var dateProfile = props.dateProfile, dayTable = props.dayTable;
dayGrid.receiveContext(context); // hack because context is used in sliceProps
dayGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, props.nextDayThreshold, context.calendar, dayGrid, dayTable), { dateProfile: dateProfile, cells: dayTable.cells, isRigid: props.isRigid }), context);
};
SimpleDayGrid.prototype.buildPositionCaches = function () {
this.dayGrid.buildPositionCaches();
};
SimpleDayGrid.prototype.queryHit = function (positionLeft, positionTop) {
var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop);
if (rawHit) {
return {
component: this.dayGrid,
dateSpan: rawHit.dateSpan,
dayEl: rawHit.dayEl,
rect: {
left: rawHit.relativeRect.left,
right: rawHit.relativeRect.right,
top: rawHit.relativeRect.top,
bottom: rawHit.relativeRect.bottom
},
layer: 0
};
}
};
return SimpleDayGrid;
}(core.DateComponent));
var DayGridSlicer = /** @class */ (function (_super) {
__extends(DayGridSlicer, _super);
function DayGridSlicer() {
return _super !== null && _super.apply(this, arguments) || this;
}
DayGridSlicer.prototype.sliceRange = function (dateRange, dayTable) {
return dayTable.sliceRange(dateRange);
};
return DayGridSlicer;
}(core.Slicer));
var DayGridView = /** @class */ (function (_super) {
__extends(DayGridView, _super);
function DayGridView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.buildDayTable = core.memoize(buildDayTable);
return _this;
}
DayGridView.prototype.render = function (props, context) {
_super.prototype.render.call(this, props, context); // will call _renderSkeleton/_unrenderSkeleton
var dateProfile = this.props.dateProfile;
var dayTable = this.dayTable =
this.buildDayTable(dateProfile, props.dateProfileGenerator);
if (this.header) {
this.header.receiveProps({
dateProfile: dateProfile,
dates: dayTable.headerDates,
datesRepDistinctDays: dayTable.rowCnt === 1,
renderIntroHtml: this.renderHeadIntroHtml
}, context);
}
this.simpleDayGrid.receiveProps({
dateProfile: dateProfile,
dayTable: dayTable,
businessHours: props.businessHours,
dateSelection: props.dateSelection,
eventStore: props.eventStore,
eventUiBases: props.eventUiBases,
eventSelection: props.eventSelection,
eventDrag: props.eventDrag,
eventResize: props.eventResize,
isRigid: this.hasRigidRows(),
nextDayThreshold: this.context.nextDayThreshold
}, context);
};
DayGridView.prototype._renderSkeleton = function (context) {
_super.prototype._renderSkeleton.call(this, context);
if (context.options.columnHeader) {
this.header = new core.DayHeader(this.el.querySelector('.fc-head-container'));
}
this.simpleDayGrid = new SimpleDayGrid(this.dayGrid);
};
DayGridView.prototype._unrenderSkeleton = function () {
_super.prototype._unrenderSkeleton.call(this);
if (this.header) {
this.header.destroy();
}
this.simpleDayGrid.destroy();
};
return DayGridView;
}(AbstractDayGridView));
function buildDayTable(dateProfile, dateProfileGenerator) {
var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
return new core.DayTable(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
}
var main = core.createPlugin({
defaultView: 'dayGridMonth',
views: {
dayGrid: DayGridView,
dayGridDay: {
type: 'dayGrid',
duration: { days: 1 }
},
dayGridWeek: {
type: 'dayGrid',
duration: { weeks: 1 }
},
dayGridMonth: {
type: 'dayGrid',
duration: { months: 1 },
monthMode: true,
fixedWeekCount: true
}
}
});
exports.AbstractDayGridView = AbstractDayGridView;
exports.DayBgRow = DayBgRow;
exports.DayGrid = DayGrid;
exports.DayGridSlicer = DayGridSlicer;
exports.DayGridView = DayGridView;
exports.SimpleDayGrid = SimpleDayGrid;
exports.buildBasicDayTable = buildDayTable;
exports.default = main;
Object.defineProperty(exports, '__esModule', { value: true });
}));
/*!
FullCalendar Google Calendar Plugin v4.4.0
Docs & License: https://fullcalendar.io/
(c) 2019 Adam Shaw
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
(global = global || self, factory(global.FullCalendarGoogleCalendar = {}, global.FullCalendar));
}(this, function (exports, core) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
// TODO: expose somehow
var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars';
var STANDARD_PROPS = {
url: String,
googleCalendarApiKey: String,
googleCalendarId: String,
googleCalendarApiBase: String,
data: null
};
var eventSourceDef = {
parseMeta: function (raw) {
if (typeof raw === 'string') {
raw = { url: raw };
}
if (typeof raw === 'object') {
var standardProps = core.refineProps(raw, STANDARD_PROPS);
if (!standardProps.googleCalendarId && standardProps.url) {
standardProps.googleCalendarId = parseGoogleCalendarId(standardProps.url);
}
delete standardProps.url;
if (standardProps.googleCalendarId) {
return standardProps;
}
}
return null;
},
fetch: function (arg, onSuccess, onFailure) {
var calendar = arg.calendar;
var meta = arg.eventSource.meta;
var apiKey = meta.googleCalendarApiKey || calendar.opt('googleCalendarApiKey');
if (!apiKey) {
onFailure({
message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/'
});
}
else {
var url = buildUrl(meta);
var requestParams_1 = buildRequestParams(arg.range, apiKey, meta.data, calendar.dateEnv);
core.requestJson('GET', url, requestParams_1, function (body, xhr) {
if (body.error) {
onFailure({
message: 'Google Calendar API: ' + body.error.message,
errors: body.error.errors,
xhr: xhr
});
}
else {
onSuccess({
rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone),
xhr: xhr
});
}
}, function (message, xhr) {
onFailure({ message: message, xhr: xhr });
});
}
}
};
function parseGoogleCalendarId(url) {
var match;
// detect if the ID was specified as a single string.
// will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) {
return url;
}
else if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) ||
(match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url))) {
return decodeURIComponent(match[1]);
}
}
function buildUrl(meta) {
var apiBase = meta.googleCalendarApiBase;
if (!apiBase) {
apiBase = API_BASE;
}
return apiBase + '/' + encodeURIComponent(meta.googleCalendarId) + '/events';
}
function buildRequestParams(range, apiKey, extraParams, dateEnv) {
var params;
var startStr;
var endStr;
if (dateEnv.canComputeOffset) {
// strings will naturally have offsets, which GCal needs
startStr = dateEnv.formatIso(range.start);
endStr = dateEnv.formatIso(range.end);
}
else {
// when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day
// from the UTC day-start to guarantee we're getting all the events
// (start/end will be UTC-coerced dates, so toISOString is okay)
startStr = core.addDays(range.start, -1).toISOString();
endStr = core.addDays(range.end, 1).toISOString();
}
params = __assign({}, (extraParams || {}), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 });
if (dateEnv.timeZone !== 'local') {
params.timeZone = dateEnv.timeZone;
}
return params;
}
function gcalItemsToRawEventDefs(items, gcalTimezone) {
return items.map(function (item) {
return gcalItemToRawEventDef(item, gcalTimezone);
});
}
function gcalItemToRawEventDef(item, gcalTimezone) {
var url = item.htmlLink || null;
// make the URLs for each event show times in the correct timezone
if (url && gcalTimezone) {
url = injectQsComponent(url, 'ctz=' + gcalTimezone);
}
return {
id: item.id,
title: item.summary,
start: item.start.dateTime || item.start.date,
end: item.end.dateTime || item.end.date,
url: url,
location: item.location,
description: item.description
};
}
// Injects a string like "arg=value" into the querystring of a URL
// TODO: move to a general util file?
function injectQsComponent(url, component) {
// inject it after the querystring but before the fragment
return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) {
return (qs ? qs + '&' : '?') + component + hash;
});
}
var main = core.createPlugin({
eventSourceDefs: [eventSourceDef]
});
exports.default = main;
Object.defineProperty(exports, '__esModule', { value: true });
}));
/*!
FullCalendar Interaction Plugin v4.4.0
Docs & License: https://fullcalendar.io/
(c) 2019 Adam Shaw
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
(global = global || self, factory(global.FullCalendarInteraction = {}, global.FullCalendar));
}(this, function (exports, core) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
core.config.touchMouseIgnoreWait = 500;
var ignoreMouseDepth = 0;
var listenerCnt = 0;
var isWindowTouchMoveCancelled = false;
/*
Uses a "pointer" abstraction, which monitors UI events for both mouse and touch.
Tracks when the pointer "drags" on a certain element, meaning down+move+up.
Also, tracks if there was touch-scrolling.
Also, can prevent touch-scrolling from happening.
Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement.
emits:
- pointerdown
- pointermove
- pointerup
*/
var PointerDragging = /** @class */ (function () {
function PointerDragging(containerEl) {
var _this = this;
this.subjectEl = null;
this.downEl = null;
// options that can be directly assigned by caller
this.selector = ''; // will cause subjectEl in all emitted events to be this element
this.handleSelector = '';
this.shouldIgnoreMove = false;
this.shouldWatchScroll = true; // for simulating pointermove on scroll
// internal states
this.isDragging = false;
this.isTouchDragging = false;
this.wasTouchScroll = false;
// Mouse
// ----------------------------------------------------------------------------------------------------
this.handleMouseDown = function (ev) {
if (!_this.shouldIgnoreMouse() &&
isPrimaryMouseButton(ev) &&
_this.tryStart(ev)) {
var pev = _this.createEventFromMouse(ev, true);
_this.emitter.trigger('pointerdown', pev);
_this.initScrollWatch(pev);
if (!_this.shouldIgnoreMove) {
document.addEventListener('mousemove', _this.handleMouseMove);
}
document.addEventListener('mouseup', _this.handleMouseUp);
}
};
this.handleMouseMove = function (ev) {
var pev = _this.createEventFromMouse(ev);
_this.recordCoords(pev);
_this.emitter.trigger('pointermove', pev);
};
this.handleMouseUp = function (ev) {
document.removeEventListener('mousemove', _this.handleMouseMove);
document.removeEventListener('mouseup', _this.handleMouseUp);
_this.emitter.trigger('pointerup', _this.createEventFromMouse(ev));
_this.cleanup(); // call last so that pointerup has access to props
};
// Touch
// ----------------------------------------------------------------------------------------------------
this.handleTouchStart = function (ev) {
if (_this.tryStart(ev)) {
_this.isTouchDragging = true;
var pev = _this.createEventFromTouch(ev, true);
_this.emitter.trigger('pointerdown', pev);
_this.initScrollWatch(pev);
// unlike mouse, need to attach to target, not document
// https://stackoverflow.com/a/45760014
var target = ev.target;
if (!_this.shouldIgnoreMove) {
target.addEventListener('touchmove', _this.handleTouchMove);
}
target.addEventListener('touchend', _this.handleTouchEnd);
target.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end
// attach a handler to get called when ANY scroll action happens on the page.
// this was impossible to do with normal on/off because 'scroll' doesn't bubble.
// http://stackoverflow.com/a/32954565/96342
window.addEventListener('scroll', _this.handleTouchScroll, true // useCapture
);
}
};
this.handleTouchMove = function (ev) {
var pev = _this.createEventFromTouch(ev);
_this.recordCoords(pev);
_this.emitter.trigger('pointermove', pev);
};
this.handleTouchEnd = function (ev) {
if (_this.isDragging) { // done to guard against touchend followed by touchcancel
var target = ev.target;
target.removeEventListener('touchmove', _this.handleTouchMove);
target.removeEventListener('touchend', _this.handleTouchEnd);
target.removeEventListener('touchcancel', _this.handleTouchEnd);
window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true
_this.emitter.trigger('pointerup', _this.createEventFromTouch(ev));
_this.cleanup(); // call last so that pointerup has access to props
_this.isTouchDragging = false;
startIgnoringMouse();
}
};
this.handleTouchScroll = function () {
_this.wasTouchScroll = true;
};
this.handleScroll = function (ev) {
if (!_this.shouldIgnoreMove) {
var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX;
var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY;
_this.emitter.trigger('pointermove', {
origEvent: ev,
isTouch: _this.isTouchDragging,
subjectEl: _this.subjectEl,
pageX: pageX,
pageY: pageY,
deltaX: pageX - _this.origPageX,
deltaY: pageY - _this.origPageY
});
}
};
this.containerEl = containerEl;
this.emitter = new core.EmitterMixin();
containerEl.addEventListener('mousedown', this.handleMouseDown);
containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true });
listenerCreated();
}
PointerDragging.prototype.destroy = function () {
this.containerEl.removeEventListener('mousedown', this.handleMouseDown);
this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
listenerDestroyed();
};
PointerDragging.prototype.tryStart = function (ev) {
var subjectEl = this.querySubjectEl(ev);
var downEl = ev.target;
if (subjectEl &&
(!this.handleSelector || core.elementClosest(downEl, this.handleSelector))) {
this.subjectEl = subjectEl;
this.downEl = downEl;
this.isDragging = true; // do this first so cancelTouchScroll will work
this.wasTouchScroll = false;
return true;
}
return false;
};
PointerDragging.prototype.cleanup = function () {
isWindowTouchMoveCancelled = false;
this.isDragging = false;
this.subjectEl = null;
this.downEl = null;
// keep wasTouchScroll around for later access
this.destroyScrollWatch();
};
PointerDragging.prototype.querySubjectEl = function (ev) {
if (this.selector) {
return core.elementClosest(ev.target, this.selector);
}
else {
return this.containerEl;
}
};
PointerDragging.prototype.shouldIgnoreMouse = function () {
return ignoreMouseDepth || this.isTouchDragging;
};
// can be called by user of this class, to cancel touch-based scrolling for the current drag
PointerDragging.prototype.cancelTouchScroll = function () {
if (this.isDragging) {
isWindowTouchMoveCancelled = true;
}
};
// Scrolling that simulates pointermoves
// ----------------------------------------------------------------------------------------------------
PointerDragging.prototype.initScrollWatch = function (ev) {
if (this.shouldWatchScroll) {
this.recordCoords(ev);
window.addEventListener('scroll', this.handleScroll, true); // useCapture=true
}
};
PointerDragging.prototype.recordCoords = function (ev) {
if (this.shouldWatchScroll) {
this.prevPageX = ev.pageX;
this.prevPageY = ev.pageY;
this.prevScrollX = window.pageXOffset;
this.prevScrollY = window.pageYOffset;
}
};
PointerDragging.prototype.destroyScrollWatch = function () {
if (this.shouldWatchScroll) {
window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true
}
};
// Event Normalization
// ----------------------------------------------------------------------------------------------------
PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) {
var deltaX = 0;
var deltaY = 0;
// TODO: repeat code
if (isFirst) {
this.origPageX = ev.pageX;
this.origPageY = ev.pageY;
}
else {
deltaX = ev.pageX - this.origPageX;
deltaY = ev.pageY - this.origPageY;
}
return {
origEvent: ev,
isTouch: false,
subjectEl: this.subjectEl,
pageX: ev.pageX,
pageY: ev.pageY,
deltaX: deltaX,
deltaY: deltaY
};
};
PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) {
var touches = ev.touches;
var pageX;
var pageY;
var deltaX = 0;
var deltaY = 0;
// if touch coords available, prefer,
// because FF would give bad ev.pageX ev.pageY
if (touches && touches.length) {
pageX = touches[0].pageX;
pageY = touches[0].pageY;
}
else {
pageX = ev.pageX;
pageY = ev.pageY;
}
// TODO: repeat code
if (isFirst) {
this.origPageX = pageX;
this.origPageY = pageY;
}
else {
deltaX = pageX - this.origPageX;
deltaY = pageY - this.origPageY;
}
return {
origEvent: ev,
isTouch: true,
subjectEl: this.subjectEl,
pageX: pageX,
pageY: pageY,
deltaX: deltaX,
deltaY: deltaY
};
};
return PointerDragging;
}());
// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
function isPrimaryMouseButton(ev) {
return ev.button === 0 && !ev.ctrlKey;
}
// Ignoring fake mouse events generated by touch
// ----------------------------------------------------------------------------------------------------
function startIgnoringMouse() {
ignoreMouseDepth++;
setTimeout(function () {
ignoreMouseDepth--;
}, core.config.touchMouseIgnoreWait);
}
// We want to attach touchmove as early as possible for Safari
// ----------------------------------------------------------------------------------------------------
function listenerCreated() {
if (!(listenerCnt++)) {
window.addEventListener('touchmove', onWindowTouchMove, { passive: false });
}
}
function listenerDestroyed() {
if (!(--listenerCnt)) {
window.removeEventListener('touchmove', onWindowTouchMove, { passive: false });
}
}
function onWindowTouchMove(ev) {
if (isWindowTouchMoveCancelled) {
ev.preventDefault();
}
}
/*
An effect in which an element follows the movement of a pointer across the screen.
The moving element is a clone of some other element.
Must call start + handleMove + stop.
*/
var ElementMirror = /** @class */ (function () {
function ElementMirror() {
this.isVisible = false; // must be explicitly enabled
this.sourceEl = null;
this.mirrorEl = null;
this.sourceElRect = null; // screen coords relative to viewport
// options that can be set directly by caller
this.parentNode = document.body;
this.zIndex = 9999;
this.revertDuration = 0;
}
ElementMirror.prototype.start = function (sourceEl, pageX, pageY) {
this.sourceEl = sourceEl;
this.sourceElRect = this.sourceEl.getBoundingClientRect();
this.origScreenX = pageX - window.pageXOffset;
this.origScreenY = pageY - window.pageYOffset;
this.deltaX = 0;
this.deltaY = 0;
this.updateElPosition();
};
ElementMirror.prototype.handleMove = function (pageX, pageY) {
this.deltaX = (pageX - window.pageXOffset) - this.origScreenX;
this.deltaY = (pageY - window.pageYOffset) - this.origScreenY;
this.updateElPosition();
};
// can be called before start
ElementMirror.prototype.setIsVisible = function (bool) {
if (bool) {
if (!this.isVisible) {
if (this.mirrorEl) {
this.mirrorEl.style.display = '';
}
this.isVisible = bool; // needs to happen before updateElPosition
this.updateElPosition(); // because was not updating the position while invisible
}
}
else {
if (this.isVisible) {
if (this.mirrorEl) {
this.mirrorEl.style.display = 'none';
}
this.isVisible = bool;
}
}
};
// always async
ElementMirror.prototype.stop = function (needsRevertAnimation, callback) {
var _this = this;
var done = function () {
_this.cleanup();
callback();
};
if (needsRevertAnimation &&
this.mirrorEl &&
this.isVisible &&
this.revertDuration && // if 0, transition won't work
(this.deltaX || this.deltaY) // if same coords, transition won't work
) {
this.doRevertAnimation(done, this.revertDuration);
}
else {
setTimeout(done, 0);
}
};
ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) {
var mirrorEl = this.mirrorEl;
var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened
mirrorEl.style.transition =
'top ' + revertDuration + 'ms,' +
'left ' + revertDuration + 'ms';
core.applyStyle(mirrorEl, {
left: finalSourceElRect.left,
top: finalSourceElRect.top
});
core.whenTransitionDone(mirrorEl, function () {
mirrorEl.style.transition = '';
callback();
});
};
ElementMirror.prototype.cleanup = function () {
if (this.mirrorEl) {
core.removeElement(this.mirrorEl);
this.mirrorEl = null;
}
this.sourceEl = null;
};
ElementMirror.prototype.updateElPosition = function () {
if (this.sourceEl && this.isVisible) {
core.applyStyle(this.getMirrorEl(), {
left: this.sourceElRect.left + this.deltaX,
top: this.sourceElRect.top + this.deltaY
});
}
};
ElementMirror.prototype.getMirrorEl = function () {
var sourceElRect = this.sourceElRect;
var mirrorEl = this.mirrorEl;
if (!mirrorEl) {
mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true
// we don't want long taps or any mouse interaction causing selection/menus.
// would use preventSelection(), but that prevents selectstart, causing problems.
mirrorEl.classList.add('fc-unselectable');
mirrorEl.classList.add('fc-dragging');
core.applyStyle(mirrorEl, {
position: 'fixed',
zIndex: this.zIndex,
visibility: '',
boxSizing: 'border-box',
width: sourceElRect.right - sourceElRect.left,
height: sourceElRect.bottom - sourceElRect.top,
right: 'auto',
bottom: 'auto',
margin: 0
});
this.parentNode.appendChild(mirrorEl);
}
return mirrorEl;
};
return ElementMirror;
}());
/*
Is a cache for a given element's scroll information (all the info that ScrollController stores)
in addition the "client rectangle" of the element.. the area within the scrollbars.
The cache can be in one of two modes:
- doesListening:false - ignores when the container is scrolled by someone else
- doesListening:true - watch for scrolling and update the cache
*/
var ScrollGeomCache = /** @class */ (function (_super) {
__extends(ScrollGeomCache, _super);
function ScrollGeomCache(scrollController, doesListening) {
var _this = _super.call(this) || this;
_this.handleScroll = function () {
_this.scrollTop = _this.scrollController.getScrollTop();
_this.scrollLeft = _this.scrollController.getScrollLeft();
_this.handleScrollChange();
};
_this.scrollController = scrollController;
_this.doesListening = doesListening;
_this.scrollTop = _this.origScrollTop = scrollController.getScrollTop();
_this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft();
_this.scrollWidth = scrollController.getScrollWidth();
_this.scrollHeight = scrollController.getScrollHeight();
_this.clientWidth = scrollController.getClientWidth();
_this.clientHeight = scrollController.getClientHeight();
_this.clientRect = _this.computeClientRect(); // do last in case it needs cached values
if (_this.doesListening) {
_this.getEventTarget().addEventListener('scroll', _this.handleScroll);
}
return _this;
}
ScrollGeomCache.prototype.destroy = function () {
if (this.doesListening) {
this.getEventTarget().removeEventListener('scroll', this.handleScroll);
}
};
ScrollGeomCache.prototype.getScrollTop = function () {
return this.scrollTop;
};
ScrollGeomCache.prototype.getScrollLeft = function () {
return this.scrollLeft;
};
ScrollGeomCache.prototype.setScrollTop = function (top) {
this.scrollController.setScrollTop(top);
if (!this.doesListening) {
// we are not relying on the element to normalize out-of-bounds scroll values
// so we need to sanitize ourselves
this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0);
this.handleScrollChange();
}
};
ScrollGeomCache.prototype.setScrollLeft = function (top) {
this.scrollController.setScrollLeft(top);
if (!this.doesListening) {
// we are not relying on the element to normalize out-of-bounds scroll values
// so we need to sanitize ourselves
this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0);
this.handleScrollChange();
}
};
ScrollGeomCache.prototype.getClientWidth = function () {
return this.clientWidth;
};
ScrollGeomCache.prototype.getClientHeight = function () {
return this.clientHeight;
};
ScrollGeomCache.prototype.getScrollWidth = function () {
return this.scrollWidth;
};
ScrollGeomCache.prototype.getScrollHeight = function () {
return this.scrollHeight;
};
ScrollGeomCache.prototype.handleScrollChange = function () {
};
return ScrollGeomCache;
}(core.ScrollController));
var ElementScrollGeomCache = /** @class */ (function (_super) {
__extends(ElementScrollGeomCache, _super);
function ElementScrollGeomCache(el, doesListening) {
return _super.call(this, new core.ElementScrollController(el), doesListening) || this;
}
ElementScrollGeomCache.prototype.getEventTarget = function () {
return this.scrollController.el;
};
ElementScrollGeomCache.prototype.computeClientRect = function () {
return core.computeInnerRect(this.scrollController.el);
};
return ElementScrollGeomCache;
}(ScrollGeomCache));
var WindowScrollGeomCache = /** @class */ (function (_super) {
__extends(WindowScrollGeomCache, _super);
function WindowScrollGeomCache(doesListening) {
return _super.call(this, new core.WindowScrollController(), doesListening) || this;
}
WindowScrollGeomCache.prototype.getEventTarget = function () {
return window;
};
WindowScrollGeomCache.prototype.computeClientRect = function () {
return {
left: this.scrollLeft,
right: this.scrollLeft + this.clientWidth,
top: this.scrollTop,
bottom: this.scrollTop + this.clientHeight
};
};
// the window is the only scroll object that changes it's rectangle relative
// to the document's topleft as it scrolls
WindowScrollGeomCache.prototype.handleScrollChange = function () {
this.clientRect = this.computeClientRect();
};
return WindowScrollGeomCache;
}(ScrollGeomCache));
// If available we are using native "performance" API instead of "Date"
// Read more about it on MDN:
// https://developer.mozilla.org/en-US/docs/Web/API/Performance
var getTime = typeof performance === 'function' ? performance.now : Date.now;
/*
For a pointer interaction, automatically scrolls certain scroll containers when the pointer
approaches the edge.
The caller must call start + handleMove + stop.
*/
var AutoScroller = /** @class */ (function () {
function AutoScroller() {
var _this = this;
// options that can be set by caller
this.isEnabled = true;
this.scrollQuery = [window, '.fc-scroller'];
this.edgeThreshold = 50; // pixels
this.maxVelocity = 300; // pixels per second
// internal state
this.pointerScreenX = null;
this.pointerScreenY = null;
this.isAnimating = false;
this.scrollCaches = null;
// protect against the initial pointerdown being too close to an edge and starting the scroll
this.everMovedUp = false;
this.everMovedDown = false;
this.everMovedLeft = false;
this.everMovedRight = false;
this.animate = function () {
if (_this.isAnimating) { // wasn't cancelled between animation calls
var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset);
if (edge) {
var now = getTime();
_this.handleSide(edge, (now - _this.msSinceRequest) / 1000);
_this.requestAnimation(now);
}
else {
_this.isAnimating = false; // will stop animation
}
}
};
}
AutoScroller.prototype.start = function (pageX, pageY) {
if (this.isEnabled) {
this.scrollCaches = this.buildCaches();
this.pointerScreenX = null;
this.pointerScreenY = null;
this.everMovedUp = false;
this.everMovedDown = false;
this.everMovedLeft = false;
this.everMovedRight = false;
this.handleMove(pageX, pageY);
}
};
AutoScroller.prototype.handleMove = function (pageX, pageY) {
if (this.isEnabled) {
var pointerScreenX = pageX - window.pageXOffset;
var pointerScreenY = pageY - window.pageYOffset;
var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY;
var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX;
if (yDelta < 0) {
this.everMovedUp = true;
}
else if (yDelta > 0) {
this.everMovedDown = true;
}
if (xDelta < 0) {
this.everMovedLeft = true;
}
else if (xDelta > 0) {
this.everMovedRight = true;
}
this.pointerScreenX = pointerScreenX;
this.pointerScreenY = pointerScreenY;
if (!this.isAnimating) {
this.isAnimating = true;
this.requestAnimation(getTime());
}
}
};
AutoScroller.prototype.stop = function () {
if (this.isEnabled) {
this.isAnimating = false; // will stop animation
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
scrollCache.destroy();
}
this.scrollCaches = null;
}
};
AutoScroller.prototype.requestAnimation = function (now) {
this.msSinceRequest = now;
requestAnimationFrame(this.animate);
};
AutoScroller.prototype.handleSide = function (edge, seconds) {
var scrollCache = edge.scrollCache;
var edgeThreshold = this.edgeThreshold;
var invDistance = edgeThreshold - edge.distance;
var velocity = // the closer to the edge, the faster we scroll
(invDistance * invDistance) / (edgeThreshold * edgeThreshold) * // quadratic
this.maxVelocity * seconds;
var sign = 1;
switch (edge.name) {
case 'left':
sign = -1;
// falls through
case 'right':
scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign);
break;
case 'top':
sign = -1;
// falls through
case 'bottom':
scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign);
break;
}
};
// left/top are relative to document topleft
AutoScroller.prototype.computeBestEdge = function (left, top) {
var edgeThreshold = this.edgeThreshold;
var bestSide = null;
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
var rect = scrollCache.clientRect;
var leftDist = left - rect.left;
var rightDist = rect.right - left;
var topDist = top - rect.top;
var bottomDist = rect.bottom - top;
// completely within the rect?
if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) {
if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() &&
(!bestSide || bestSide.distance > topDist)) {
bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist };
}
if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() &&
(!bestSide || bestSide.distance > bottomDist)) {
bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist };
}
if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() &&
(!bestSide || bestSide.distance > leftDist)) {
bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist };
}
if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() &&
(!bestSide || bestSide.distance > rightDist)) {
bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist };
}
}
}
return bestSide;
};
AutoScroller.prototype.buildCaches = function () {
return this.queryScrollEls().map(function (el) {
if (el === window) {
return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls
}
else {
return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls
}
});
};
AutoScroller.prototype.queryScrollEls = function () {
var els = [];
for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) {
var query = _a[_i];
if (typeof query === 'object') {
els.push(query);
}
else {
els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query)));
}
}
return els;
};
return AutoScroller;
}());
/*
Monitors dragging on an element. Has a number of high-level features:
- minimum distance required before dragging
- minimum wait time ("delay") before dragging
- a mirror element that follows the pointer
*/
var FeaturefulElementDragging = /** @class */ (function (_super) {
__extends(FeaturefulElementDragging, _super);
function FeaturefulElementDragging(containerEl) {
var _this = _super.call(this, containerEl) || this;
// options that can be directly set by caller
// the caller can also set the PointerDragging's options as well
_this.delay = null;
_this.minDistance = 0;
_this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag
_this.mirrorNeedsRevert = false;
_this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup
_this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation
_this.isDelayEnded = false;
_this.isDistanceSurpassed = false;
_this.delayTimeoutId = null;
_this.onPointerDown = function (ev) {
if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going
_this.isInteracting = true;
_this.isDelayEnded = false;
_this.isDistanceSurpassed = false;
core.preventSelection(document.body);
core.preventContextMenu(document.body);
// prevent links from being visited if there's an eventual drag.
// also prevents selection in older browsers (maybe?).
// not necessary for touch, besides, browser would complain about passiveness.
if (!ev.isTouch) {
ev.origEvent.preventDefault();
}
_this.emitter.trigger('pointerdown', ev);
if (!_this.pointer.shouldIgnoreMove) {
// actions related to initiating dragstart+dragmove+dragend...
_this.mirror.setIsVisible(false); // reset. caller must set-visible
_this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down
_this.startDelay(ev);
if (!_this.minDistance) {
_this.handleDistanceSurpassed(ev);
}
}
}
};
_this.onPointerMove = function (ev) {
if (_this.isInteracting) { // if false, still waiting for previous drag's revert
_this.emitter.trigger('pointermove', ev);
if (!_this.isDistanceSurpassed) {
var minDistance = _this.minDistance;
var distanceSq = void 0; // current distance from the origin, squared
var deltaX = ev.deltaX, deltaY = ev.deltaY;
distanceSq = deltaX * deltaX + deltaY * deltaY;
if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
_this.handleDistanceSurpassed(ev);
}
}
if (_this.isDragging) {
// a real pointer move? (not one simulated by scrolling)
if (ev.origEvent.type !== 'scroll') {
_this.mirror.handleMove(ev.pageX, ev.pageY);
_this.autoScroller.handleMove(ev.pageX, ev.pageY);
}
_this.emitter.trigger('dragmove', ev);
}
}
};
_this.onPointerUp = function (ev) {
if (_this.isInteracting) { // if false, still waiting for previous drag's revert
_this.isInteracting = false;
core.allowSelection(document.body);
core.allowContextMenu(document.body);
_this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert
if (_this.isDragging) {
_this.autoScroller.stop();
_this.tryStopDrag(ev); // which will stop the mirror
}
if (_this.delayTimeoutId) {
clearTimeout(_this.delayTimeoutId);
_this.delayTimeoutId = null;
}
}
};
var pointer = _this.pointer = new PointerDragging(containerEl);
pointer.emitter.on('pointerdown', _this.onPointerDown);
pointer.emitter.on('pointermove', _this.onPointerMove);
pointer.emitter.on('pointerup', _this.onPointerUp);
_this.mirror = new ElementMirror();
_this.autoScroller = new AutoScroller();
return _this;
}
FeaturefulElementDragging.prototype.destroy = function () {
this.pointer.destroy();
};
FeaturefulElementDragging.prototype.startDelay = function (ev) {
var _this = this;
if (typeof this.delay === 'number') {
this.delayTimeoutId = setTimeout(function () {
_this.delayTimeoutId = null;
_this.handleDelayEnd(ev);
}, this.delay); // not assignable to number!
}
else {
this.handleDelayEnd(ev);
}
};
FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) {
this.isDelayEnded = true;
this.tryStartDrag(ev);
};
FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) {
this.isDistanceSurpassed = true;
this.tryStartDrag(ev);
};
FeaturefulElementDragging.prototype.tryStartDrag = function (ev) {
if (this.isDelayEnded && this.isDistanceSurpassed) {
if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) {
this.isDragging = true;
this.mirrorNeedsRevert = false;
this.autoScroller.start(ev.pageX, ev.pageY);
this.emitter.trigger('dragstart', ev);
if (this.touchScrollAllowed === false) {
this.pointer.cancelTouchScroll();
}
}
}
};
FeaturefulElementDragging.prototype.tryStopDrag = function (ev) {
// .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events
// that come from the document to fire beforehand. much more convenient this way.
this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev) // bound with args
);
};
FeaturefulElementDragging.prototype.stopDrag = function (ev) {
this.isDragging = false;
this.emitter.trigger('dragend', ev);
};
// fill in the implementations...
FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) {
this.pointer.shouldIgnoreMove = bool;
};
FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) {
this.mirror.setIsVisible(bool);
};
FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
this.mirrorNeedsRevert = bool;
};
FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) {
this.autoScroller.isEnabled = bool;
};
return FeaturefulElementDragging;
}(core.ElementDragging));
/*
When this class is instantiated, it records the offset of an element (relative to the document topleft),
and continues to monitor scrolling, updating the cached coordinates if it needs to.
Does not access the DOM after instantiation, so highly performant.
Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element
and an determine if a given point is inside the combined clipping rectangle.
*/
var OffsetTracker = /** @class */ (function () {
function OffsetTracker(el) {
this.origRect = core.computeRect(el);
// will work fine for divs that have overflow:hidden
this.scrollCaches = core.getClippingParents(el).map(function (el) {
return new ElementScrollGeomCache(el, true); // listen=true
});
}
OffsetTracker.prototype.destroy = function () {
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
scrollCache.destroy();
}
};
OffsetTracker.prototype.computeLeft = function () {
var left = this.origRect.left;
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
left += scrollCache.origScrollLeft - scrollCache.getScrollLeft();
}
return left;
};
OffsetTracker.prototype.computeTop = function () {
var top = this.origRect.top;
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
top += scrollCache.origScrollTop - scrollCache.getScrollTop();
}
return top;
};
OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) {
var point = { left: pageX, top: pageY };
for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
var scrollCache = _a[_i];
if (!isIgnoredClipping(scrollCache.getEventTarget()) &&
!core.pointInsideRect(point, scrollCache.clientRect)) {
return false;
}
}
return true;
};
return OffsetTracker;
}());
// certain clipping containers should never constrain interactions, like and
// https://github.com/fullcalendar/fullcalendar/issues/3615
function isIgnoredClipping(node) {
var tagName = node.tagName;
return tagName === 'HTML' || tagName === 'BODY';
}
/*
Tracks movement over multiple droppable areas (aka "hits")
that exist in one or more DateComponents.
Relies on an existing draggable.
emits:
- pointerdown
- dragstart
- hitchange - fires initially, even if not over a hit
- pointerup
- (hitchange - again, to null, if ended over a hit)
- dragend
*/
var HitDragging = /** @class */ (function () {
function HitDragging(dragging, droppableStore) {
var _this = this;
// options that can be set by caller
this.useSubjectCenter = false;
this.requireInitial = true; // if doesn't start out on a hit, won't emit any events
this.initialHit = null;
this.movingHit = null;
this.finalHit = null; // won't ever be populated if shouldIgnoreMove
this.handlePointerDown = function (ev) {
var dragging = _this.dragging;
_this.initialHit = null;
_this.movingHit = null;
_this.finalHit = null;
_this.prepareHits();
_this.processFirstCoord(ev);
if (_this.initialHit || !_this.requireInitial) {
dragging.setIgnoreMove(false);
_this.emitter.trigger('pointerdown', ev); // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
}
else {
dragging.setIgnoreMove(true);
}
};
this.handleDragStart = function (ev) {
_this.emitter.trigger('dragstart', ev);
_this.handleMove(ev, true); // force = fire even if initially null
};
this.handleDragMove = function (ev) {
_this.emitter.trigger('dragmove', ev);
_this.handleMove(ev);
};
this.handlePointerUp = function (ev) {
_this.releaseHits();
_this.emitter.trigger('pointerup', ev);
};
this.handleDragEnd = function (ev) {
if (_this.movingHit) {
_this.emitter.trigger('hitupdate', null, true, ev);
}
_this.finalHit = _this.movingHit;
_this.movingHit = null;
_this.emitter.trigger('dragend', ev);
};
this.droppableStore = droppableStore;
dragging.emitter.on('pointerdown', this.handlePointerDown);
dragging.emitter.on('dragstart', this.handleDragStart);
dragging.emitter.on('dragmove', this.handleDragMove);
dragging.emitter.on('pointerup', this.handlePointerUp);
dragging.emitter.on('dragend', this.handleDragEnd);
this.dragging = dragging;
this.emitter = new core.EmitterMixin();
}
// sets initialHit
// sets coordAdjust
HitDragging.prototype.processFirstCoord = function (ev) {
var origPoint = { left: ev.pageX, top: ev.pageY };
var adjustedPoint = origPoint;
var subjectEl = ev.subjectEl;
var subjectRect;
if (subjectEl !== document) {
subjectRect = core.computeRect(subjectEl);
adjustedPoint = core.constrainPoint(adjustedPoint, subjectRect);
}
var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top);
if (initialHit) {
if (this.useSubjectCenter && subjectRect) {
var slicedSubjectRect = core.intersectRects(subjectRect, initialHit.rect);
if (slicedSubjectRect) {
adjustedPoint = core.getRectCenter(slicedSubjectRect);
}
}
this.coordAdjust = core.diffPoints(adjustedPoint, origPoint);
}
else {
this.coordAdjust = { left: 0, top: 0 };
}
};
HitDragging.prototype.handleMove = function (ev, forceHandle) {
var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top);
if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
this.movingHit = hit;
this.emitter.trigger('hitupdate', hit, false, ev);
}
};
HitDragging.prototype.prepareHits = function () {
this.offsetTrackers = core.mapHash(this.droppableStore, function (interactionSettings) {
interactionSettings.component.buildPositionCaches();
return new OffsetTracker(interactionSettings.el);
});
};
HitDragging.prototype.releaseHits = function () {
var offsetTrackers = this.offsetTrackers;
for (var id in offsetTrackers) {
offsetTrackers[id].destroy();
}
this.offsetTrackers = {};
};
HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) {
var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers;
var bestHit = null;
for (var id in droppableStore) {
var component = droppableStore[id].component;
var offsetTracker = offsetTrackers[id];
if (offsetTracker.isWithinClipping(offsetLeft, offsetTop)) {
var originLeft = offsetTracker.computeLeft();
var originTop = offsetTracker.computeTop();
var positionLeft = offsetLeft - originLeft;
var positionTop = offsetTop - originTop;
var origRect = offsetTracker.origRect;
var width = origRect.right - origRect.left;
var height = origRect.bottom - origRect.top;
if (
// must be within the element's bounds
positionLeft >= 0 && positionLeft < width &&
positionTop >= 0 && positionTop < height) {
var hit = component.queryHit(positionLeft, positionTop, width, height);
if (hit &&
(
// make sure the hit is within activeRange, meaning it's not a deal cell
!component.props.dateProfile || // hack for DayTile
core.rangeContainsRange(component.props.dateProfile.activeRange, hit.dateSpan.range)) &&
(!bestHit || hit.layer > bestHit.layer)) {
// TODO: better way to re-orient rectangle
hit.rect.left += originLeft;
hit.rect.right += originLeft;
hit.rect.top += originTop;
hit.rect.bottom += originTop;
bestHit = hit;
}
}
}
}
return bestHit;
};
return HitDragging;
}());
function isHitsEqual(hit0, hit1) {
if (!hit0 && !hit1) {
return true;
}
if (Boolean(hit0) !== Boolean(hit1)) {
return false;
}
return core.isDateSpansEqual(hit0.dateSpan, hit1.dateSpan);
}
/*
Monitors when the user clicks on a specific date/time of a component.
A pointerdown+pointerup on the same "hit" constitutes a click.
*/
var DateClicking = /** @class */ (function (_super) {
__extends(DateClicking, _super);
function DateClicking(settings) {
var _this = _super.call(this, settings) || this;
_this.handlePointerDown = function (ev) {
var dragging = _this.dragging;
// do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
dragging.setIgnoreMove(!_this.component.isValidDateDownEl(dragging.pointer.downEl));
};
// won't even fire if moving was ignored
_this.handleDragEnd = function (ev) {
var component = _this.component;
var _a = component.context, calendar = _a.calendar, view = _a.view;
var pointer = _this.dragging.pointer;
if (!pointer.wasTouchScroll) {
var _b = _this.hitDragging, initialHit = _b.initialHit, finalHit = _b.finalHit;
if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
calendar.triggerDateClick(initialHit.dateSpan, initialHit.dayEl, view, ev.origEvent);
}
}
};
var component = settings.component;
// we DO want to watch pointer moves because otherwise finalHit won't get populated
_this.dragging = new FeaturefulElementDragging(component.el);
_this.dragging.autoScroller.isEnabled = false;
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
hitDragging.emitter.on('dragend', _this.handleDragEnd);
return _this;
}
DateClicking.prototype.destroy = function () {
this.dragging.destroy();
};
return DateClicking;
}(core.Interaction));
/*
Tracks when the user selects a portion of time of a component,
constituted by a drag over date cells, with a possible delay at the beginning of the drag.
*/
var DateSelecting = /** @class */ (function (_super) {
__extends(DateSelecting, _super);
function DateSelecting(settings) {
var _this = _super.call(this, settings) || this;
_this.dragSelection = null;
_this.handlePointerDown = function (ev) {
var _a = _this, component = _a.component, dragging = _a.dragging;
var options = component.context.options;
var canSelect = options.selectable &&
component.isValidDateDownEl(ev.origEvent.target);
// don't bother to watch expensive moves if component won't do selection
dragging.setIgnoreMove(!canSelect);
// if touch, require user to hold down
dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null;
};
_this.handleDragStart = function (ev) {
_this.component.context.calendar.unselect(ev); // unselect previous selections
};
_this.handleHitUpdate = function (hit, isFinal) {
var calendar = _this.component.context.calendar;
var dragSelection = null;
var isInvalid = false;
if (hit) {
dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, calendar.pluginSystem.hooks.dateSelectionTransformers);
if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) {
isInvalid = true;
dragSelection = null;
}
}
if (dragSelection) {
calendar.dispatch({ type: 'SELECT_DATES', selection: dragSelection });
}
else if (!isFinal) { // only unselect if moved away while dragging
calendar.dispatch({ type: 'UNSELECT_DATES' });
}
if (!isInvalid) {
core.enableCursor();
}
else {
core.disableCursor();
}
if (!isFinal) {
_this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging
}
};
_this.handlePointerUp = function (pev) {
if (_this.dragSelection) {
// selection is already rendered, so just need to report selection
_this.component.context.calendar.triggerDateSelect(_this.dragSelection, pev);
_this.dragSelection = null;
}
};
var component = settings.component;
var options = component.context.options;
var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
dragging.touchScrollAllowed = false;
dragging.minDistance = options.selectMinDistance || 0;
dragging.autoScroller.isEnabled = options.dragScroll;
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
hitDragging.emitter.on('dragstart', _this.handleDragStart);
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
hitDragging.emitter.on('pointerup', _this.handlePointerUp);
return _this;
}
DateSelecting.prototype.destroy = function () {
this.dragging.destroy();
};
return DateSelecting;
}(core.Interaction));
function getComponentTouchDelay(component) {
var options = component.context.options;
var delay = options.selectLongPressDelay;
if (delay == null) {
delay = options.longPressDelay;
}
return delay;
}
function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) {
var dateSpan0 = hit0.dateSpan;
var dateSpan1 = hit1.dateSpan;
var ms = [
dateSpan0.range.start,
dateSpan0.range.end,
dateSpan1.range.start,
dateSpan1.range.end
];
ms.sort(core.compareNumbers);
var props = {};
for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) {
var transformer = dateSelectionTransformers_1[_i];
var res = transformer(hit0, hit1);
if (res === false) {
return null;
}
else if (res) {
__assign(props, res);
}
}
props.range = { start: ms[0], end: ms[3] };
props.allDay = dateSpan0.allDay;
return props;
}
var EventDragging = /** @class */ (function (_super) {
__extends(EventDragging, _super);
function EventDragging(settings) {
var _this = _super.call(this, settings) || this;
// internal state
_this.subjectSeg = null; // the seg being selected/dragged
_this.isDragging = false;
_this.eventRange = null;
_this.relevantEvents = null; // the events being dragged
_this.receivingCalendar = null;
_this.validMutation = null;
_this.mutatedRelevantEvents = null;
_this.handlePointerDown = function (ev) {
var origTarget = ev.origEvent.target;
var _a = _this, component = _a.component, dragging = _a.dragging;
var mirror = dragging.mirror;
var options = component.context.options;
var initialCalendar = component.context.calendar;
var subjectSeg = _this.subjectSeg = core.getElSeg(ev.subjectEl);
var eventRange = _this.eventRange = subjectSeg.eventRange;
var eventInstanceId = eventRange.instance.instanceId;
_this.relevantEvents = core.getRelevantEvents(initialCalendar.state.eventStore, eventInstanceId);
dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance;
dragging.delay =
// only do a touch delay if touch and this event hasn't been selected yet
(ev.isTouch && eventInstanceId !== component.props.eventSelection) ?
getComponentTouchDelay$1(component) :
null;
mirror.parentNode = initialCalendar.el;
mirror.revertDuration = options.dragRevertDuration;
var isValid = component.isValidSegDownEl(origTarget) &&
!core.elementClosest(origTarget, '.fc-resizer'); // NOT on a resizer
dragging.setIgnoreMove(!isValid);
// disable dragging for elements that are resizable (ie, selectable)
// but are not draggable
_this.isDragging = isValid &&
ev.subjectEl.classList.contains('fc-draggable');
};
_this.handleDragStart = function (ev) {
var context = _this.component.context;
var initialCalendar = context.calendar;
var eventRange = _this.eventRange;
var eventInstanceId = eventRange.instance.instanceId;
if (ev.isTouch) {
// need to select a different event?
if (eventInstanceId !== _this.component.props.eventSelection) {
initialCalendar.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId });
}
}
else {
// if now using mouse, but was previous touch interaction, clear selected event
initialCalendar.dispatch({ type: 'UNSELECT_EVENT' });
}
if (_this.isDragging) {
initialCalendar.unselect(ev); // unselect *date* selection
initialCalendar.publiclyTrigger('eventDragStart', [
{
el: _this.subjectSeg.el,
event: new core.EventApi(initialCalendar, eventRange.def, eventRange.instance),
jsEvent: ev.origEvent,
view: context.view
}
]);
}
};
_this.handleHitUpdate = function (hit, isFinal) {
if (!_this.isDragging) {
return;
}
var relevantEvents = _this.relevantEvents;
var initialHit = _this.hitDragging.initialHit;
var initialCalendar = _this.component.context.calendar;
// states based on new hit
var receivingCalendar = null;
var mutation = null;
var mutatedRelevantEvents = null;
var isInvalid = false;
var interaction = {
affectedEvents: relevantEvents,
mutatedEvents: core.createEmptyEventStore(),
isEvent: true,
origSeg: _this.subjectSeg
};
if (hit) {
var receivingComponent = hit.component;
receivingCalendar = receivingComponent.context.calendar;
var receivingOptions = receivingComponent.context.options;
if (initialCalendar === receivingCalendar ||
receivingOptions.editable && receivingOptions.droppable) {
mutation = computeEventMutation(initialHit, hit, receivingCalendar.pluginSystem.hooks.eventDragMutationMassagers);
if (mutation) {
mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, receivingCalendar.eventUiBases, mutation, receivingCalendar);
interaction.mutatedEvents = mutatedRelevantEvents;
if (!receivingComponent.isInteractionValid(interaction)) {
isInvalid = true;
mutation = null;
mutatedRelevantEvents = null;
interaction.mutatedEvents = core.createEmptyEventStore();
}
}
}
else {
receivingCalendar = null;
}
}
_this.displayDrag(receivingCalendar, interaction);
if (!isInvalid) {
core.enableCursor();
}
else {
core.disableCursor();
}
if (!isFinal) {
if (initialCalendar === receivingCalendar && // TODO: write test for this
isHitsEqual(initialHit, hit)) {
mutation = null;
}
_this.dragging.setMirrorNeedsRevert(!mutation);
// render the mirror if no already-rendered mirror
// TODO: wish we could somehow wait for dispatch to guarantee render
_this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-mirror'));
// assign states based on new hit
_this.receivingCalendar = receivingCalendar;
_this.validMutation = mutation;
_this.mutatedRelevantEvents = mutatedRelevantEvents;
}
};
_this.handlePointerUp = function () {
if (!_this.isDragging) {
_this.cleanup(); // because handleDragEnd won't fire
}
};
_this.handleDragEnd = function (ev) {
if (_this.isDragging) {
var context = _this.component.context;
var initialCalendar_1 = context.calendar;
var initialView = context.view;
var _a = _this, receivingCalendar = _a.receivingCalendar, validMutation = _a.validMutation;
var eventDef = _this.eventRange.def;
var eventInstance = _this.eventRange.instance;
var eventApi = new core.EventApi(initialCalendar_1, eventDef, eventInstance);
var relevantEvents_1 = _this.relevantEvents;
var mutatedRelevantEvents = _this.mutatedRelevantEvents;
var finalHit = _this.hitDragging.finalHit;
_this.clearDrag(); // must happen after revert animation
initialCalendar_1.publiclyTrigger('eventDragStop', [
{
el: _this.subjectSeg.el,
event: eventApi,
jsEvent: ev.origEvent,
view: initialView
}
]);
if (validMutation) {
// dropped within same calendar
if (receivingCalendar === initialCalendar_1) {
initialCalendar_1.dispatch({
type: 'MERGE_EVENTS',
eventStore: mutatedRelevantEvents
});
var transformed = {};
for (var _i = 0, _b = initialCalendar_1.pluginSystem.hooks.eventDropTransformers; _i < _b.length; _i++) {
var transformer = _b[_i];
__assign(transformed, transformer(validMutation, initialCalendar_1));
}
var eventDropArg = __assign({}, transformed, { el: ev.subjectEl, delta: validMutation.datesDelta, oldEvent: eventApi, event: new core.EventApi(// the data AFTER the mutation
initialCalendar_1, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null), revert: function () {
initialCalendar_1.dispatch({
type: 'MERGE_EVENTS',
eventStore: relevantEvents_1
});
}, jsEvent: ev.origEvent, view: initialView });
initialCalendar_1.publiclyTrigger('eventDrop', [eventDropArg]);
// dropped in different calendar
}
else if (receivingCalendar) {
initialCalendar_1.publiclyTrigger('eventLeave', [
{
draggedEl: ev.subjectEl,
event: eventApi,
view: initialView
}
]);
initialCalendar_1.dispatch({
type: 'REMOVE_EVENT_INSTANCES',
instances: _this.mutatedRelevantEvents.instances
});
receivingCalendar.dispatch({
type: 'MERGE_EVENTS',
eventStore: _this.mutatedRelevantEvents
});
if (ev.isTouch) {
receivingCalendar.dispatch({
type: 'SELECT_EVENT',
eventInstanceId: eventInstance.instanceId
});
}
var dropArg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component // should this be finalHit.component.view? See #4644
});
receivingCalendar.publiclyTrigger('drop', [dropArg]);
receivingCalendar.publiclyTrigger('eventReceive', [
{
draggedEl: ev.subjectEl,
event: new core.EventApi(// the data AFTER the mutation
receivingCalendar, mutatedRelevantEvents.defs[eventDef.defId], mutatedRelevantEvents.instances[eventInstance.instanceId]),
view: finalHit.component // should this be finalHit.component.view? See #4644
}
]);
}
}
else {
initialCalendar_1.publiclyTrigger('_noEventDrop');
}
}
_this.cleanup();
};
var component = _this.component;
var options = component.context.options;
var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
dragging.pointer.selector = EventDragging.SELECTOR;
dragging.touchScrollAllowed = false;
dragging.autoScroller.isEnabled = options.dragScroll;
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsStore);
hitDragging.useSubjectCenter = settings.useEventCenter;
hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
hitDragging.emitter.on('dragstart', _this.handleDragStart);
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
hitDragging.emitter.on('pointerup', _this.handlePointerUp);
hitDragging.emitter.on('dragend', _this.handleDragEnd);
return _this;
}
EventDragging.prototype.destroy = function () {
this.dragging.destroy();
};
// render a drag state on the next receivingCalendar
EventDragging.prototype.displayDrag = function (nextCalendar, state) {
var initialCalendar = this.component.context.calendar;
var prevCalendar = this.receivingCalendar;
// does the previous calendar need to be cleared?
if (prevCalendar && prevCalendar !== nextCalendar) {
// does the initial calendar need to be cleared?
// if so, don't clear all the way. we still need to to hide the affectedEvents
if (prevCalendar === initialCalendar) {
prevCalendar.dispatch({
type: 'SET_EVENT_DRAG',
state: {
affectedEvents: state.affectedEvents,
mutatedEvents: core.createEmptyEventStore(),
isEvent: true,
origSeg: state.origSeg
}
});
// completely clear the old calendar if it wasn't the initial
}
else {
prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
}
}
if (nextCalendar) {
nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
}
};
EventDragging.prototype.clearDrag = function () {
var initialCalendar = this.component.context.calendar;
var receivingCalendar = this.receivingCalendar;
if (receivingCalendar) {
receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
}
// the initial calendar might have an dummy drag state from displayDrag
if (initialCalendar !== receivingCalendar) {
initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
}
};
EventDragging.prototype.cleanup = function () {
this.subjectSeg = null;
this.isDragging = false;
this.eventRange = null;
this.relevantEvents = null;
this.receivingCalendar = null;
this.validMutation = null;
this.mutatedRelevantEvents = null;
};
EventDragging.SELECTOR = '.fc-draggable, .fc-resizable'; // TODO: test this in IE11
return EventDragging;
}(core.Interaction));
function computeEventMutation(hit0, hit1, massagers) {
var dateSpan0 = hit0.dateSpan;
var dateSpan1 = hit1.dateSpan;
var date0 = dateSpan0.range.start;
var date1 = dateSpan1.range.start;
var standardProps = {};
if (dateSpan0.allDay !== dateSpan1.allDay) {
standardProps.allDay = dateSpan1.allDay;
standardProps.hasEnd = hit1.component.context.options.allDayMaintainDuration;
if (dateSpan1.allDay) {
// means date1 is already start-of-day,
// but date0 needs to be converted
date0 = core.startOfDay(date0);
}
}
var delta = core.diffDates(date0, date1, hit0.component.context.dateEnv, hit0.component === hit1.component ?
hit0.component.largeUnit :
null);
if (delta.milliseconds) { // has hours/minutes/seconds
standardProps.allDay = false;
}
var mutation = {
datesDelta: delta,
standardProps: standardProps
};
for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) {
var massager = massagers_1[_i];
massager(mutation, hit0, hit1);
}
return mutation;
}
function getComponentTouchDelay$1(component) {
var options = component.context.options;
var delay = options.eventLongPressDelay;
if (delay == null) {
delay = options.longPressDelay;
}
return delay;
}
var EventDragging$1 = /** @class */ (function (_super) {
__extends(EventDragging, _super);
function EventDragging(settings) {
var _this = _super.call(this, settings) || this;
// internal state
_this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg?
_this.eventRange = null;
_this.relevantEvents = null;
_this.validMutation = null;
_this.mutatedRelevantEvents = null;
_this.handlePointerDown = function (ev) {
var component = _this.component;
var seg = _this.querySeg(ev);
var eventRange = _this.eventRange = seg.eventRange;
_this.dragging.minDistance = component.context.options.eventDragMinDistance;
// if touch, need to be working with a selected event
_this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) ||
(ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId));
};
_this.handleDragStart = function (ev) {
var _a = _this.component.context, calendar = _a.calendar, view = _a.view;
var eventRange = _this.eventRange;
_this.relevantEvents = core.getRelevantEvents(calendar.state.eventStore, _this.eventRange.instance.instanceId);
_this.draggingSeg = _this.querySeg(ev);
calendar.unselect();
calendar.publiclyTrigger('eventResizeStart', [
{
el: _this.draggingSeg.el,
event: new core.EventApi(calendar, eventRange.def, eventRange.instance),
jsEvent: ev.origEvent,
view: view
}
]);
};
_this.handleHitUpdate = function (hit, isFinal, ev) {
var calendar = _this.component.context.calendar;
var relevantEvents = _this.relevantEvents;
var initialHit = _this.hitDragging.initialHit;
var eventInstance = _this.eventRange.instance;
var mutation = null;
var mutatedRelevantEvents = null;
var isInvalid = false;
var interaction = {
affectedEvents: relevantEvents,
mutatedEvents: core.createEmptyEventStore(),
isEvent: true,
origSeg: _this.draggingSeg
};
if (hit) {
mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-start-resizer'), eventInstance.range, calendar.pluginSystem.hooks.eventResizeJoinTransforms);
}
if (mutation) {
mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, calendar.eventUiBases, mutation, calendar);
interaction.mutatedEvents = mutatedRelevantEvents;
if (!_this.component.isInteractionValid(interaction)) {
isInvalid = true;
mutation = null;
mutatedRelevantEvents = null;
interaction.mutatedEvents = null;
}
}
if (mutatedRelevantEvents) {
calendar.dispatch({
type: 'SET_EVENT_RESIZE',
state: interaction
});
}
else {
calendar.dispatch({ type: 'UNSET_EVENT_RESIZE' });
}
if (!isInvalid) {
core.enableCursor();
}
else {
core.disableCursor();
}
if (!isFinal) {
if (mutation && isHitsEqual(initialHit, hit)) {
mutation = null;
}
_this.validMutation = mutation;
_this.mutatedRelevantEvents = mutatedRelevantEvents;
}
};
_this.handleDragEnd = function (ev) {
var _a = _this.component.context, calendar = _a.calendar, view = _a.view;
var eventDef = _this.eventRange.def;
var eventInstance = _this.eventRange.instance;
var eventApi = new core.EventApi(calendar, eventDef, eventInstance);
var relevantEvents = _this.relevantEvents;
var mutatedRelevantEvents = _this.mutatedRelevantEvents;
calendar.publiclyTrigger('eventResizeStop', [
{
el: _this.draggingSeg.el,
event: eventApi,
jsEvent: ev.origEvent,
view: view
}
]);
if (_this.validMutation) {
calendar.dispatch({
type: 'MERGE_EVENTS',
eventStore: mutatedRelevantEvents
});
calendar.publiclyTrigger('eventResize', [
{
el: _this.draggingSeg.el,
startDelta: _this.validMutation.startDelta || core.createDuration(0),
endDelta: _this.validMutation.endDelta || core.createDuration(0),
prevEvent: eventApi,
event: new core.EventApi(// the data AFTER the mutation
calendar, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null),
revert: function () {
calendar.dispatch({
type: 'MERGE_EVENTS',
eventStore: relevantEvents
});
},
jsEvent: ev.origEvent,
view: view
}
]);
}
else {
calendar.publiclyTrigger('_noEventResize');
}
// reset all internal state
_this.draggingSeg = null;
_this.relevantEvents = null;
_this.validMutation = null;
// okay to keep eventInstance around. useful to set it in handlePointerDown
};
var component = settings.component;
var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
dragging.pointer.selector = '.fc-resizer';
dragging.touchScrollAllowed = false;
dragging.autoScroller.isEnabled = component.context.options.dragScroll;
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
hitDragging.emitter.on('dragstart', _this.handleDragStart);
hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
hitDragging.emitter.on('dragend', _this.handleDragEnd);
return _this;
}
EventDragging.prototype.destroy = function () {
this.dragging.destroy();
};
EventDragging.prototype.querySeg = function (ev) {
return core.getElSeg(core.elementClosest(ev.subjectEl, this.component.fgSegSelector));
};
return EventDragging;
}(core.Interaction));
function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) {
var dateEnv = hit0.component.context.dateEnv;
var date0 = hit0.dateSpan.range.start;
var date1 = hit1.dateSpan.range.start;
var delta = core.diffDates(date0, date1, dateEnv, hit0.component.largeUnit);
var props = {};
for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) {
var transform = transforms_1[_i];
var res = transform(hit0, hit1);
if (res === false) {
return null;
}
else if (res) {
__assign(props, res);
}
}
if (isFromStart) {
if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
props.startDelta = delta;
return props;
}
}
else {
if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
props.endDelta = delta;
return props;
}
}
return null;
}
var UnselectAuto = /** @class */ (function () {
function UnselectAuto(calendar) {
var _this = this;
this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system
this.onSelect = function (selectInfo) {
if (selectInfo.jsEvent) {
_this.isRecentPointerDateSelect = true;
}
};
this.onDocumentPointerUp = function (pev) {
var _a = _this, calendar = _a.calendar, documentPointer = _a.documentPointer;
var state = calendar.state;
// touch-scrolling should never unfocus any type of selection
if (!documentPointer.wasTouchScroll) {
if (state.dateSelection && // an existing date selection?
!_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
) {
var unselectAuto = calendar.viewOpt('unselectAuto');
var unselectCancel = calendar.viewOpt('unselectCancel');
if (unselectAuto && (!unselectAuto || !core.elementClosest(documentPointer.downEl, unselectCancel))) {
calendar.unselect(pev);
}
}
if (state.eventSelection && // an existing event selected?
!core.elementClosest(documentPointer.downEl, EventDragging.SELECTOR) // interaction DIDN'T start on an event
) {
calendar.dispatch({ type: 'UNSELECT_EVENT' });
}
}
_this.isRecentPointerDateSelect = false;
};
this.calendar = calendar;
var documentPointer = this.documentPointer = new PointerDragging(document);
documentPointer.shouldIgnoreMove = true;
documentPointer.shouldWatchScroll = false;
documentPointer.emitter.on('pointerup', this.onDocumentPointerUp);
/*
TODO: better way to know about whether there was a selection with the pointer
*/
calendar.on('select', this.onSelect);
}
UnselectAuto.prototype.destroy = function () {
this.calendar.off('select', this.onSelect);
this.documentPointer.destroy();
};
return UnselectAuto;
}());
/*
Given an already instantiated draggable object for one-or-more elements,
Interprets any dragging as an attempt to drag an events that lives outside
of a calendar onto a calendar.
*/
var ExternalElementDragging = /** @class */ (function () {
function ExternalElementDragging(dragging, suppliedDragMeta) {
var _this = this;
this.receivingCalendar = null;
this.droppableEvent = null; // will exist for all drags, even if create:false
this.suppliedDragMeta = null;
this.dragMeta = null;
this.handleDragStart = function (ev) {
_this.dragMeta = _this.buildDragMeta(ev.subjectEl);
};
this.handleHitUpdate = function (hit, isFinal, ev) {
var dragging = _this.hitDragging.dragging;
var receivingCalendar = null;
var droppableEvent = null;
var isInvalid = false;
var interaction = {
affectedEvents: core.createEmptyEventStore(),
mutatedEvents: core.createEmptyEventStore(),
isEvent: _this.dragMeta.create,
origSeg: null
};
if (hit) {
receivingCalendar = hit.component.context.calendar;
if (_this.canDropElOnCalendar(ev.subjectEl, receivingCalendar)) {
droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingCalendar);
interaction.mutatedEvents = core.eventTupleToStore(droppableEvent);
isInvalid = !core.isInteractionValid(interaction, receivingCalendar);
if (isInvalid) {
interaction.mutatedEvents = core.createEmptyEventStore();
droppableEvent = null;
}
}
}
_this.displayDrag(receivingCalendar, interaction);
// show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?)
// TODO: wish we could somehow wait for dispatch to guarantee render
dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-mirror'));
if (!isInvalid) {
core.enableCursor();
}
else {
core.disableCursor();
}
if (!isFinal) {
dragging.setMirrorNeedsRevert(!droppableEvent);
_this.receivingCalendar = receivingCalendar;
_this.droppableEvent = droppableEvent;
}
};
this.handleDragEnd = function (pev) {
var _a = _this, receivingCalendar = _a.receivingCalendar, droppableEvent = _a.droppableEvent;
_this.clearDrag();
if (receivingCalendar && droppableEvent) {
var finalHit = _this.hitDragging.finalHit;
var finalView = finalHit.component.context.view;
var dragMeta = _this.dragMeta;
var arg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView });
receivingCalendar.publiclyTrigger('drop', [arg]);
if (dragMeta.create) {
receivingCalendar.dispatch({
type: 'MERGE_EVENTS',
eventStore: core.eventTupleToStore(droppableEvent)
});
if (pev.isTouch) {
receivingCalendar.dispatch({
type: 'SELECT_EVENT',
eventInstanceId: droppableEvent.instance.instanceId
});
}
// signal that an external event landed
receivingCalendar.publiclyTrigger('eventReceive', [
{
draggedEl: pev.subjectEl,
event: new core.EventApi(receivingCalendar, droppableEvent.def, droppableEvent.instance),
view: finalView
}
]);
}
}
_this.receivingCalendar = null;
_this.droppableEvent = null;
};
var hitDragging = this.hitDragging = new HitDragging(dragging, core.interactionSettingsStore);
hitDragging.requireInitial = false; // will start outside of a component
hitDragging.emitter.on('dragstart', this.handleDragStart);
hitDragging.emitter.on('hitupdate', this.handleHitUpdate);
hitDragging.emitter.on('dragend', this.handleDragEnd);
this.suppliedDragMeta = suppliedDragMeta;
}
ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) {
if (typeof this.suppliedDragMeta === 'object') {
return core.parseDragMeta(this.suppliedDragMeta);
}
else if (typeof this.suppliedDragMeta === 'function') {
return core.parseDragMeta(this.suppliedDragMeta(subjectEl));
}
else {
return getDragMetaFromEl(subjectEl);
}
};
ExternalElementDragging.prototype.displayDrag = function (nextCalendar, state) {
var prevCalendar = this.receivingCalendar;
if (prevCalendar && prevCalendar !== nextCalendar) {
prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
}
if (nextCalendar) {
nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
}
};
ExternalElementDragging.prototype.clearDrag = function () {
if (this.receivingCalendar) {
this.receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
}
};
ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingCalendar) {
var dropAccept = receivingCalendar.opt('dropAccept');
if (typeof dropAccept === 'function') {
return dropAccept(el);
}
else if (typeof dropAccept === 'string' && dropAccept) {
return Boolean(core.elementMatches(el, dropAccept));
}
return true;
};
return ExternalElementDragging;
}());
// Utils for computing event store from the DragMeta
// ----------------------------------------------------------------------------------------------------
function computeEventForDateSpan(dateSpan, dragMeta, calendar) {
var defProps = __assign({}, dragMeta.leftoverProps);
for (var _i = 0, _a = calendar.pluginSystem.hooks.externalDefTransforms; _i < _a.length; _i++) {
var transform = _a[_i];
__assign(defProps, transform(dateSpan, dragMeta));
}
var def = core.parseEventDef(defProps, dragMeta.sourceId, dateSpan.allDay, calendar.opt('forceEventDuration') || Boolean(dragMeta.duration), // hasEnd
calendar);
var start = dateSpan.range.start;
// only rely on time info if drop zone is all-day,
// otherwise, we already know the time
if (dateSpan.allDay && dragMeta.startTime) {
start = calendar.dateEnv.add(start, dragMeta.startTime);
}
var end = dragMeta.duration ?
calendar.dateEnv.add(start, dragMeta.duration) :
calendar.getDefaultEventEnd(dateSpan.allDay, start);
var instance = core.createEventInstance(def.defId, { start: start, end: end });
return { def: def, instance: instance };
}
// Utils for extracting data from element
// ----------------------------------------------------------------------------------------------------
function getDragMetaFromEl(el) {
var str = getEmbeddedElData(el, 'event');
var obj = str ?
JSON.parse(str) :
{ create: false }; // if no embedded data, assume no event creation
return core.parseDragMeta(obj);
}
core.config.dataAttrPrefix = '';
function getEmbeddedElData(el, name) {
var prefix = core.config.dataAttrPrefix;
var prefixedName = (prefix ? prefix + '-' : '') + name;
return el.getAttribute('data-' + prefixedName) || '';
}
/*
Makes an element (that is *external* to any calendar) draggable.
Can pass in data that determines how an event will be created when dropped onto a calendar.
Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system.
*/
var ExternalDraggable = /** @class */ (function () {
function ExternalDraggable(el, settings) {
var _this = this;
if (settings === void 0) { settings = {}; }
this.handlePointerDown = function (ev) {
var dragging = _this.dragging;
var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay;
dragging.minDistance =
minDistance != null ?
minDistance :
(ev.isTouch ? 0 : core.globalDefaults.eventDragMinDistance);
dragging.delay =
ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
(longPressDelay != null ? longPressDelay : core.globalDefaults.longPressDelay) :
0;
};
this.handleDragStart = function (ev) {
if (ev.isTouch &&
_this.dragging.delay &&
ev.subjectEl.classList.contains('fc-event')) {
_this.dragging.mirror.getMirrorEl().classList.add('fc-selected');
}
};
this.settings = settings;
var dragging = this.dragging = new FeaturefulElementDragging(el);
dragging.touchScrollAllowed = false;
if (settings.itemSelector != null) {
dragging.pointer.selector = settings.itemSelector;
}
if (settings.appendTo != null) {
dragging.mirror.parentNode = settings.appendTo; // TODO: write tests
}
dragging.emitter.on('pointerdown', this.handlePointerDown);
dragging.emitter.on('dragstart', this.handleDragStart);
new ExternalElementDragging(dragging, settings.eventData);
}
ExternalDraggable.prototype.destroy = function () {
this.dragging.destroy();
};
return ExternalDraggable;
}());
/*
Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements.
The third-party system is responsible for drawing the visuals effects of the drag.
This class simply monitors for pointer movements and fires events.
It also has the ability to hide the moving element (the "mirror") during the drag.
*/
var InferredElementDragging = /** @class */ (function (_super) {
__extends(InferredElementDragging, _super);
function InferredElementDragging(containerEl) {
var _this = _super.call(this, containerEl) || this;
_this.shouldIgnoreMove = false;
_this.mirrorSelector = '';
_this.currentMirrorEl = null;
_this.handlePointerDown = function (ev) {
_this.emitter.trigger('pointerdown', ev);
if (!_this.shouldIgnoreMove) {
// fire dragstart right away. does not support delay or min-distance
_this.emitter.trigger('dragstart', ev);
}
};
_this.handlePointerMove = function (ev) {
if (!_this.shouldIgnoreMove) {
_this.emitter.trigger('dragmove', ev);
}
};
_this.handlePointerUp = function (ev) {
_this.emitter.trigger('pointerup', ev);
if (!_this.shouldIgnoreMove) {
// fire dragend right away. does not support a revert animation
_this.emitter.trigger('dragend', ev);
}
};
var pointer = _this.pointer = new PointerDragging(containerEl);
pointer.emitter.on('pointerdown', _this.handlePointerDown);
pointer.emitter.on('pointermove', _this.handlePointerMove);
pointer.emitter.on('pointerup', _this.handlePointerUp);
return _this;
}
InferredElementDragging.prototype.destroy = function () {
this.pointer.destroy();
};
InferredElementDragging.prototype.setIgnoreMove = function (bool) {
this.shouldIgnoreMove = bool;
};
InferredElementDragging.prototype.setMirrorIsVisible = function (bool) {
if (bool) {
// restore a previously hidden element.
// use the reference in case the selector class has already been removed.
if (this.currentMirrorEl) {
this.currentMirrorEl.style.visibility = '';
this.currentMirrorEl = null;
}
}
else {
var mirrorEl = this.mirrorSelector ?
document.querySelector(this.mirrorSelector) :
null;
if (mirrorEl) {
this.currentMirrorEl = mirrorEl;
mirrorEl.style.visibility = 'hidden';
}
}
};
return InferredElementDragging;
}(core.ElementDragging));
/*
Bridges third-party drag-n-drop systems with FullCalendar.
Must be instantiated and destroyed by caller.
*/
var ThirdPartyDraggable = /** @class */ (function () {
function ThirdPartyDraggable(containerOrSettings, settings) {
var containerEl = document;
if (
// wish we could just test instanceof EventTarget, but doesn't work in IE11
containerOrSettings === document ||
containerOrSettings instanceof Element) {
containerEl = containerOrSettings;
settings = settings || {};
}
else {
settings = (containerOrSettings || {});
}
var dragging = this.dragging = new InferredElementDragging(containerEl);
if (typeof settings.itemSelector === 'string') {
dragging.pointer.selector = settings.itemSelector;
}
else if (containerEl === document) {
dragging.pointer.selector = '[data-event]';
}
if (typeof settings.mirrorSelector === 'string') {
dragging.mirrorSelector = settings.mirrorSelector;
}
new ExternalElementDragging(dragging, settings.eventData);
}
ThirdPartyDraggable.prototype.destroy = function () {
this.dragging.destroy();
};
return ThirdPartyDraggable;
}());
var main = core.createPlugin({
componentInteractions: [DateClicking, DateSelecting, EventDragging, EventDragging$1],
calendarInteractions: [UnselectAuto],
elementDraggingImpl: FeaturefulElementDragging
});
exports.Draggable = ExternalDraggable;
exports.FeaturefulElementDragging = FeaturefulElementDragging;
exports.PointerDragging = PointerDragging;
exports.ThirdPartyDraggable = ThirdPartyDraggable;
exports.default = main;
Object.defineProperty(exports, '__esModule', { value: true });
}));
/*!
FullCalendar List View Plugin v4.4.0
Docs & License: https://fullcalendar.io/
(c) 2019 Adam Shaw
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
(global = global || self, factory(global.FullCalendarList = {}, global.FullCalendar));
}(this, function (exports, core) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var ListEventRenderer = /** @class */ (function (_super) {
__extends(ListEventRenderer, _super);
function ListEventRenderer(listView) {
var _this = _super.call(this) || this;
_this.listView = listView;
return _this;
}
ListEventRenderer.prototype.attachSegs = function (segs) {
if (!segs.length) {
this.listView.renderEmptyMessage();
}
else {
this.listView.renderSegList(segs);
}
};
ListEventRenderer.prototype.detachSegs = function () {
};
// generates the HTML for a single event row
ListEventRenderer.prototype.renderSegHtml = function (seg) {
var _a = this.context, theme = _a.theme, options = _a.options;
var eventRange = seg.eventRange;
var eventDef = eventRange.def;
var eventInstance = eventRange.instance;
var eventUi = eventRange.ui;
var url = eventDef.url;
var classes = ['fc-list-item'].concat(eventUi.classNames);
var bgColor = eventUi.backgroundColor;
var timeHtml;
if (eventDef.allDay) {
timeHtml = core.getAllDayHtml(options);
}
else if (core.isMultiDayRange(eventRange.range)) {
if (seg.isStart) {
timeHtml = core.htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
));
}
else if (seg.isEnd) {
timeHtml = core.htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
));
}
else { // inner segment that lasts the whole day
timeHtml = core.getAllDayHtml(options);
}
}
else {
// Display the normal time text for the *event's* times
timeHtml = core.htmlEscape(this.getTimeText(eventRange));
}
if (url) {
classes.push('fc-has-url');
}
return '
' +
(this.displayEventTime ?
'' +
(timeHtml || '') +
' | ' :
'') +
'' +
'' +
' | ' +
'' +
'' +
core.htmlEscape(eventDef.title || '') +
'' +
' | ' +
'
';
};
// like "4:00am"
ListEventRenderer.prototype.computeEventTimeFormat = function () {
return {
hour: 'numeric',
minute: '2-digit',
meridiem: 'short'
};
};
return ListEventRenderer;
}(core.FgEventRenderer));
/*
Responsible for the scroller, and forwarding event-related actions into the "grid".
*/
var ListView = /** @class */ (function (_super) {
__extends(ListView, _super);
function ListView(viewSpec, parentEl) {
var _this = _super.call(this, viewSpec, parentEl) || this;
_this.computeDateVars = core.memoize(computeDateVars);
_this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs);
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
_this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [_this.renderSkeleton]);
return _this;
}
ListView.prototype.firstContext = function (context) {
context.calendar.registerInteractiveComponent(this, {
el: this.el
// TODO: make aware that it doesn't do Hits
});
};
ListView.prototype.render = function (props, context) {
_super.prototype.render.call(this, props, context);
var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
this.dayDates = dayDates;
this.renderSkeleton(context);
this.renderContent(context, this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
};
ListView.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.renderSkeleton.unrender();
this.renderContent.unrender();
this.context.calendar.unregisterInteractiveComponent(this);
};
ListView.prototype._renderSkeleton = function (context) {
var theme = context.theme;
this.el.classList.add('fc-list-view');
var listViewClassNames = (theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
var listViewClassName = listViewClassNames_1[_i];
if (listViewClassName) { // in case input was empty string
this.el.classList.add(listViewClassName);
}
}
this.scroller = new core.ScrollComponent('hidden', // overflow x
'auto' // overflow y
);
this.el.appendChild(this.scroller.el);
this.contentEl = this.scroller.el; // shortcut
};
ListView.prototype._unrenderSkeleton = function () {
// TODO: remove classNames
this.scroller.destroy(); // will remove the Grid too
};
ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
_super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
this.eventRenderer.computeSizes(isResize);
this.eventRenderer.assignSizes(isResize);
this.scroller.clear(); // sets height to 'auto' and clears overflow
if (!isAuto) {
this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
}
};
ListView.prototype.computeScrollerHeight = function (viewHeight) {
return viewHeight -
core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
};
ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
return this.eventRangesToSegs(core.sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.context.nextDayThreshold).fg, dayRanges);
};
ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
var segs = [];
for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
var eventRange = eventRanges_1[_i];
segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
}
return segs;
};
ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
var _a = this.context, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
var range = eventRange.range;
var allDay = eventRange.def.allDay;
var dayIndex;
var segRange;
var seg;
var segs = [];
for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
segRange = core.intersectRanges(range, dayRanges[dayIndex]);
if (segRange) {
seg = {
component: this,
eventRange: eventRange,
start: segRange.start,
end: segRange.end,
isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
dayIndex: dayIndex
};
segs.push(seg);
// detect when range won't go fully into the next day,
// and mutate the latest seg to the be the end.
if (!seg.isEnd && !allDay &&
dayIndex + 1 < dayRanges.length &&
range.end <
dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
seg.end = range.end;
seg.isEnd = true;
break;
}
}
}
return segs;
};
ListView.prototype.renderEmptyMessage = function () {
this.contentEl.innerHTML =
'
' + // TODO: try less wraps
'
' +
'
' +
core.htmlEscape(this.context.options.noEventsMessage) +
'
' +
'
' +
'
';
};
// called by ListEventRenderer
ListView.prototype.renderSegList = function (allSegs) {
var theme = this.context.theme;
var segsByDay = this.groupSegsByDay(allSegs); // sparse array
var dayIndex;
var daySegs;
var i;
var tableEl = core.htmlToElement('
');
var tbodyEl = tableEl.querySelector('tbody');
for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
daySegs = segsByDay[dayIndex];
if (daySegs) { // sparse array, so might be undefined
// append a day header
tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
daySegs = this.eventRenderer.sortEventSegs(daySegs);
for (i = 0; i < daySegs.length; i++) {
tbodyEl.appendChild(daySegs[i].el); // append event row
}
}
}
this.contentEl.innerHTML = '';
this.contentEl.appendChild(tableEl);
};
// Returns a sparse array of arrays, segs grouped by their dayIndex
ListView.prototype.groupSegsByDay = function (segs) {
var segsByDay = []; // sparse array
var i;
var seg;
for (i = 0; i < segs.length; i++) {
seg = segs[i];
(segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
.push(seg);
}
return segsByDay;
};
// generates the HTML for the day headers that live amongst the event rows
ListView.prototype.buildDayHeaderRow = function (dayDate) {
var _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
var mainFormat = core.createFormatter(options.listDayFormat); // TODO: cache
var altFormat = core.createFormatter(options.listDayAltFormat); // TODO: cache
return core.createElement('tr', {
className: 'fc-list-heading',
'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
}, '');
};
return ListView;
}(core.View));
ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
function computeDateVars(dateProfile) {
var dayStart = core.startOfDay(dateProfile.renderRange.start);
var viewEnd = dateProfile.renderRange.end;
var dayDates = [];
var dayRanges = [];
while (dayStart < viewEnd) {
dayDates.push(dayStart);
dayRanges.push({
start: dayStart,
end: core.addDays(dayStart, 1)
});
dayStart = core.addDays(dayStart, 1);
}
return { dayDates: dayDates, dayRanges: dayRanges };
}
var main = core.createPlugin({
views: {
list: {
class: ListView,
buttonTextKey: 'list',
listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
},
listDay: {
type: 'list',
duration: { days: 1 },
listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
},
listWeek: {
type: 'list',
duration: { weeks: 1 },
listDayFormat: { weekday: 'long' },
listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
},
listMonth: {
type: 'list',
duration: { month: 1 },
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
},
listYear: {
type: 'list',
duration: { year: 1 },
listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
}
}
});
exports.ListView = ListView;
exports.default = main;
Object.defineProperty(exports, '__esModule', { value: true });
}));
/*!
FullCalendar Time Grid Plugin v4.4.1
Docs & License: https://fullcalendar.io/
(c) 2019 Adam Shaw
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/daygrid')) :
typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/daygrid'], factory) :
(global = global || self, factory(global.FullCalendarTimeGrid = {}, global.FullCalendar, global.FullCalendarDayGrid));
}(this, function (exports, core, daygrid) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
/*
Only handles foreground segs.
Does not own rendering. Use for low-level util methods by TimeGrid.
*/
var TimeGridEventRenderer = /** @class */ (function (_super) {
__extends(TimeGridEventRenderer, _super);
function TimeGridEventRenderer(timeGrid) {
var _this = _super.call(this) || this;
_this.timeGrid = timeGrid;
return _this;
}
TimeGridEventRenderer.prototype.renderSegs = function (context, segs, mirrorInfo) {
_super.prototype.renderSegs.call(this, context, segs, mirrorInfo);
// TODO: dont do every time. memoize
this.fullTimeFormat = core.createFormatter({
hour: 'numeric',
minute: '2-digit',
separator: this.context.options.defaultRangeSeparator
});
};
// Given an array of foreground segments, render a DOM element for each, computes position,
// and attaches to the column inner-container elements.
TimeGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
var segsByCol = this.timeGrid.groupSegsByCol(segs);
// order the segs within each column
// TODO: have groupSegsByCol do this?
for (var col = 0; col < segsByCol.length; col++) {
segsByCol[col] = this.sortEventSegs(segsByCol[col]);
}
this.segsByCol = segsByCol;
this.timeGrid.attachSegsByCol(segsByCol, this.timeGrid.fgContainerEls);
};
TimeGridEventRenderer.prototype.detachSegs = function (segs) {
segs.forEach(function (seg) {
core.removeElement(seg.el);
});
this.segsByCol = null;
};
TimeGridEventRenderer.prototype.computeSegSizes = function (allSegs) {
var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
var colCnt = timeGrid.colCnt;
timeGrid.computeSegVerticals(allSegs); // horizontals relies on this
if (segsByCol) {
for (var col = 0; col < colCnt; col++) {
this.computeSegHorizontals(segsByCol[col]); // compute horizontal coordinates, z-index's, and reorder the array
}
}
};
TimeGridEventRenderer.prototype.assignSegSizes = function (allSegs) {
var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
var colCnt = timeGrid.colCnt;
timeGrid.assignSegVerticals(allSegs); // horizontals relies on this
if (segsByCol) {
for (var col = 0; col < colCnt; col++) {
this.assignSegCss(segsByCol[col]);
}
}
};
// Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
TimeGridEventRenderer.prototype.computeEventTimeFormat = function () {
return {
hour: 'numeric',
minute: '2-digit',
meridiem: false
};
};
// Computes a default `displayEventEnd` value if one is not expliclty defined
TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () {
return true;
};
// Renders the HTML for a single event segment's default rendering
TimeGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
var eventRange = seg.eventRange;
var eventDef = eventRange.def;
var eventUi = eventRange.ui;
var allDay = eventDef.allDay;
var isDraggable = core.computeEventDraggable(this.context, eventDef, eventUi);
var isResizableFromStart = seg.isStart && core.computeEventStartResizable(this.context, eventDef, eventUi);
var isResizableFromEnd = seg.isEnd && core.computeEventEndResizable(this.context, eventDef, eventUi);
var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
var skinCss = core.cssToStr(this.getSkinCss(eventUi));
var timeText;
var fullTimeText; // more verbose time text. for the print stylesheet
var startTimeText; // just the start time text
classes.unshift('fc-time-grid-event');
// if the event appears to span more than one day...
if (core.isMultiDayRange(eventRange.range)) {
// Don't display time text on segments that run entirely through a day.
// That would appear as midnight-midnight and would look dumb.
// Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
if (seg.isStart || seg.isEnd) {
var unzonedStart = seg.start;
var unzonedEnd = seg.end;
timeText = this._getTimeText(unzonedStart, unzonedEnd, allDay); // TODO: give the timezones
fullTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, this.fullTimeFormat);
startTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, null, false); // displayEnd=false
}
}
else {
// Display the normal time text for the *event's* times
timeText = this.getTimeText(eventRange);
fullTimeText = this.getTimeText(eventRange, this.fullTimeFormat);
startTimeText = this.getTimeText(eventRange, null, false); // displayEnd=false
}
return '
' +
'' +
(timeText ?
'
' +
'' + core.htmlEscape(timeText) + '' +
'
' :
'') +
(eventDef.title ?
'
' +
core.htmlEscape(eventDef.title) +
'
' :
'') +
'
' +
/* TODO: write CSS for this
(isResizableFromStart ?
'' :
''
) +
*/
(isResizableFromEnd ?
'' :
'') +
'';
};
// Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
// Assumed the segs are already ordered.
// NOTE: Also reorders the given array by date!
TimeGridEventRenderer.prototype.computeSegHorizontals = function (segs) {
var levels;
var level0;
var i;
levels = buildSlotSegLevels(segs);
computeForwardSlotSegs(levels);
if ((level0 = levels[0])) {
for (i = 0; i < level0.length; i++) {
computeSlotSegPressures(level0[i]);
}
for (i = 0; i < level0.length; i++) {
this.computeSegForwardBack(level0[i], 0, 0);
}
}
};
// Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
// from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
// seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
//
// The segment might be part of a "series", which means consecutive segments with the same pressure
// who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
// segments behind this one in the current series, and `seriesBackwardCoord` is the starting
// coordinate of the first segment in the series.
TimeGridEventRenderer.prototype.computeSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) {
var forwardSegs = seg.forwardSegs;
var i;
if (seg.forwardCoord === undefined) { // not already computed
if (!forwardSegs.length) {
// if there are no forward segments, this segment should butt up against the edge
seg.forwardCoord = 1;
}
else {
// sort highest pressure first
this.sortForwardSegs(forwardSegs);
// this segment's forwardCoord will be calculated from the backwardCoord of the
// highest-pressure forward segment.
this.computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
seg.forwardCoord = forwardSegs[0].backwardCoord;
}
// calculate the backwardCoord from the forwardCoord. consider the series
seg.backwardCoord = seg.forwardCoord -
(seg.forwardCoord - seriesBackwardCoord) / // available width for series
(seriesBackwardPressure + 1); // # of segments in the series
// use this segment's coordinates to computed the coordinates of the less-pressurized
// forward segments
for (i = 0; i < forwardSegs.length; i++) {
this.computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
}
}
};
TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) {
var objs = forwardSegs.map(buildTimeGridSegCompareObj);
var specs = [
// put higher-pressure first
{ field: 'forwardPressure', order: -1 },
// put segments that are closer to initial edge first (and favor ones with no coords yet)
{ field: 'backwardCoord', order: 1 }
].concat(this.context.eventOrderSpecs);
objs.sort(function (obj0, obj1) {
return core.compareByFieldSpecs(obj0, obj1, specs);
});
return objs.map(function (c) {
return c._seg;
});
};
// Given foreground event segments that have already had their position coordinates computed,
// assigns position-related CSS values to their elements.
TimeGridEventRenderer.prototype.assignSegCss = function (segs) {
for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
var seg = segs_1[_i];
core.applyStyle(seg.el, this.generateSegCss(seg));
if (seg.level > 0) {
seg.el.classList.add('fc-time-grid-event-inset');
}
// if the event is short that the title will be cut off,
// attach a className that condenses the title into the time area.
if (seg.eventRange.def.title && seg.bottom - seg.top < 30) {
seg.el.classList.add('fc-short'); // TODO: "condensed" is a better name
}
}
};
// Generates an object with CSS properties/values that should be applied to an event segment element.
// Contains important positioning-related properties that should be applied to any event element, customized or not.
TimeGridEventRenderer.prototype.generateSegCss = function (seg) {
var shouldOverlap = this.context.options.slotEventOverlap;
var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
var isRtl = this.context.isRtl;
var left; // amount of space from left edge, a fraction of the total width
var right; // amount of space from right edge, a fraction of the total width
if (shouldOverlap) {
// double the width, but don't go beyond the maximum forward coordinate (1.0)
forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
}
if (isRtl) {
left = 1 - forwardCoord;
right = backwardCoord;
}
else {
left = backwardCoord;
right = 1 - forwardCoord;
}
props.zIndex = seg.level + 1; // convert from 0-base to 1-based
props.left = left * 100 + '%';
props.right = right * 100 + '%';
if (shouldOverlap && seg.forwardPressure) {
// add padding to the edge so that forward stacked events don't cover the resizer's icon
props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
}
return props;
};
return TimeGridEventRenderer;
}(core.FgEventRenderer));
// Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
// left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date.
function buildSlotSegLevels(segs) {
var levels = [];
var i;
var seg;
var j;
for (i = 0; i < segs.length; i++) {
seg = segs[i];
// go through all the levels and stop on the first level where there are no collisions
for (j = 0; j < levels.length; j++) {
if (!computeSlotSegCollisions(seg, levels[j]).length) {
break;
}
}
seg.level = j;
(levels[j] || (levels[j] = [])).push(seg);
}
return levels;
}
// For every segment, figure out the other segments that are in subsequent
// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
function computeForwardSlotSegs(levels) {
var i;
var level;
var j;
var seg;
var k;
for (i = 0; i < levels.length; i++) {
level = levels[i];
for (j = 0; j < level.length; j++) {
seg = level[j];
seg.forwardSegs = [];
for (k = i + 1; k < levels.length; k++) {
computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
}
}
}
}
// Figure out which path forward (via seg.forwardSegs) results in the longest path until
// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
function computeSlotSegPressures(seg) {
var forwardSegs = seg.forwardSegs;
var forwardPressure = 0;
var i;
var forwardSeg;
if (seg.forwardPressure === undefined) { // not already computed
for (i = 0; i < forwardSegs.length; i++) {
forwardSeg = forwardSegs[i];
// figure out the child's maximum forward path
computeSlotSegPressures(forwardSeg);
// either use the existing maximum, or use the child's forward pressure
// plus one (for the forwardSeg itself)
forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure);
}
seg.forwardPressure = forwardPressure;
}
}
// Find all the segments in `otherSegs` that vertically collide with `seg`.
// Append into an optionally-supplied `results` array and return.
function computeSlotSegCollisions(seg, otherSegs, results) {
if (results === void 0) { results = []; }
for (var i = 0; i < otherSegs.length; i++) {
if (isSlotSegCollision(seg, otherSegs[i])) {
results.push(otherSegs[i]);
}
}
return results;
}
// Do these segments occupy the same vertical space?
function isSlotSegCollision(seg1, seg2) {
return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
}
function buildTimeGridSegCompareObj(seg) {
var obj = core.buildSegCompareObj(seg);
obj.forwardPressure = seg.forwardPressure;
obj.backwardCoord = seg.backwardCoord;
return obj;
}
var TimeGridMirrorRenderer = /** @class */ (function (_super) {
__extends(TimeGridMirrorRenderer, _super);
function TimeGridMirrorRenderer() {
return _super !== null && _super.apply(this, arguments) || this;
}
TimeGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
this.segsByCol = this.timeGrid.groupSegsByCol(segs);
this.timeGrid.attachSegsByCol(this.segsByCol, this.timeGrid.mirrorContainerEls);
this.sourceSeg = mirrorInfo.sourceSeg;
};
TimeGridMirrorRenderer.prototype.generateSegCss = function (seg) {
var props = _super.prototype.generateSegCss.call(this, seg);
var sourceSeg = this.sourceSeg;
if (sourceSeg && sourceSeg.col === seg.col) {
var sourceSegProps = _super.prototype.generateSegCss.call(this, sourceSeg);
props.left = sourceSegProps.left;
props.right = sourceSegProps.right;
props.marginLeft = sourceSegProps.marginLeft;
props.marginRight = sourceSegProps.marginRight;
}
return props;
};
return TimeGridMirrorRenderer;
}(TimeGridEventRenderer));
var TimeGridFillRenderer = /** @class */ (function (_super) {
__extends(TimeGridFillRenderer, _super);
function TimeGridFillRenderer(timeGrid) {
var _this = _super.call(this) || this;
_this.timeGrid = timeGrid;
return _this;
}
TimeGridFillRenderer.prototype.attachSegs = function (type, segs) {
var timeGrid = this.timeGrid;
var containerEls;
// TODO: more efficient lookup
if (type === 'bgEvent') {
containerEls = timeGrid.bgContainerEls;
}
else if (type === 'businessHours') {
containerEls = timeGrid.businessContainerEls;
}
else if (type === 'highlight') {
containerEls = timeGrid.highlightContainerEls;
}
timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls);
return segs.map(function (seg) {
return seg.el;
});
};
TimeGridFillRenderer.prototype.computeSegSizes = function (segs) {
this.timeGrid.computeSegVerticals(segs);
};
TimeGridFillRenderer.prototype.assignSegSizes = function (segs) {
this.timeGrid.assignSegVerticals(segs);
};
return TimeGridFillRenderer;
}(core.FillRenderer));
/* A component that renders one or more columns of vertical time slots
----------------------------------------------------------------------------------------------------------------------*/
// potential nice values for the slot-duration and interval-duration
// from largest to smallest
var AGENDA_STOCK_SUB_DURATIONS = [
{ hours: 1 },
{ minutes: 30 },
{ minutes: 15 },
{ seconds: 30 },
{ seconds: 15 }
];
var TimeGrid = /** @class */ (function (_super) {
__extends(TimeGrid, _super);
function TimeGrid(el, renderProps) {
var _this = _super.call(this, el) || this;
_this.isSlatSizesDirty = false;
_this.isColSizesDirty = false;
_this.processOptions = core.memoize(_this._processOptions);
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton);
_this.renderSlats = core.memoizeRendering(_this._renderSlats, null, [_this.renderSkeleton]);
_this.renderColumns = core.memoizeRendering(_this._renderColumns, _this._unrenderColumns, [_this.renderSkeleton]);
_this.renderProps = renderProps;
var renderColumns = _this.renderColumns;
var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this);
var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this);
_this.mirrorRenderer = new TimeGridMirrorRenderer(_this);
_this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderColumns]);
_this.renderDateSelection = core.memoizeRendering(_this._renderDateSelection, _this._unrenderDateSelection, [renderColumns]);
_this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderColumns]);
_this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderColumns]);
_this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
_this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderColumns]);
_this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderColumns]);
return _this;
}
/* Options
------------------------------------------------------------------------------------------------------------------*/
// Parses various options into properties of this object
// MUST have context already set
TimeGrid.prototype._processOptions = function (options) {
var slotDuration = options.slotDuration, snapDuration = options.snapDuration;
var snapsPerSlot;
var input;
slotDuration = core.createDuration(slotDuration);
snapDuration = snapDuration ? core.createDuration(snapDuration) : slotDuration;
snapsPerSlot = core.wholeDivideDurations(slotDuration, snapDuration);
if (snapsPerSlot === null) {
snapDuration = slotDuration;
snapsPerSlot = 1;
// TODO: say warning?
}
this.slotDuration = slotDuration;
this.snapDuration = snapDuration;
this.snapsPerSlot = snapsPerSlot;
// might be an array value (for TimelineView).
// if so, getting the most granular entry (the last one probably).
input = options.slotLabelFormat;
if (Array.isArray(input)) {
input = input[input.length - 1];
}
this.labelFormat = core.createFormatter(input || {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: true,
meridiem: 'short'
});
input = options.slotLabelInterval;
this.labelInterval = input ?
core.createDuration(input) :
this.computeLabelInterval(slotDuration);
};
// Computes an automatic value for slotLabelInterval
TimeGrid.prototype.computeLabelInterval = function (slotDuration) {
var i;
var labelInterval;
var slotsPerLabel;
// find the smallest stock label interval that results in more than one slots-per-label
for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
labelInterval = core.createDuration(AGENDA_STOCK_SUB_DURATIONS[i]);
slotsPerLabel = core.wholeDivideDurations(labelInterval, slotDuration);
if (slotsPerLabel !== null && slotsPerLabel > 1) {
return labelInterval;
}
}
return slotDuration; // fall back
};
/* Rendering
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype.render = function (props, context) {
this.processOptions(context.options);
var cells = props.cells;
this.colCnt = cells.length;
this.renderSkeleton(context.theme);
this.renderSlats(props.dateProfile);
this.renderColumns(props.cells, props.dateProfile);
this.renderBusinessHours(context, props.businessHourSegs);
this.renderDateSelection(props.dateSelectionSegs);
this.renderFgEvents(context, props.fgEventSegs);
this.renderBgEvents(context, props.bgEventSegs);
this.renderEventSelection(props.eventSelection);
this.renderEventDrag(props.eventDrag);
this.renderEventResize(props.eventResize);
};
TimeGrid.prototype.destroy = function () {
_super.prototype.destroy.call(this);
// should unrender everything else too
this.renderSlats.unrender();
this.renderColumns.unrender();
this.renderSkeleton.unrender();
};
TimeGrid.prototype.updateSize = function (isResize) {
var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
if (isResize || this.isSlatSizesDirty) {
this.buildSlatPositions();
this.isSlatSizesDirty = false;
}
if (isResize || this.isColSizesDirty) {
this.buildColPositions();
this.isColSizesDirty = false;
}
fillRenderer.computeSizes(isResize);
eventRenderer.computeSizes(isResize);
mirrorRenderer.computeSizes(isResize);
fillRenderer.assignSizes(isResize);
eventRenderer.assignSizes(isResize);
mirrorRenderer.assignSizes(isResize);
};
TimeGrid.prototype._renderSkeleton = function (theme) {
var el = this.el;
el.innerHTML =
'
' +
'
' +
'';
this.rootBgContainerEl = el.querySelector('.fc-bg');
this.slatContainerEl = el.querySelector('.fc-slats');
this.bottomRuleEl = el.querySelector('.fc-divider');
};
TimeGrid.prototype._renderSlats = function (dateProfile) {
var theme = this.context.theme;
this.slatContainerEl.innerHTML =
'
' +
this.renderSlatRowHtml(dateProfile) +
'
';
this.slatEls = core.findElements(this.slatContainerEl, 'tr');
this.slatPositions = new core.PositionCache(this.el, this.slatEls, false, true // vertical
);
this.isSlatSizesDirty = true;
};
// Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
TimeGrid.prototype.renderSlatRowHtml = function (dateProfile) {
var _a = this.context, dateEnv = _a.dateEnv, theme = _a.theme, isRtl = _a.isRtl;
var html = '';
var dayStart = core.startOfDay(dateProfile.renderRange.start);
var slotTime = dateProfile.minTime;
var slotIterator = core.createDuration(0);
var slotDate; // will be on the view's first day, but we only care about its time
var isLabeled;
var axisHtml;
// Calculate the time for each slot
while (core.asRoughMs(slotTime) < core.asRoughMs(dateProfile.maxTime)) {
slotDate = dateEnv.add(dayStart, slotTime);
isLabeled = core.wholeDivideDurations(slotIterator, this.labelInterval) !== null;
axisHtml =
'
' +
(isLabeled ?
'' + // for matchCellWidths
core.htmlEscape(dateEnv.format(slotDate, this.labelFormat)) +
'' :
'') +
' | ';
html +=
'
' +
(!isRtl ? axisHtml : '') +
' | ' +
(isRtl ? axisHtml : '') +
'
';
slotTime = core.addDurations(slotTime, this.slotDuration);
slotIterator = core.addDurations(slotIterator, this.slotDuration);
}
return html;
};
TimeGrid.prototype._renderColumns = function (cells, dateProfile) {
var _a = this.context, calendar = _a.calendar, view = _a.view, isRtl = _a.isRtl, theme = _a.theme, dateEnv = _a.dateEnv;
var bgRow = new daygrid.DayBgRow(this.context);
this.rootBgContainerEl.innerHTML =
'
' +
bgRow.renderHtml({
cells: cells,
dateProfile: dateProfile,
renderIntroHtml: this.renderProps.renderBgIntroHtml
}) +
'
';
this.colEls = core.findElements(this.el, '.fc-day, .fc-disabled-day');
for (var col = 0; col < this.colCnt; col++) {
calendar.publiclyTrigger('dayRender', [
{
date: dateEnv.toDate(cells[col].date),
el: this.colEls[col],
view: view
}
]);
}
if (isRtl) {
this.colEls.reverse();
}
this.colPositions = new core.PositionCache(this.el, this.colEls, true, // horizontal
false);
this.renderContentSkeleton();
this.isColSizesDirty = true;
};
TimeGrid.prototype._unrenderColumns = function () {
this.unrenderContentSkeleton();
};
/* Content Skeleton
------------------------------------------------------------------------------------------------------------------*/
// Renders the DOM that the view's content will live in
TimeGrid.prototype.renderContentSkeleton = function () {
var isRtl = this.context.isRtl;
var parts = [];
var skeletonEl;
parts.push(this.renderProps.renderIntroHtml());
for (var i = 0; i < this.colCnt; i++) {
parts.push('
' +
'' +
' ' +
' ' +
' ' +
' ' +
' ' +
' ' +
' | ');
}
if (isRtl) {
parts.reverse();
}
skeletonEl = this.contentSkeletonEl = core.htmlToElement('
' +
'
' +
'' + parts.join('') + '
' +
'
' +
'
');
this.colContainerEls = core.findElements(skeletonEl, '.fc-content-col');
this.mirrorContainerEls = core.findElements(skeletonEl, '.fc-mirror-container');
this.fgContainerEls = core.findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)');
this.bgContainerEls = core.findElements(skeletonEl, '.fc-bgevent-container');
this.highlightContainerEls = core.findElements(skeletonEl, '.fc-highlight-container');
this.businessContainerEls = core.findElements(skeletonEl, '.fc-business-container');
if (isRtl) {
this.colContainerEls.reverse();
this.mirrorContainerEls.reverse();
this.fgContainerEls.reverse();
this.bgContainerEls.reverse();
this.highlightContainerEls.reverse();
this.businessContainerEls.reverse();
}
this.el.appendChild(skeletonEl);
};
TimeGrid.prototype.unrenderContentSkeleton = function () {
core.removeElement(this.contentSkeletonEl);
};
// Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
TimeGrid.prototype.groupSegsByCol = function (segs) {
var segsByCol = [];
var i;
for (i = 0; i < this.colCnt; i++) {
segsByCol.push([]);
}
for (i = 0; i < segs.length; i++) {
segsByCol[segs[i].col].push(segs[i]);
}
return segsByCol;
};
// Given segments grouped by column, insert the segments' elements into a parallel array of container
// elements, each living within a column.
TimeGrid.prototype.attachSegsByCol = function (segsByCol, containerEls) {
var col;
var segs;
var i;
for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
segs = segsByCol[col];
for (i = 0; i < segs.length; i++) {
containerEls[col].appendChild(segs[i].el);
}
}
};
/* Now Indicator
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype.getNowIndicatorUnit = function () {
return 'minute'; // will refresh on the minute
};
TimeGrid.prototype.renderNowIndicator = function (segs, date) {
// HACK: if date columns not ready for some reason (scheduler)
if (!this.colContainerEls) {
return;
}
var top = this.computeDateTop(date);
var nodes = [];
var i;
// render lines within the columns
for (i = 0; i < segs.length; i++) {
var lineEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' });
lineEl.style.top = top + 'px';
this.colContainerEls[segs[i].col].appendChild(lineEl);
nodes.push(lineEl);
}
// render an arrow over the axis
if (segs.length > 0) { // is the current time in view?
var arrowEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' });
arrowEl.style.top = top + 'px';
this.contentSkeletonEl.appendChild(arrowEl);
nodes.push(arrowEl);
}
this.nowIndicatorEls = nodes;
};
TimeGrid.prototype.unrenderNowIndicator = function () {
if (this.nowIndicatorEls) {
this.nowIndicatorEls.forEach(core.removeElement);
this.nowIndicatorEls = null;
}
};
/* Coordinates
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype.getTotalSlatHeight = function () {
return this.slatContainerEl.getBoundingClientRect().height;
};
// Computes the top coordinate, relative to the bounds of the grid, of the given date.
// A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
TimeGrid.prototype.computeDateTop = function (when, startOfDayDate) {
if (!startOfDayDate) {
startOfDayDate = core.startOfDay(when);
}
return this.computeTimeTop(core.createDuration(when.valueOf() - startOfDayDate.valueOf()));
};
// Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
TimeGrid.prototype.computeTimeTop = function (duration) {
var len = this.slatEls.length;
var dateProfile = this.props.dateProfile;
var slatCoverage = (duration.milliseconds - core.asRoughMs(dateProfile.minTime)) / core.asRoughMs(this.slotDuration); // floating-point value of # of slots covered
var slatIndex;
var slatRemainder;
// compute a floating-point number for how many slats should be progressed through.
// from 0 to number of slats (inclusive)
// constrained because minTime/maxTime might be customized.
slatCoverage = Math.max(0, slatCoverage);
slatCoverage = Math.min(len, slatCoverage);
// an integer index of the furthest whole slat
// from 0 to number slats (*exclusive*, so len-1)
slatIndex = Math.floor(slatCoverage);
slatIndex = Math.min(slatIndex, len - 1);
// how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
// could be 1.0 if slatCoverage is covering *all* the slots
slatRemainder = slatCoverage - slatIndex;
return this.slatPositions.tops[slatIndex] +
this.slatPositions.getHeight(slatIndex) * slatRemainder;
};
// For each segment in an array, computes and assigns its top and bottom properties
TimeGrid.prototype.computeSegVerticals = function (segs) {
var options = this.context.options;
var eventMinHeight = options.timeGridEventMinHeight;
var i;
var seg;
var dayDate;
for (i = 0; i < segs.length; i++) {
seg = segs[i];
dayDate = this.props.cells[seg.col].date;
seg.top = this.computeDateTop(seg.start, dayDate);
seg.bottom = Math.max(seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate));
}
};
// Given segments that already have their top/bottom properties computed, applies those values to
// the segments' elements.
TimeGrid.prototype.assignSegVerticals = function (segs) {
var i;
var seg;
for (i = 0; i < segs.length; i++) {
seg = segs[i];
core.applyStyle(seg.el, this.generateSegVerticalCss(seg));
}
};
// Generates an object with CSS properties for the top/bottom coordinates of a segment element
TimeGrid.prototype.generateSegVerticalCss = function (seg) {
return {
top: seg.top,
bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
};
};
/* Sizing
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype.buildPositionCaches = function () {
this.buildColPositions();
this.buildSlatPositions();
};
TimeGrid.prototype.buildColPositions = function () {
this.colPositions.build();
};
TimeGrid.prototype.buildSlatPositions = function () {
this.slatPositions.build();
};
/* Hit System
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype.positionToHit = function (positionLeft, positionTop) {
var dateEnv = this.context.dateEnv;
var _a = this, snapsPerSlot = _a.snapsPerSlot, slatPositions = _a.slatPositions, colPositions = _a.colPositions;
var colIndex = colPositions.leftToIndex(positionLeft);
var slatIndex = slatPositions.topToIndex(positionTop);
if (colIndex != null && slatIndex != null) {
var slatTop = slatPositions.tops[slatIndex];
var slatHeight = slatPositions.getHeight(slatIndex);
var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
var dayDate = this.props.cells[colIndex].date;
var time = core.addDurations(this.props.dateProfile.minTime, core.multiplyDuration(this.snapDuration, snapIndex));
var start = dateEnv.add(dayDate, time);
var end = dateEnv.add(start, this.snapDuration);
return {
col: colIndex,
dateSpan: {
range: { start: start, end: end },
allDay: false
},
dayEl: this.colEls[colIndex],
relativeRect: {
left: colPositions.lefts[colIndex],
right: colPositions.rights[colIndex],
top: slatTop,
bottom: slatTop + slatHeight
}
};
}
};
/* Event Drag Visualization
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype._renderEventDrag = function (state) {
if (state) {
this.eventRenderer.hideByHash(state.affectedInstances);
if (state.isEvent) {
this.mirrorRenderer.renderSegs(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
}
else {
this.fillRenderer.renderSegs('highlight', this.context, state.segs);
}
}
};
TimeGrid.prototype._unrenderEventDrag = function (state) {
if (state) {
this.eventRenderer.showByHash(state.affectedInstances);
if (state.isEvent) {
this.mirrorRenderer.unrender(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
}
else {
this.fillRenderer.unrender('highlight', this.context);
}
}
};
/* Event Resize Visualization
------------------------------------------------------------------------------------------------------------------*/
TimeGrid.prototype._renderEventResize = function (state) {
if (state) {
this.eventRenderer.hideByHash(state.affectedInstances);
this.mirrorRenderer.renderSegs(this.context, state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
}
};
TimeGrid.prototype._unrenderEventResize = function (state) {
if (state) {
this.eventRenderer.showByHash(state.affectedInstances);
this.mirrorRenderer.unrender(this.context, state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
}
};
/* Selection
------------------------------------------------------------------------------------------------------------------*/
// Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight.
TimeGrid.prototype._renderDateSelection = function (segs) {
if (segs) {
if (this.context.options.selectMirror) {
this.mirrorRenderer.renderSegs(this.context, segs, { isSelecting: true });
}
else {
this.fillRenderer.renderSegs('highlight', this.context, segs);
}
}
};
TimeGrid.prototype._unrenderDateSelection = function (segs) {
if (segs) {
if (this.context.options.selectMirror) {
this.mirrorRenderer.unrender(this.context, segs, { isSelecting: true });
}
else {
this.fillRenderer.unrender('highlight', this.context);
}
}
};
return TimeGrid;
}(core.DateComponent));
var AllDaySplitter = /** @class */ (function (_super) {
__extends(AllDaySplitter, _super);
function AllDaySplitter() {
return _super !== null && _super.apply(this, arguments) || this;
}
AllDaySplitter.prototype.getKeyInfo = function () {
return {
allDay: {},
timed: {}
};
};
AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
if (dateSpan.allDay) {
return ['allDay'];
}
else {
return ['timed'];
}
};
AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
if (!eventDef.allDay) {
return ['timed'];
}
else if (core.hasBgRendering(eventDef)) {
return ['timed', 'allDay'];
}
else {
return ['allDay'];
}
};
return AllDaySplitter;
}(core.Splitter));
var TIMEGRID_ALL_DAY_EVENT_LIMIT = 5;
var WEEK_HEADER_FORMAT = core.createFormatter({ week: 'short' });
/* An abstract class for all timegrid-related views. Displays one more columns with time slots running vertically.
----------------------------------------------------------------------------------------------------------------------*/
// Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
// Responsible for managing width/height.
var AbstractTimeGridView = /** @class */ (function (_super) {
__extends(AbstractTimeGridView, _super);
function AbstractTimeGridView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.splitter = new AllDaySplitter();
_this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton);
/* Header Render Methods
------------------------------------------------------------------------------------------------------------------*/
// Generates the HTML that will go before the day-of week header cells
_this.renderHeadIntroHtml = function () {
var _a = _this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options;
var range = _this.props.dateProfile.renderRange;
var dayCnt = core.diffDays(range.start, range.end);
var weekText;
if (options.weekNumbers) {
weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT);
return '' +
'';
}
else {
return '';
}
};
/* Time Grid Render Methods
------------------------------------------------------------------------------------------------------------------*/
// Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column.
_this.renderTimeGridBgIntroHtml = function () {
var theme = _this.context.theme;
return '
| ';
};
// Generates the HTML that goes before all other types of cells.
// Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
_this.renderTimeGridIntroHtml = function () {
return '
| ';
};
/* Day Grid Render Methods
------------------------------------------------------------------------------------------------------------------*/
// Generates the HTML that goes before the all-day cells
_this.renderDayGridBgIntroHtml = function () {
var _a = _this.context, theme = _a.theme, options = _a.options;
return '' +
'
' +
'' + // needed for matchCellWidths
core.getAllDayHtml(options) +
'' +
' | ';
};
// Generates the HTML that goes before all other types of cells.
// Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
_this.renderDayGridIntroHtml = function () {
return '
| ';
};
return _this;
}
AbstractTimeGridView.prototype.render = function (props, context) {
_super.prototype.render.call(this, props, context);
this.renderSkeleton(context);
};
AbstractTimeGridView.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.renderSkeleton.unrender();
};
AbstractTimeGridView.prototype._renderSkeleton = function (context) {
this.el.classList.add('fc-timeGrid-view');
this.el.innerHTML = this.renderSkeletonHtml();
this.scroller = new core.ScrollComponent('hidden', // overflow x
'auto' // overflow y
);
var timeGridWrapEl = this.scroller.el;
this.el.querySelector('.fc-body > tr > td').appendChild(timeGridWrapEl);
timeGridWrapEl.classList.add('fc-time-grid-container');
var timeGridEl = core.createElement('div', { className: 'fc-time-grid' });
timeGridWrapEl.appendChild(timeGridEl);
this.timeGrid = new TimeGrid(timeGridEl, {
renderBgIntroHtml: this.renderTimeGridBgIntroHtml,
renderIntroHtml: this.renderTimeGridIntroHtml
});
if (context.options.allDaySlot) { // should we display the "all-day" area?
this.dayGrid = new daygrid.DayGrid(// the all-day subcomponent of this view
this.el.querySelector('.fc-day-grid'), {
renderNumberIntroHtml: this.renderDayGridIntroHtml,
renderBgIntroHtml: this.renderDayGridBgIntroHtml,
renderIntroHtml: this.renderDayGridIntroHtml,
colWeekNumbersVisible: false,
cellWeekNumbersVisible: false
});
// have the day-grid extend it's coordinate area over the
dividing the two grids
var dividerEl = this.el.querySelector('.fc-divider');
this.dayGrid.bottomCoordPadding = dividerEl.getBoundingClientRect().height;
}
};
AbstractTimeGridView.prototype._unrenderSkeleton = function () {
this.el.classList.remove('fc-timeGrid-view');
this.timeGrid.destroy();
if (this.dayGrid) {
this.dayGrid.destroy();
}
this.scroller.destroy();
};
/* Rendering
------------------------------------------------------------------------------------------------------------------*/
// Builds the HTML skeleton for the view.
// The day-grid and time-grid components will render inside containers defined by this HTML.
AbstractTimeGridView.prototype.renderSkeletonHtml = function () {
var _a = this.context, theme = _a.theme, options = _a.options;
return '' +
'
' +
(options.columnHeader ?
'' +
'' +
'' +
'
' +
'' :
'') +
'' +
'' +
'' +
(options.allDaySlot ?
'' +
'' :
'') +
' | ' +
'
' +
'' +
'
';
};
/* Now Indicator
------------------------------------------------------------------------------------------------------------------*/
AbstractTimeGridView.prototype.getNowIndicatorUnit = function () {
return this.timeGrid.getNowIndicatorUnit();
};
// subclasses should implement
// renderNowIndicator(date: DateMarker) {
// }
AbstractTimeGridView.prototype.unrenderNowIndicator = function () {
this.timeGrid.unrenderNowIndicator();
};
/* Dimensions
------------------------------------------------------------------------------------------------------------------*/
AbstractTimeGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
_super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
this.timeGrid.updateSize(isResize);
if (this.dayGrid) {
this.dayGrid.updateSize(isResize);
}
};
// Adjusts the vertical dimensions of the view to the specified values
AbstractTimeGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
var _this = this;
var eventLimit;
var scrollerHeight;
var scrollbarWidths;
// make all axis cells line up
this.axisWidth = core.matchCellWidths(core.findElements(this.el, '.fc-axis'));
// hack to give the view some height prior to timeGrid's columns being rendered
// TODO: separate setting height from scroller VS timeGrid.
if (!this.timeGrid.colEls) {
if (!isAuto) {
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.scroller.setHeight(scrollerHeight);
}
return;
}
// set of fake row elements that must compensate when scroller has scrollbars
var noScrollRowEls = core.findElements(this.el, '.fc-row').filter(function (node) {
return !_this.scroller.el.contains(node);
});
// reset all dimensions back to the original state
this.timeGrid.bottomRuleEl.style.display = 'none'; // will be shown later if this
is necessary
this.scroller.clear(); // sets height to 'auto' and clears overflow
noScrollRowEls.forEach(core.uncompensateScroll);
// limit number of events in the all-day area
if (this.dayGrid) {
this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
eventLimit = this.context.options.eventLimit;
if (eventLimit && typeof eventLimit !== 'number') {
eventLimit = TIMEGRID_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number
}
if (eventLimit) {
this.dayGrid.limitRows(eventLimit);
}
}
if (!isAuto) { // should we force dimensions of the scroll container?
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.scroller.setHeight(scrollerHeight);
scrollbarWidths = this.scroller.getScrollbarWidths();
if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
// make the all-day and header rows lines up
noScrollRowEls.forEach(function (rowEl) {
core.compensateScroll(rowEl, scrollbarWidths);
});
// the scrollbar compensation might have changed text flow, which might affect height, so recalculate
// and reapply the desired height to the scroller.
scrollerHeight = this.computeScrollerHeight(viewHeight);
this.scroller.setHeight(scrollerHeight);
}
// guarantees the same scrollbar widths
this.scroller.lockOverflow(scrollbarWidths);
// if there's any space below the slats, show the horizontal rule.
// this won't cause any new overflow, because lockOverflow already called.
if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
this.timeGrid.bottomRuleEl.style.display = '';
}
}
};
// given a desired total height of the view, returns what the height of the scroller should be
AbstractTimeGridView.prototype.computeScrollerHeight = function (viewHeight) {
return viewHeight -
core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
};
/* Scroll
------------------------------------------------------------------------------------------------------------------*/
// Computes the initial pre-configured scroll state prior to allowing the user to change it
AbstractTimeGridView.prototype.computeDateScroll = function (duration) {
var top = this.timeGrid.computeTimeTop(duration);
// zoom can give weird floating-point values. rather scroll a little bit further
top = Math.ceil(top);
if (top) {
top++; // to overcome top border that slots beyond the first have. looks better
}
return { top: top };
};
AbstractTimeGridView.prototype.queryDateScroll = function () {
return { top: this.scroller.getScrollTop() };
};
AbstractTimeGridView.prototype.applyDateScroll = function (scroll) {
if (scroll.top !== undefined) {
this.scroller.setScrollTop(scroll.top);
}
};
// Generates an HTML attribute string for setting the width of the axis, if it is known
AbstractTimeGridView.prototype.axisStyleAttr = function () {
if (this.axisWidth != null) {
return 'style="width:' + this.axisWidth + 'px"';
}
return '';
};
return AbstractTimeGridView;
}(core.View));
AbstractTimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering
var SimpleTimeGrid = /** @class */ (function (_super) {
__extends(SimpleTimeGrid, _super);
function SimpleTimeGrid(timeGrid) {
var _this = _super.call(this, timeGrid.el) || this;
_this.buildDayRanges = core.memoize(buildDayRanges);
_this.slicer = new TimeGridSlicer();
_this.timeGrid = timeGrid;
return _this;
}
SimpleTimeGrid.prototype.firstContext = function (context) {
context.calendar.registerInteractiveComponent(this, {
el: this.timeGrid.el
});
};
SimpleTimeGrid.prototype.destroy = function () {
_super.prototype.destroy.call(this);
this.context.calendar.unregisterInteractiveComponent(this);
};
SimpleTimeGrid.prototype.render = function (props, context) {
var dateEnv = this.context.dateEnv;
var dateProfile = props.dateProfile, dayTable = props.dayTable;
var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, dateEnv);
var timeGrid = this.timeGrid;
timeGrid.receiveContext(context); // hack because context is used in sliceProps
timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, context.calendar, timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }), context);
};
SimpleTimeGrid.prototype.renderNowIndicator = function (date) {
this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(date, this.timeGrid, this.dayRanges), date);
};
SimpleTimeGrid.prototype.buildPositionCaches = function () {
this.timeGrid.buildPositionCaches();
};
SimpleTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
if (rawHit) {
return {
component: this.timeGrid,
dateSpan: rawHit.dateSpan,
dayEl: rawHit.dayEl,
rect: {
left: rawHit.relativeRect.left,
right: rawHit.relativeRect.right,
top: rawHit.relativeRect.top,
bottom: rawHit.relativeRect.bottom
},
layer: 0
};
}
};
return SimpleTimeGrid;
}(core.DateComponent));
function buildDayRanges(dayTable, dateProfile, dateEnv) {
var ranges = [];
for (var _i = 0, _a = dayTable.headerDates; _i < _a.length; _i++) {
var date = _a[_i];
ranges.push({
start: dateEnv.add(date, dateProfile.minTime),
end: dateEnv.add(date, dateProfile.maxTime)
});
}
return ranges;
}
var TimeGridSlicer = /** @class */ (function (_super) {
__extends(TimeGridSlicer, _super);
function TimeGridSlicer() {
return _super !== null && _super.apply(this, arguments) || this;
}
TimeGridSlicer.prototype.sliceRange = function (range, dayRanges) {
var segs = [];
for (var col = 0; col < dayRanges.length; col++) {
var segRange = core.intersectRanges(range, dayRanges[col]);
if (segRange) {
segs.push({
start: segRange.start,
end: segRange.end,
isStart: segRange.start.valueOf() === range.start.valueOf(),
isEnd: segRange.end.valueOf() === range.end.valueOf(),
col: col
});
}
}
return segs;
};
return TimeGridSlicer;
}(core.Slicer));
var TimeGridView = /** @class */ (function (_super) {
__extends(TimeGridView, _super);
function TimeGridView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.buildDayTable = core.memoize(buildDayTable);
return _this;
}
TimeGridView.prototype.render = function (props, context) {
_super.prototype.render.call(this, props, context); // for flags for updateSize. also _renderSkeleton/_unrenderSkeleton
var _a = this.props, dateProfile = _a.dateProfile, dateProfileGenerator = _a.dateProfileGenerator;
var nextDayThreshold = context.nextDayThreshold;
var dayTable = this.buildDayTable(dateProfile, dateProfileGenerator);
var splitProps = this.splitter.splitProps(props);
if (this.header) {
this.header.receiveProps({
dateProfile: dateProfile,
dates: dayTable.headerDates,
datesRepDistinctDays: true,
renderIntroHtml: this.renderHeadIntroHtml
}, context);
}
this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile,
dayTable: dayTable }), context);
if (this.simpleDayGrid) {
this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile,
dayTable: dayTable,
nextDayThreshold: nextDayThreshold, isRigid: false }), context);
}
this.startNowIndicator(dateProfile, dateProfileGenerator);
};
TimeGridView.prototype._renderSkeleton = function (context) {
_super.prototype._renderSkeleton.call(this, context);
if (context.options.columnHeader) {
this.header = new core.DayHeader(this.el.querySelector('.fc-head-container'));
}
this.simpleTimeGrid = new SimpleTimeGrid(this.timeGrid);
if (this.dayGrid) {
this.simpleDayGrid = new daygrid.SimpleDayGrid(this.dayGrid);
}
};
TimeGridView.prototype._unrenderSkeleton = function () {
_super.prototype._unrenderSkeleton.call(this);
if (this.header) {
this.header.destroy();
}
this.simpleTimeGrid.destroy();
if (this.simpleDayGrid) {
this.simpleDayGrid.destroy();
}
};
TimeGridView.prototype.renderNowIndicator = function (date) {
this.simpleTimeGrid.renderNowIndicator(date);
};
return TimeGridView;
}(AbstractTimeGridView));
function buildDayTable(dateProfile, dateProfileGenerator) {
var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
return new core.DayTable(daySeries, false);
}
var main = core.createPlugin({
defaultView: 'timeGridWeek',
views: {
timeGrid: {
class: TimeGridView,
allDaySlot: true,
slotDuration: '00:30:00',
slotEventOverlap: true // a bad name. confused with overlap/constraint system
},
timeGridDay: {
type: 'timeGrid',
duration: { days: 1 }
},
timeGridWeek: {
type: 'timeGrid',
duration: { weeks: 1 }
}
}
});
exports.AbstractTimeGridView = AbstractTimeGridView;
exports.TimeGrid = TimeGrid;
exports.TimeGridSlicer = TimeGridSlicer;
exports.TimeGridView = TimeGridView;
exports.buildDayRanges = buildDayRanges;
exports.buildDayTable = buildDayTable;
exports.default = main;
Object.defineProperty(exports, '__esModule', { value: true });
}));