2167 lines
95 KiB
JavaScript
2167 lines
95 KiB
JavaScript
|
/*!
|
||
|
FullCalendar v5.10.2
|
||
|
Docs & License: https://fullcalendar.io/
|
||
|
(c) 2021 Adam Shaw
|
||
|
*/
|
||
|
'use strict';
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
||
|
var common = require('@fullcalendar/common');
|
||
|
var tslib = require('tslib');
|
||
|
|
||
|
common.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;
|
||
|
// 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 targetEl = ev.target;
|
||
|
if (!_this.shouldIgnoreMove) {
|
||
|
targetEl.addEventListener('touchmove', _this.handleTouchMove);
|
||
|
}
|
||
|
targetEl.addEventListener('touchend', _this.handleTouchEnd);
|
||
|
targetEl.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);
|
||
|
}
|
||
|
};
|
||
|
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 targetEl = ev.target;
|
||
|
targetEl.removeEventListener('touchmove', _this.handleTouchMove);
|
||
|
targetEl.removeEventListener('touchend', _this.handleTouchEnd);
|
||
|
targetEl.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 common.Emitter();
|
||
|
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 || common.elementClosest(downEl, this.handleSelector))) {
|
||
|
this.subjectEl = subjectEl;
|
||
|
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;
|
||
|
// keep wasTouchScroll around for later access
|
||
|
this.destroyScrollWatch();
|
||
|
};
|
||
|
PointerDragging.prototype.querySubjectEl = function (ev) {
|
||
|
if (this.selector) {
|
||
|
return common.elementClosest(ev.target, this.selector);
|
||
|
}
|
||
|
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 += 1;
|
||
|
setTimeout(function () {
|
||
|
ignoreMouseDepth -= 1;
|
||
|
}, common.config.touchMouseIgnoreWait);
|
||
|
}
|
||
|
// We want to attach touchmove as early as possible for Safari
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
function listenerCreated() {
|
||
|
listenerCnt += 1;
|
||
|
if (listenerCnt === 1) {
|
||
|
window.addEventListener('touchmove', onWindowTouchMove, { passive: false });
|
||
|
}
|
||
|
}
|
||
|
function listenerDestroyed() {
|
||
|
listenerCnt -= 1;
|
||
|
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; // HIGHLY SUGGESTED to set this to sidestep ShadowDOM issues
|
||
|
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';
|
||
|
common.applyStyle(mirrorEl, {
|
||
|
left: finalSourceElRect.left,
|
||
|
top: finalSourceElRect.top,
|
||
|
});
|
||
|
common.whenTransitionDone(mirrorEl, function () {
|
||
|
mirrorEl.style.transition = '';
|
||
|
callback();
|
||
|
});
|
||
|
};
|
||
|
ElementMirror.prototype.cleanup = function () {
|
||
|
if (this.mirrorEl) {
|
||
|
common.removeElement(this.mirrorEl);
|
||
|
this.mirrorEl = null;
|
||
|
}
|
||
|
this.sourceEl = null;
|
||
|
};
|
||
|
ElementMirror.prototype.updateElPosition = function () {
|
||
|
if (this.sourceEl && this.isVisible) {
|
||
|
common.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-event-dragging');
|
||
|
common.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) {
|
||
|
tslib.__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;
|
||
|
}(common.ScrollController));
|
||
|
|
||
|
var ElementScrollGeomCache = /** @class */ (function (_super) {
|
||
|
tslib.__extends(ElementScrollGeomCache, _super);
|
||
|
function ElementScrollGeomCache(el, doesListening) {
|
||
|
return _super.call(this, new common.ElementScrollController(el), doesListening) || this;
|
||
|
}
|
||
|
ElementScrollGeomCache.prototype.getEventTarget = function () {
|
||
|
return this.scrollController.el;
|
||
|
};
|
||
|
ElementScrollGeomCache.prototype.computeClientRect = function () {
|
||
|
return common.computeInnerRect(this.scrollController.el);
|
||
|
};
|
||
|
return ElementScrollGeomCache;
|
||
|
}(ScrollGeomCache));
|
||
|
|
||
|
var WindowScrollGeomCache = /** @class */ (function (_super) {
|
||
|
tslib.__extends(WindowScrollGeomCache, _super);
|
||
|
function WindowScrollGeomCache(doesListening) {
|
||
|
return _super.call(this, new common.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, scrollStartEl) {
|
||
|
if (this.isEnabled) {
|
||
|
this.scrollCaches = this.buildCaches(scrollStartEl);
|
||
|
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;
|
||
|
var scrollCaches = this.scrollCaches || [];
|
||
|
for (var _i = 0, scrollCaches_1 = scrollCaches; _i < scrollCaches_1.length; _i++) {
|
||
|
var scrollCache = scrollCaches_1[_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 (scrollStartEl) {
|
||
|
return this.queryScrollEls(scrollStartEl).map(function (el) {
|
||
|
if (el === window) {
|
||
|
return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls
|
||
|
}
|
||
|
return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls
|
||
|
});
|
||
|
};
|
||
|
AutoScroller.prototype.queryScrollEls = function (scrollStartEl) {
|
||
|
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(common.getElRoot(scrollStartEl).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) {
|
||
|
tslib.__extends(FeaturefulElementDragging, _super);
|
||
|
function FeaturefulElementDragging(containerEl, selector) {
|
||
|
var _this = _super.call(this, containerEl) || this;
|
||
|
_this.containerEl = containerEl;
|
||
|
// 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;
|
||
|
common.preventSelection(document.body);
|
||
|
common.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.isInteracting && // not destroyed via pointerdown handler
|
||
|
!_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) {
|
||
|
_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) {
|
||
|
_this.isInteracting = false;
|
||
|
common.allowSelection(document.body);
|
||
|
common.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);
|
||
|
if (selector) {
|
||
|
pointer.selector = selector;
|
||
|
}
|
||
|
_this.mirror = new ElementMirror();
|
||
|
_this.autoScroller = new AutoScroller();
|
||
|
return _this;
|
||
|
}
|
||
|
FeaturefulElementDragging.prototype.destroy = function () {
|
||
|
this.pointer.destroy();
|
||
|
// HACK: simulate a pointer-up to end the current drag
|
||
|
// TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire)
|
||
|
this.onPointerUp({});
|
||
|
};
|
||
|
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.containerEl);
|
||
|
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));
|
||
|
};
|
||
|
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;
|
||
|
}(common.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 = common.computeRect(el);
|
||
|
// will work fine for divs that have overflow:hidden
|
||
|
this.scrollCaches = common.getClippingParents(el).map(function (scrollEl) { return new ElementScrollGeomCache(scrollEl, 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()) &&
|
||
|
!common.pointInsideRect(point, scrollCache.clientRect)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
return OffsetTracker;
|
||
|
}());
|
||
|
// certain clipping containers should never constrain interactions, like <html> and <body>
|
||
|
// 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);
|
||
|
// TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
|
||
|
_this.emitter.trigger('pointerdown', ev);
|
||
|
}
|
||
|
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 common.Emitter();
|
||
|
}
|
||
|
// 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 instanceof HTMLElement) { // i.e. not a Document/ShadowRoot
|
||
|
subjectRect = common.computeRect(subjectEl);
|
||
|
adjustedPoint = common.constrainPoint(adjustedPoint, subjectRect);
|
||
|
}
|
||
|
var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top);
|
||
|
if (initialHit) {
|
||
|
if (this.useSubjectCenter && subjectRect) {
|
||
|
var slicedSubjectRect = common.intersectRects(subjectRect, initialHit.rect);
|
||
|
if (slicedSubjectRect) {
|
||
|
adjustedPoint = common.getRectCenter(slicedSubjectRect);
|
||
|
}
|
||
|
}
|
||
|
this.coordAdjust = common.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 = common.mapHash(this.droppableStore, function (interactionSettings) {
|
||
|
interactionSettings.component.prepareHits();
|
||
|
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 && // wasn't destroyed mid-drag
|
||
|
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 dead cell
|
||
|
common.rangeContainsRange(hit.dateProfile.activeRange, hit.dateSpan.range)) &&
|
||
|
(!bestHit || hit.layer > bestHit.layer)) {
|
||
|
hit.componentId = id;
|
||
|
hit.context = component.context;
|
||
|
// 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 common.isDateSpansEqual(hit0.dateSpan, hit1.dateSpan);
|
||
|
}
|
||
|
|
||
|
function buildDatePointApiWithContext(dateSpan, context) {
|
||
|
var props = {};
|
||
|
for (var _i = 0, _a = context.pluginHooks.datePointTransforms; _i < _a.length; _i++) {
|
||
|
var transform = _a[_i];
|
||
|
tslib.__assign(props, transform(dateSpan, context));
|
||
|
}
|
||
|
tslib.__assign(props, buildDatePointApi(dateSpan, context.dateEnv));
|
||
|
return props;
|
||
|
}
|
||
|
function buildDatePointApi(span, dateEnv) {
|
||
|
return {
|
||
|
date: dateEnv.toDate(span.range.start),
|
||
|
dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
|
||
|
allDay: span.allDay,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
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) {
|
||
|
tslib.__extends(DateClicking, _super);
|
||
|
function DateClicking(settings) {
|
||
|
var _this = _super.call(this, settings) || this;
|
||
|
_this.handlePointerDown = function (pev) {
|
||
|
var dragging = _this.dragging;
|
||
|
var downEl = pev.origEvent.target;
|
||
|
// do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
|
||
|
dragging.setIgnoreMove(!_this.component.isValidDateDownEl(downEl));
|
||
|
};
|
||
|
// won't even fire if moving was ignored
|
||
|
_this.handleDragEnd = function (ev) {
|
||
|
var component = _this.component;
|
||
|
var pointer = _this.dragging.pointer;
|
||
|
if (!pointer.wasTouchScroll) {
|
||
|
var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit;
|
||
|
if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
|
||
|
var context = component.context;
|
||
|
var arg = tslib.__assign(tslib.__assign({}, buildDatePointApiWithContext(initialHit.dateSpan, context)), { dayEl: initialHit.dayEl, jsEvent: ev.origEvent, view: context.viewApi || context.calendarApi.view });
|
||
|
context.emitter.trigger('dateClick', arg);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
// we DO want to watch pointer moves because otherwise finalHit won't get populated
|
||
|
_this.dragging = new FeaturefulElementDragging(settings.el);
|
||
|
_this.dragging.autoScroller.isEnabled = false;
|
||
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, common.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;
|
||
|
}(common.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) {
|
||
|
tslib.__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$1(component) : null;
|
||
|
};
|
||
|
_this.handleDragStart = function (ev) {
|
||
|
_this.component.context.calendarApi.unselect(ev); // unselect previous selections
|
||
|
};
|
||
|
_this.handleHitUpdate = function (hit, isFinal) {
|
||
|
var context = _this.component.context;
|
||
|
var dragSelection = null;
|
||
|
var isInvalid = false;
|
||
|
if (hit) {
|
||
|
var initialHit = _this.hitDragging.initialHit;
|
||
|
var disallowed = hit.componentId === initialHit.componentId
|
||
|
&& _this.isHitComboAllowed
|
||
|
&& !_this.isHitComboAllowed(initialHit, hit);
|
||
|
if (!disallowed) {
|
||
|
dragSelection = joinHitsIntoSelection(initialHit, hit, context.pluginHooks.dateSelectionTransformers);
|
||
|
}
|
||
|
if (!dragSelection || !common.isDateSelectionValid(dragSelection, hit.dateProfile, context)) {
|
||
|
isInvalid = true;
|
||
|
dragSelection = null;
|
||
|
}
|
||
|
}
|
||
|
if (dragSelection) {
|
||
|
context.dispatch({ type: 'SELECT_DATES', selection: dragSelection });
|
||
|
}
|
||
|
else if (!isFinal) { // only unselect if moved away while dragging
|
||
|
context.dispatch({ type: 'UNSELECT_DATES' });
|
||
|
}
|
||
|
if (!isInvalid) {
|
||
|
common.enableCursor();
|
||
|
}
|
||
|
else {
|
||
|
common.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
|
||
|
common.triggerDateSelect(_this.dragSelection, pev, _this.component.context);
|
||
|
_this.dragSelection = null;
|
||
|
}
|
||
|
};
|
||
|
var component = settings.component;
|
||
|
var options = component.context.options;
|
||
|
var dragging = _this.dragging = new FeaturefulElementDragging(settings.el);
|
||
|
dragging.touchScrollAllowed = false;
|
||
|
dragging.minDistance = options.selectMinDistance || 0;
|
||
|
dragging.autoScroller.isEnabled = options.dragScroll;
|
||
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, common.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;
|
||
|
}(common.Interaction));
|
||
|
function getComponentTouchDelay$1(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(common.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;
|
||
|
}
|
||
|
if (res) {
|
||
|
tslib.__assign(props, res);
|
||
|
}
|
||
|
}
|
||
|
props.range = { start: ms[0], end: ms[3] };
|
||
|
props.allDay = dateSpan0.allDay;
|
||
|
return props;
|
||
|
}
|
||
|
|
||
|
var EventDragging = /** @class */ (function (_super) {
|
||
|
tslib.__extends(EventDragging, _super);
|
||
|
function EventDragging(settings) {
|
||
|
var _this = _super.call(this, settings) || this;
|
||
|
// internal state
|
||
|
_this.subjectEl = null;
|
||
|
_this.subjectSeg = null; // the seg being selected/dragged
|
||
|
_this.isDragging = false;
|
||
|
_this.eventRange = null;
|
||
|
_this.relevantEvents = null; // the events being dragged
|
||
|
_this.receivingContext = 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 initialContext = component.context;
|
||
|
_this.subjectEl = ev.subjectEl;
|
||
|
var subjectSeg = _this.subjectSeg = common.getElSeg(ev.subjectEl);
|
||
|
var eventRange = _this.eventRange = subjectSeg.eventRange;
|
||
|
var eventInstanceId = eventRange.instance.instanceId;
|
||
|
_this.relevantEvents = common.getRelevantEvents(initialContext.getCurrentData().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(component) :
|
||
|
null;
|
||
|
if (options.fixedMirrorParent) {
|
||
|
mirror.parentNode = options.fixedMirrorParent;
|
||
|
}
|
||
|
else {
|
||
|
mirror.parentNode = common.elementClosest(origTarget, '.fc');
|
||
|
}
|
||
|
mirror.revertDuration = options.dragRevertDuration;
|
||
|
var isValid = component.isValidSegDownEl(origTarget) &&
|
||
|
!common.elementClosest(origTarget, '.fc-event-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-event-draggable');
|
||
|
};
|
||
|
_this.handleDragStart = function (ev) {
|
||
|
var initialContext = _this.component.context;
|
||
|
var eventRange = _this.eventRange;
|
||
|
var eventInstanceId = eventRange.instance.instanceId;
|
||
|
if (ev.isTouch) {
|
||
|
// need to select a different event?
|
||
|
if (eventInstanceId !== _this.component.props.eventSelection) {
|
||
|
initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId });
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// if now using mouse, but was previous touch interaction, clear selected event
|
||
|
initialContext.dispatch({ type: 'UNSELECT_EVENT' });
|
||
|
}
|
||
|
if (_this.isDragging) {
|
||
|
initialContext.calendarApi.unselect(ev); // unselect *date* selection
|
||
|
initialContext.emitter.trigger('eventDragStart', {
|
||
|
el: _this.subjectEl,
|
||
|
event: new common.EventApi(initialContext, eventRange.def, eventRange.instance),
|
||
|
jsEvent: ev.origEvent,
|
||
|
view: initialContext.viewApi,
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
_this.handleHitUpdate = function (hit, isFinal) {
|
||
|
if (!_this.isDragging) {
|
||
|
return;
|
||
|
}
|
||
|
var relevantEvents = _this.relevantEvents;
|
||
|
var initialHit = _this.hitDragging.initialHit;
|
||
|
var initialContext = _this.component.context;
|
||
|
// states based on new hit
|
||
|
var receivingContext = null;
|
||
|
var mutation = null;
|
||
|
var mutatedRelevantEvents = null;
|
||
|
var isInvalid = false;
|
||
|
var interaction = {
|
||
|
affectedEvents: relevantEvents,
|
||
|
mutatedEvents: common.createEmptyEventStore(),
|
||
|
isEvent: true,
|
||
|
};
|
||
|
if (hit) {
|
||
|
receivingContext = hit.context;
|
||
|
var receivingOptions = receivingContext.options;
|
||
|
if (initialContext === receivingContext ||
|
||
|
(receivingOptions.editable && receivingOptions.droppable)) {
|
||
|
mutation = computeEventMutation(initialHit, hit, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers);
|
||
|
if (mutation) {
|
||
|
mutatedRelevantEvents = common.applyMutationToEventStore(relevantEvents, receivingContext.getCurrentData().eventUiBases, mutation, receivingContext);
|
||
|
interaction.mutatedEvents = mutatedRelevantEvents;
|
||
|
if (!common.isInteractionValid(interaction, hit.dateProfile, receivingContext)) {
|
||
|
isInvalid = true;
|
||
|
mutation = null;
|
||
|
mutatedRelevantEvents = null;
|
||
|
interaction.mutatedEvents = common.createEmptyEventStore();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
receivingContext = null;
|
||
|
}
|
||
|
}
|
||
|
_this.displayDrag(receivingContext, interaction);
|
||
|
if (!isInvalid) {
|
||
|
common.enableCursor();
|
||
|
}
|
||
|
else {
|
||
|
common.disableCursor();
|
||
|
}
|
||
|
if (!isFinal) {
|
||
|
if (initialContext === receivingContext && // 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 || !common.getElRoot(_this.subjectEl).querySelector('.fc-event-mirror'));
|
||
|
// assign states based on new hit
|
||
|
_this.receivingContext = receivingContext;
|
||
|
_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 initialContext_1 = _this.component.context;
|
||
|
var initialView = initialContext_1.viewApi;
|
||
|
var _a = _this, receivingContext_1 = _a.receivingContext, validMutation = _a.validMutation;
|
||
|
var eventDef = _this.eventRange.def;
|
||
|
var eventInstance = _this.eventRange.instance;
|
||
|
var eventApi = new common.EventApi(initialContext_1, eventDef, eventInstance);
|
||
|
var relevantEvents_1 = _this.relevantEvents;
|
||
|
var mutatedRelevantEvents_1 = _this.mutatedRelevantEvents;
|
||
|
var finalHit = _this.hitDragging.finalHit;
|
||
|
_this.clearDrag(); // must happen after revert animation
|
||
|
initialContext_1.emitter.trigger('eventDragStop', {
|
||
|
el: _this.subjectEl,
|
||
|
event: eventApi,
|
||
|
jsEvent: ev.origEvent,
|
||
|
view: initialView,
|
||
|
});
|
||
|
if (validMutation) {
|
||
|
// dropped within same calendar
|
||
|
if (receivingContext_1 === initialContext_1) {
|
||
|
var updatedEventApi = new common.EventApi(initialContext_1, mutatedRelevantEvents_1.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents_1.instances[eventInstance.instanceId] : null);
|
||
|
initialContext_1.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: mutatedRelevantEvents_1,
|
||
|
});
|
||
|
var eventChangeArg = {
|
||
|
oldEvent: eventApi,
|
||
|
event: updatedEventApi,
|
||
|
relatedEvents: common.buildEventApis(mutatedRelevantEvents_1, initialContext_1, eventInstance),
|
||
|
revert: function () {
|
||
|
initialContext_1.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: relevantEvents_1, // the pre-change data
|
||
|
});
|
||
|
},
|
||
|
};
|
||
|
var transformed = {};
|
||
|
for (var _i = 0, _b = initialContext_1.getCurrentData().pluginHooks.eventDropTransformers; _i < _b.length; _i++) {
|
||
|
var transformer = _b[_i];
|
||
|
tslib.__assign(transformed, transformer(validMutation, initialContext_1));
|
||
|
}
|
||
|
initialContext_1.emitter.trigger('eventDrop', tslib.__assign(tslib.__assign(tslib.__assign({}, eventChangeArg), transformed), { el: ev.subjectEl, delta: validMutation.datesDelta, jsEvent: ev.origEvent, view: initialView }));
|
||
|
initialContext_1.emitter.trigger('eventChange', eventChangeArg);
|
||
|
// dropped in different calendar
|
||
|
}
|
||
|
else if (receivingContext_1) {
|
||
|
var eventRemoveArg = {
|
||
|
event: eventApi,
|
||
|
relatedEvents: common.buildEventApis(relevantEvents_1, initialContext_1, eventInstance),
|
||
|
revert: function () {
|
||
|
initialContext_1.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: relevantEvents_1,
|
||
|
});
|
||
|
},
|
||
|
};
|
||
|
initialContext_1.emitter.trigger('eventLeave', tslib.__assign(tslib.__assign({}, eventRemoveArg), { draggedEl: ev.subjectEl, view: initialView }));
|
||
|
initialContext_1.dispatch({
|
||
|
type: 'REMOVE_EVENTS',
|
||
|
eventStore: relevantEvents_1,
|
||
|
});
|
||
|
initialContext_1.emitter.trigger('eventRemove', eventRemoveArg);
|
||
|
var addedEventDef = mutatedRelevantEvents_1.defs[eventDef.defId];
|
||
|
var addedEventInstance = mutatedRelevantEvents_1.instances[eventInstance.instanceId];
|
||
|
var addedEventApi = new common.EventApi(receivingContext_1, addedEventDef, addedEventInstance);
|
||
|
receivingContext_1.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: mutatedRelevantEvents_1,
|
||
|
});
|
||
|
var eventAddArg = {
|
||
|
event: addedEventApi,
|
||
|
relatedEvents: common.buildEventApis(mutatedRelevantEvents_1, receivingContext_1, addedEventInstance),
|
||
|
revert: function () {
|
||
|
receivingContext_1.dispatch({
|
||
|
type: 'REMOVE_EVENTS',
|
||
|
eventStore: mutatedRelevantEvents_1,
|
||
|
});
|
||
|
},
|
||
|
};
|
||
|
receivingContext_1.emitter.trigger('eventAdd', eventAddArg);
|
||
|
if (ev.isTouch) {
|
||
|
receivingContext_1.dispatch({
|
||
|
type: 'SELECT_EVENT',
|
||
|
eventInstanceId: eventInstance.instanceId,
|
||
|
});
|
||
|
}
|
||
|
receivingContext_1.emitter.trigger('drop', tslib.__assign(tslib.__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext_1)), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.context.viewApi }));
|
||
|
receivingContext_1.emitter.trigger('eventReceive', tslib.__assign(tslib.__assign({}, eventAddArg), { draggedEl: ev.subjectEl, view: finalHit.context.viewApi }));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
initialContext_1.emitter.trigger('_noEventDrop');
|
||
|
}
|
||
|
}
|
||
|
_this.cleanup();
|
||
|
};
|
||
|
var component = _this.component;
|
||
|
var options = component.context.options;
|
||
|
var dragging = _this.dragging = new FeaturefulElementDragging(settings.el);
|
||
|
dragging.pointer.selector = EventDragging.SELECTOR;
|
||
|
dragging.touchScrollAllowed = false;
|
||
|
dragging.autoScroller.isEnabled = options.dragScroll;
|
||
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, common.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 (nextContext, state) {
|
||
|
var initialContext = this.component.context;
|
||
|
var prevContext = this.receivingContext;
|
||
|
// does the previous calendar need to be cleared?
|
||
|
if (prevContext && prevContext !== nextContext) {
|
||
|
// 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 (prevContext === initialContext) {
|
||
|
prevContext.dispatch({
|
||
|
type: 'SET_EVENT_DRAG',
|
||
|
state: {
|
||
|
affectedEvents: state.affectedEvents,
|
||
|
mutatedEvents: common.createEmptyEventStore(),
|
||
|
isEvent: true,
|
||
|
},
|
||
|
});
|
||
|
// completely clear the old calendar if it wasn't the initial
|
||
|
}
|
||
|
else {
|
||
|
prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
|
||
|
}
|
||
|
}
|
||
|
if (nextContext) {
|
||
|
nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state });
|
||
|
}
|
||
|
};
|
||
|
EventDragging.prototype.clearDrag = function () {
|
||
|
var initialCalendar = this.component.context;
|
||
|
var receivingContext = this.receivingContext;
|
||
|
if (receivingContext) {
|
||
|
receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
|
||
|
}
|
||
|
// the initial calendar might have an dummy drag state from displayDrag
|
||
|
if (initialCalendar !== receivingContext) {
|
||
|
initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
|
||
|
}
|
||
|
};
|
||
|
EventDragging.prototype.cleanup = function () {
|
||
|
this.subjectSeg = null;
|
||
|
this.isDragging = false;
|
||
|
this.eventRange = null;
|
||
|
this.relevantEvents = null;
|
||
|
this.receivingContext = null;
|
||
|
this.validMutation = null;
|
||
|
this.mutatedRelevantEvents = null;
|
||
|
};
|
||
|
// TODO: test this in IE11
|
||
|
// QUESTION: why do we need it on the resizable???
|
||
|
EventDragging.SELECTOR = '.fc-event-draggable, .fc-event-resizable';
|
||
|
return EventDragging;
|
||
|
}(common.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.context.options.allDayMaintainDuration;
|
||
|
if (dateSpan1.allDay) {
|
||
|
// means date1 is already start-of-day,
|
||
|
// but date0 needs to be converted
|
||
|
date0 = common.startOfDay(date0);
|
||
|
}
|
||
|
}
|
||
|
var delta = common.diffDates(date0, date1, hit0.context.dateEnv, hit0.componentId === hit1.componentId ?
|
||
|
hit0.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(component) {
|
||
|
var options = component.context.options;
|
||
|
var delay = options.eventLongPressDelay;
|
||
|
if (delay == null) {
|
||
|
delay = options.longPressDelay;
|
||
|
}
|
||
|
return delay;
|
||
|
}
|
||
|
|
||
|
var EventResizing = /** @class */ (function (_super) {
|
||
|
tslib.__extends(EventResizing, _super);
|
||
|
function EventResizing(settings) {
|
||
|
var _this = _super.call(this, settings) || this;
|
||
|
// internal state
|
||
|
_this.draggingSegEl = null;
|
||
|
_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 segEl = _this.querySegEl(ev);
|
||
|
var seg = common.getElSeg(segEl);
|
||
|
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 context = _this.component.context;
|
||
|
var eventRange = _this.eventRange;
|
||
|
_this.relevantEvents = common.getRelevantEvents(context.getCurrentData().eventStore, _this.eventRange.instance.instanceId);
|
||
|
var segEl = _this.querySegEl(ev);
|
||
|
_this.draggingSegEl = segEl;
|
||
|
_this.draggingSeg = common.getElSeg(segEl);
|
||
|
context.calendarApi.unselect();
|
||
|
context.emitter.trigger('eventResizeStart', {
|
||
|
el: segEl,
|
||
|
event: new common.EventApi(context, eventRange.def, eventRange.instance),
|
||
|
jsEvent: ev.origEvent,
|
||
|
view: context.viewApi,
|
||
|
});
|
||
|
};
|
||
|
_this.handleHitUpdate = function (hit, isFinal, ev) {
|
||
|
var context = _this.component.context;
|
||
|
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: common.createEmptyEventStore(),
|
||
|
isEvent: true,
|
||
|
};
|
||
|
if (hit) {
|
||
|
var disallowed = hit.componentId === initialHit.componentId
|
||
|
&& _this.isHitComboAllowed
|
||
|
&& !_this.isHitComboAllowed(initialHit, hit);
|
||
|
if (!disallowed) {
|
||
|
mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-event-resizer-start'), eventInstance.range);
|
||
|
}
|
||
|
}
|
||
|
if (mutation) {
|
||
|
mutatedRelevantEvents = common.applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context);
|
||
|
interaction.mutatedEvents = mutatedRelevantEvents;
|
||
|
if (!common.isInteractionValid(interaction, hit.dateProfile, context)) {
|
||
|
isInvalid = true;
|
||
|
mutation = null;
|
||
|
mutatedRelevantEvents = null;
|
||
|
interaction.mutatedEvents = null;
|
||
|
}
|
||
|
}
|
||
|
if (mutatedRelevantEvents) {
|
||
|
context.dispatch({
|
||
|
type: 'SET_EVENT_RESIZE',
|
||
|
state: interaction,
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
context.dispatch({ type: 'UNSET_EVENT_RESIZE' });
|
||
|
}
|
||
|
if (!isInvalid) {
|
||
|
common.enableCursor();
|
||
|
}
|
||
|
else {
|
||
|
common.disableCursor();
|
||
|
}
|
||
|
if (!isFinal) {
|
||
|
if (mutation && isHitsEqual(initialHit, hit)) {
|
||
|
mutation = null;
|
||
|
}
|
||
|
_this.validMutation = mutation;
|
||
|
_this.mutatedRelevantEvents = mutatedRelevantEvents;
|
||
|
}
|
||
|
};
|
||
|
_this.handleDragEnd = function (ev) {
|
||
|
var context = _this.component.context;
|
||
|
var eventDef = _this.eventRange.def;
|
||
|
var eventInstance = _this.eventRange.instance;
|
||
|
var eventApi = new common.EventApi(context, eventDef, eventInstance);
|
||
|
var relevantEvents = _this.relevantEvents;
|
||
|
var mutatedRelevantEvents = _this.mutatedRelevantEvents;
|
||
|
context.emitter.trigger('eventResizeStop', {
|
||
|
el: _this.draggingSegEl,
|
||
|
event: eventApi,
|
||
|
jsEvent: ev.origEvent,
|
||
|
view: context.viewApi,
|
||
|
});
|
||
|
if (_this.validMutation) {
|
||
|
var updatedEventApi = new common.EventApi(context, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null);
|
||
|
context.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: mutatedRelevantEvents,
|
||
|
});
|
||
|
var eventChangeArg = {
|
||
|
oldEvent: eventApi,
|
||
|
event: updatedEventApi,
|
||
|
relatedEvents: common.buildEventApis(mutatedRelevantEvents, context, eventInstance),
|
||
|
revert: function () {
|
||
|
context.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: relevantEvents, // the pre-change events
|
||
|
});
|
||
|
},
|
||
|
};
|
||
|
context.emitter.trigger('eventResize', tslib.__assign(tslib.__assign({}, eventChangeArg), { el: _this.draggingSegEl, startDelta: _this.validMutation.startDelta || common.createDuration(0), endDelta: _this.validMutation.endDelta || common.createDuration(0), jsEvent: ev.origEvent, view: context.viewApi }));
|
||
|
context.emitter.trigger('eventChange', eventChangeArg);
|
||
|
}
|
||
|
else {
|
||
|
context.emitter.trigger('_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(settings.el);
|
||
|
dragging.pointer.selector = '.fc-event-resizer';
|
||
|
dragging.touchScrollAllowed = false;
|
||
|
dragging.autoScroller.isEnabled = component.context.options.dragScroll;
|
||
|
var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, common.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;
|
||
|
}
|
||
|
EventResizing.prototype.destroy = function () {
|
||
|
this.dragging.destroy();
|
||
|
};
|
||
|
EventResizing.prototype.querySegEl = function (ev) {
|
||
|
return common.elementClosest(ev.subjectEl, '.fc-event');
|
||
|
};
|
||
|
return EventResizing;
|
||
|
}(common.Interaction));
|
||
|
function computeMutation(hit0, hit1, isFromStart, instanceRange) {
|
||
|
var dateEnv = hit0.context.dateEnv;
|
||
|
var date0 = hit0.dateSpan.range.start;
|
||
|
var date1 = hit1.dateSpan.range.start;
|
||
|
var delta = common.diffDates(date0, date1, dateEnv, hit0.largeUnit);
|
||
|
if (isFromStart) {
|
||
|
if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
|
||
|
return { startDelta: delta };
|
||
|
}
|
||
|
}
|
||
|
else if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
|
||
|
return { endDelta: delta };
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var UnselectAuto = /** @class */ (function () {
|
||
|
function UnselectAuto(context) {
|
||
|
var _this = this;
|
||
|
this.context = context;
|
||
|
this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system
|
||
|
this.matchesCancel = false;
|
||
|
this.matchesEvent = false;
|
||
|
this.onSelect = function (selectInfo) {
|
||
|
if (selectInfo.jsEvent) {
|
||
|
_this.isRecentPointerDateSelect = true;
|
||
|
}
|
||
|
};
|
||
|
this.onDocumentPointerDown = function (pev) {
|
||
|
var unselectCancel = _this.context.options.unselectCancel;
|
||
|
var downEl = common.getEventTargetViaRoot(pev.origEvent);
|
||
|
_this.matchesCancel = !!common.elementClosest(downEl, unselectCancel);
|
||
|
_this.matchesEvent = !!common.elementClosest(downEl, EventDragging.SELECTOR); // interaction started on an event?
|
||
|
};
|
||
|
this.onDocumentPointerUp = function (pev) {
|
||
|
var context = _this.context;
|
||
|
var documentPointer = _this.documentPointer;
|
||
|
var calendarState = context.getCurrentData();
|
||
|
// touch-scrolling should never unfocus any type of selection
|
||
|
if (!documentPointer.wasTouchScroll) {
|
||
|
if (calendarState.dateSelection && // an existing date selection?
|
||
|
!_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
|
||
|
) {
|
||
|
var unselectAuto = context.options.unselectAuto;
|
||
|
if (unselectAuto && (!unselectAuto || !_this.matchesCancel)) {
|
||
|
context.calendarApi.unselect(pev);
|
||
|
}
|
||
|
}
|
||
|
if (calendarState.eventSelection && // an existing event selected?
|
||
|
!_this.matchesEvent // interaction DIDN'T start on an event
|
||
|
) {
|
||
|
context.dispatch({ type: 'UNSELECT_EVENT' });
|
||
|
}
|
||
|
}
|
||
|
_this.isRecentPointerDateSelect = false;
|
||
|
};
|
||
|
var documentPointer = this.documentPointer = new PointerDragging(document);
|
||
|
documentPointer.shouldIgnoreMove = true;
|
||
|
documentPointer.shouldWatchScroll = false;
|
||
|
documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown);
|
||
|
documentPointer.emitter.on('pointerup', this.onDocumentPointerUp);
|
||
|
/*
|
||
|
TODO: better way to know about whether there was a selection with the pointer
|
||
|
*/
|
||
|
context.emitter.on('select', this.onSelect);
|
||
|
}
|
||
|
UnselectAuto.prototype.destroy = function () {
|
||
|
this.context.emitter.off('select', this.onSelect);
|
||
|
this.documentPointer.destroy();
|
||
|
};
|
||
|
return UnselectAuto;
|
||
|
}());
|
||
|
|
||
|
var OPTION_REFINERS = {
|
||
|
fixedMirrorParent: common.identity,
|
||
|
};
|
||
|
var LISTENER_REFINERS = {
|
||
|
dateClick: common.identity,
|
||
|
eventDragStart: common.identity,
|
||
|
eventDragStop: common.identity,
|
||
|
eventDrop: common.identity,
|
||
|
eventResizeStart: common.identity,
|
||
|
eventResizeStop: common.identity,
|
||
|
eventResize: common.identity,
|
||
|
drop: common.identity,
|
||
|
eventReceive: common.identity,
|
||
|
eventLeave: common.identity,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
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.receivingContext = 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 receivingContext = null;
|
||
|
var droppableEvent = null;
|
||
|
var isInvalid = false;
|
||
|
var interaction = {
|
||
|
affectedEvents: common.createEmptyEventStore(),
|
||
|
mutatedEvents: common.createEmptyEventStore(),
|
||
|
isEvent: _this.dragMeta.create,
|
||
|
};
|
||
|
if (hit) {
|
||
|
receivingContext = hit.context;
|
||
|
if (_this.canDropElOnCalendar(ev.subjectEl, receivingContext)) {
|
||
|
droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingContext);
|
||
|
interaction.mutatedEvents = common.eventTupleToStore(droppableEvent);
|
||
|
isInvalid = !common.isInteractionValid(interaction, hit.dateProfile, receivingContext);
|
||
|
if (isInvalid) {
|
||
|
interaction.mutatedEvents = common.createEmptyEventStore();
|
||
|
droppableEvent = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
_this.displayDrag(receivingContext, 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-event-mirror'));
|
||
|
if (!isInvalid) {
|
||
|
common.enableCursor();
|
||
|
}
|
||
|
else {
|
||
|
common.disableCursor();
|
||
|
}
|
||
|
if (!isFinal) {
|
||
|
dragging.setMirrorNeedsRevert(!droppableEvent);
|
||
|
_this.receivingContext = receivingContext;
|
||
|
_this.droppableEvent = droppableEvent;
|
||
|
}
|
||
|
};
|
||
|
this.handleDragEnd = function (pev) {
|
||
|
var _a = _this, receivingContext = _a.receivingContext, droppableEvent = _a.droppableEvent;
|
||
|
_this.clearDrag();
|
||
|
if (receivingContext && droppableEvent) {
|
||
|
var finalHit = _this.hitDragging.finalHit;
|
||
|
var finalView = finalHit.context.viewApi;
|
||
|
var dragMeta = _this.dragMeta;
|
||
|
receivingContext.emitter.trigger('drop', tslib.__assign(tslib.__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView }));
|
||
|
if (dragMeta.create) {
|
||
|
var addingEvents_1 = common.eventTupleToStore(droppableEvent);
|
||
|
receivingContext.dispatch({
|
||
|
type: 'MERGE_EVENTS',
|
||
|
eventStore: addingEvents_1,
|
||
|
});
|
||
|
if (pev.isTouch) {
|
||
|
receivingContext.dispatch({
|
||
|
type: 'SELECT_EVENT',
|
||
|
eventInstanceId: droppableEvent.instance.instanceId,
|
||
|
});
|
||
|
}
|
||
|
// signal that an external event landed
|
||
|
receivingContext.emitter.trigger('eventReceive', {
|
||
|
event: new common.EventApi(receivingContext, droppableEvent.def, droppableEvent.instance),
|
||
|
relatedEvents: [],
|
||
|
revert: function () {
|
||
|
receivingContext.dispatch({
|
||
|
type: 'REMOVE_EVENTS',
|
||
|
eventStore: addingEvents_1,
|
||
|
});
|
||
|
},
|
||
|
draggedEl: pev.subjectEl,
|
||
|
view: finalView,
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
_this.receivingContext = null;
|
||
|
_this.droppableEvent = null;
|
||
|
};
|
||
|
var hitDragging = this.hitDragging = new HitDragging(dragging, common.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 common.parseDragMeta(this.suppliedDragMeta);
|
||
|
}
|
||
|
if (typeof this.suppliedDragMeta === 'function') {
|
||
|
return common.parseDragMeta(this.suppliedDragMeta(subjectEl));
|
||
|
}
|
||
|
return getDragMetaFromEl(subjectEl);
|
||
|
};
|
||
|
ExternalElementDragging.prototype.displayDrag = function (nextContext, state) {
|
||
|
var prevContext = this.receivingContext;
|
||
|
if (prevContext && prevContext !== nextContext) {
|
||
|
prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
|
||
|
}
|
||
|
if (nextContext) {
|
||
|
nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state });
|
||
|
}
|
||
|
};
|
||
|
ExternalElementDragging.prototype.clearDrag = function () {
|
||
|
if (this.receivingContext) {
|
||
|
this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
|
||
|
}
|
||
|
};
|
||
|
ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingContext) {
|
||
|
var dropAccept = receivingContext.options.dropAccept;
|
||
|
if (typeof dropAccept === 'function') {
|
||
|
return dropAccept.call(receivingContext.calendarApi, el);
|
||
|
}
|
||
|
if (typeof dropAccept === 'string' && dropAccept) {
|
||
|
return Boolean(common.elementMatches(el, dropAccept));
|
||
|
}
|
||
|
return true;
|
||
|
};
|
||
|
return ExternalElementDragging;
|
||
|
}());
|
||
|
// Utils for computing event store from the DragMeta
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
function computeEventForDateSpan(dateSpan, dragMeta, context) {
|
||
|
var defProps = tslib.__assign({}, dragMeta.leftoverProps);
|
||
|
for (var _i = 0, _a = context.pluginHooks.externalDefTransforms; _i < _a.length; _i++) {
|
||
|
var transform = _a[_i];
|
||
|
tslib.__assign(defProps, transform(dateSpan, dragMeta));
|
||
|
}
|
||
|
var _b = common.refineEventDef(defProps, context), refined = _b.refined, extra = _b.extra;
|
||
|
var def = common.parseEventDef(refined, extra, dragMeta.sourceId, dateSpan.allDay, context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd
|
||
|
context);
|
||
|
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 = context.dateEnv.add(start, dragMeta.startTime);
|
||
|
}
|
||
|
var end = dragMeta.duration ?
|
||
|
context.dateEnv.add(start, dragMeta.duration) :
|
||
|
common.getDefaultEventEnd(dateSpan.allDay, start, context);
|
||
|
var instance = common.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 common.parseDragMeta(obj);
|
||
|
}
|
||
|
common.config.dataAttrPrefix = '';
|
||
|
function getEmbeddedElData(el, name) {
|
||
|
var prefix = common.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 : common.BASE_OPTION_DEFAULTS.eventDragMinDistance);
|
||
|
dragging.delay =
|
||
|
ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
|
||
|
(longPressDelay != null ? longPressDelay : common.BASE_OPTION_DEFAULTS.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-event-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); // eslint-disable-line no-new
|
||
|
}
|
||
|
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) {
|
||
|
tslib.__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
|
||
|
// TODO: somehow query FullCalendars WITHIN shadow-roots
|
||
|
? document.querySelector(this.mirrorSelector)
|
||
|
: null;
|
||
|
if (mirrorEl) {
|
||
|
this.currentMirrorEl = mirrorEl;
|
||
|
mirrorEl.style.visibility = 'hidden';
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
return InferredElementDragging;
|
||
|
}(common.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); // eslint-disable-line no-new
|
||
|
}
|
||
|
ThirdPartyDraggable.prototype.destroy = function () {
|
||
|
this.dragging.destroy();
|
||
|
};
|
||
|
return ThirdPartyDraggable;
|
||
|
}());
|
||
|
|
||
|
var main = common.createPlugin({
|
||
|
componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing],
|
||
|
calendarInteractions: [UnselectAuto],
|
||
|
elementDraggingImpl: FeaturefulElementDragging,
|
||
|
optionRefiners: OPTION_REFINERS,
|
||
|
listenerRefiners: LISTENER_REFINERS,
|
||
|
});
|
||
|
|
||
|
exports.Draggable = ExternalDraggable;
|
||
|
exports.FeaturefulElementDragging = FeaturefulElementDragging;
|
||
|
exports.PointerDragging = PointerDragging;
|
||
|
exports.ThirdPartyDraggable = ThirdPartyDraggable;
|
||
|
exports.default = main;
|