2019-08-28 10:59:33 +00:00
/ * !
2020-06-01 11:05:25 +00:00
FullCalendar Interaction Plugin v4 . 4.2
2019-08-28 10:59:33 +00:00
Docs & License : https : //fullcalendar.io/
( c ) 2019 Adam Shaw
* /
import { config , elementClosest , EmitterMixin , applyStyle , whenTransitionDone , removeElement , ScrollController , ElementScrollController , computeInnerRect , WindowScrollController , preventSelection , preventContextMenu , allowSelection , allowContextMenu , ElementDragging , computeRect , getClippingParents , pointInsideRect , isDateSpansEqual , constrainPoint , intersectRects , getRectCenter , diffPoints , mapHash , rangeContainsRange , interactionSettingsToStore , Interaction , enableCursor , disableCursor , compareNumbers , getElSeg , getRelevantEvents , EventApi , createEmptyEventStore , applyMutationToEventStore , interactionSettingsStore , startOfDay , diffDates , createDuration , eventTupleToStore , isInteractionValid , parseDragMeta , elementMatches , parseEventDef , createEventInstance , globalDefaults , createPlugin } from '@fullcalendar/core' ;
/ * ! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2020-06-01 11:05:25 +00:00
Copyright ( c ) Microsoft Corporation .
2019-08-28 10:59:33 +00:00
2020-06-01 11:05:25 +00:00
Permission to use , copy , modify , and / or distribute this software for any
purpose with or without fee is hereby granted .
2019-08-28 10:59:33 +00:00
2020-06-01 11:05:25 +00:00
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 .
2019-08-28 10:59:33 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* 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 ) ;
} ;
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 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 || 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 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 -- ;
} , 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' ;
applyStyle ( mirrorEl , {
left : finalSourceElRect . left ,
top : finalSourceElRect . top
} ) ;
whenTransitionDone ( mirrorEl , function ( ) {
mirrorEl . style . transition = '' ;
callback ( ) ;
} ) ;
} ;
ElementMirror . prototype . cleanup = function ( ) {
if ( this . mirrorEl ) {
removeElement ( this . mirrorEl ) ;
this . mirrorEl = null ;
}
this . sourceEl = null ;
} ;
ElementMirror . prototype . updateElPosition = function ( ) {
if ( this . sourceEl && this . isVisible ) {
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' ) ;
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 ;
} ( ScrollController ) ) ;
var ElementScrollGeomCache = /** @class */ ( function ( _super ) {
_ _extends ( ElementScrollGeomCache , _super ) ;
function ElementScrollGeomCache ( el , doesListening ) {
return _super . call ( this , new ElementScrollController ( el ) , doesListening ) || this ;
}
ElementScrollGeomCache . prototype . getEventTarget = function ( ) {
return this . scrollController . el ;
} ;
ElementScrollGeomCache . prototype . computeClientRect = function ( ) {
return computeInnerRect ( this . scrollController . el ) ;
} ;
return ElementScrollGeomCache ;
} ( ScrollGeomCache ) ) ;
var WindowScrollGeomCache = /** @class */ ( function ( _super ) {
_ _extends ( WindowScrollGeomCache , _super ) ;
function WindowScrollGeomCache ( doesListening ) {
return _super . call ( this , new 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 ;
preventSelection ( document . body ) ;
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 ;
allowSelection ( document . body ) ;
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 ;
} ( 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 = computeRect ( el ) ;
// will work fine for divs that have overflow:hidden
this . scrollCaches = 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 ( ) ) &&
! 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 ) ;
_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 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 = computeRect ( subjectEl ) ;
adjustedPoint = constrainPoint ( adjustedPoint , subjectRect ) ;
}
var initialHit = this . initialHit = this . queryHitForOffset ( adjustedPoint . left , adjustedPoint . top ) ;
if ( initialHit ) {
if ( this . useSubjectCenter && subjectRect ) {
var slicedSubjectRect = intersectRects ( subjectRect , initialHit . rect ) ;
if ( slicedSubjectRect ) {
adjustedPoint = getRectCenter ( slicedSubjectRect ) ;
}
}
this . coordAdjust = 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 = 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
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 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 ;
2020-04-01 22:52:43 +00:00
var _a = component . context , calendar = _a . calendar , view = _a . view ;
2019-08-28 10:59:33 +00:00
var pointer = _this . dragging . pointer ;
if ( ! pointer . wasTouchScroll ) {
2020-04-01 22:52:43 +00:00
var _b = _this . hitDragging , initialHit = _b . initialHit , finalHit = _b . finalHit ;
2019-08-28 10:59:33 +00:00
if ( initialHit && finalHit && isHitsEqual ( initialHit , finalHit ) ) {
2020-04-01 22:52:43 +00:00
calendar . triggerDateClick ( initialHit . dateSpan , initialHit . dayEl , view , ev . origEvent ) ;
2019-08-28 10:59:33 +00:00
}
}
} ;
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 , 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 ;
} ( 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 ;
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
var canSelect = options . selectable &&
2019-08-28 10:59:33 +00:00
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 ) {
2020-04-01 22:52:43 +00:00
_this . component . context . calendar . unselect ( ev ) ; // unselect previous selections
2019-08-28 10:59:33 +00:00
} ;
_this . handleHitUpdate = function ( hit , isFinal ) {
2020-04-01 22:52:43 +00:00
var calendar = _this . component . context . calendar ;
2019-08-28 10:59:33 +00:00
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 ) {
enableCursor ( ) ;
}
else {
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
2020-04-01 22:52:43 +00:00
_this . component . context . calendar . triggerDateSelect ( _this . dragSelection , pev ) ;
2019-08-28 10:59:33 +00:00
_this . dragSelection = null ;
}
} ;
var component = settings . component ;
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
2019-08-28 10:59:33 +00:00
var dragging = _this . dragging = new FeaturefulElementDragging ( component . el ) ;
dragging . touchScrollAllowed = false ;
2020-04-01 22:52:43 +00:00
dragging . minDistance = options . selectMinDistance || 0 ;
dragging . autoScroller . isEnabled = options . dragScroll ;
2019-08-28 10:59:33 +00:00
var hitDragging = _this . hitDragging = new HitDragging ( _this . dragging , 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 ;
} ( Interaction ) ) ;
function getComponentTouchDelay ( component ) {
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
var delay = options . selectLongPressDelay ;
2019-08-28 10:59:33 +00:00
if ( delay == null ) {
2020-04-01 22:52:43 +00:00
delay = options . longPressDelay ;
2019-08-28 10:59:33 +00:00
}
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 ( 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 ;
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
var initialCalendar = component . context . calendar ;
2019-08-28 10:59:33 +00:00
var subjectSeg = _this . subjectSeg = getElSeg ( ev . subjectEl ) ;
var eventRange = _this . eventRange = subjectSeg . eventRange ;
var eventInstanceId = eventRange . instance . instanceId ;
_this . relevantEvents = getRelevantEvents ( initialCalendar . state . eventStore , eventInstanceId ) ;
2020-04-01 22:52:43 +00:00
dragging . minDistance = ev . isTouch ? 0 : options . eventDragMinDistance ;
2019-08-28 10:59:33 +00:00
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 ;
2020-04-01 22:52:43 +00:00
mirror . revertDuration = options . dragRevertDuration ;
2019-08-28 10:59:33 +00:00
var isValid = component . isValidSegDownEl ( origTarget ) &&
! 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 ) {
2020-04-01 22:52:43 +00:00
var context = _this . component . context ;
var initialCalendar = context . calendar ;
2019-08-28 10:59:33 +00:00
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 EventApi ( initialCalendar , eventRange . def , eventRange . instance ) ,
jsEvent : ev . origEvent ,
2020-04-01 22:52:43 +00:00
view : context . view
2019-08-28 10:59:33 +00:00
}
] ) ;
}
} ;
_this . handleHitUpdate = function ( hit , isFinal ) {
if ( ! _this . isDragging ) {
return ;
}
var relevantEvents = _this . relevantEvents ;
var initialHit = _this . hitDragging . initialHit ;
2020-04-01 22:52:43 +00:00
var initialCalendar = _this . component . context . calendar ;
2019-08-28 10:59:33 +00:00
// states based on new hit
var receivingCalendar = null ;
var mutation = null ;
var mutatedRelevantEvents = null ;
var isInvalid = false ;
var interaction = {
affectedEvents : relevantEvents ,
mutatedEvents : createEmptyEventStore ( ) ,
isEvent : true ,
origSeg : _this . subjectSeg
} ;
if ( hit ) {
var receivingComponent = hit . component ;
2020-04-01 22:52:43 +00:00
receivingCalendar = receivingComponent . context . calendar ;
var receivingOptions = receivingComponent . context . options ;
2019-08-28 10:59:33 +00:00
if ( initialCalendar === receivingCalendar ||
2020-04-01 22:52:43 +00:00
receivingOptions . editable && receivingOptions . droppable ) {
2019-08-28 10:59:33 +00:00
mutation = computeEventMutation ( initialHit , hit , receivingCalendar . pluginSystem . hooks . eventDragMutationMassagers ) ;
if ( mutation ) {
mutatedRelevantEvents = applyMutationToEventStore ( relevantEvents , receivingCalendar . eventUiBases , mutation , receivingCalendar ) ;
interaction . mutatedEvents = mutatedRelevantEvents ;
if ( ! receivingComponent . isInteractionValid ( interaction ) ) {
isInvalid = true ;
mutation = null ;
mutatedRelevantEvents = null ;
interaction . mutatedEvents = createEmptyEventStore ( ) ;
}
}
}
else {
receivingCalendar = null ;
}
}
_this . displayDrag ( receivingCalendar , interaction ) ;
if ( ! isInvalid ) {
enableCursor ( ) ;
}
else {
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 ) {
2020-04-01 22:52:43 +00:00
var context = _this . component . context ;
var initialCalendar _1 = context . calendar ;
var initialView = context . view ;
2019-08-28 10:59:33 +00:00
var _a = _this , receivingCalendar = _a . receivingCalendar , validMutation = _a . validMutation ;
var eventDef = _this . eventRange . def ;
var eventInstance = _this . eventRange . instance ;
var eventApi = new 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 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 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 ;
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
2019-08-28 10:59:33 +00:00
var dragging = _this . dragging = new FeaturefulElementDragging ( component . el ) ;
dragging . pointer . selector = EventDragging . SELECTOR ;
dragging . touchScrollAllowed = false ;
2020-04-01 22:52:43 +00:00
dragging . autoScroller . isEnabled = options . dragScroll ;
2019-08-28 10:59:33 +00:00
var hitDragging = _this . hitDragging = new HitDragging ( _this . dragging , 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 ) {
2020-04-01 22:52:43 +00:00
var initialCalendar = this . component . context . calendar ;
2019-08-28 10:59:33 +00:00
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 : 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 ( ) {
2020-04-01 22:52:43 +00:00
var initialCalendar = this . component . context . calendar ;
2019-08-28 10:59:33 +00:00
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 ;
} ( 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 ;
2020-04-01 22:52:43 +00:00
standardProps . hasEnd = hit1 . component . context . options . allDayMaintainDuration ;
2019-08-28 10:59:33 +00:00
if ( dateSpan1 . allDay ) {
// means date1 is already start-of-day,
// but date0 needs to be converted
date0 = startOfDay ( date0 ) ;
}
}
2020-04-01 22:52:43 +00:00
var delta = diffDates ( date0 , date1 , hit0 . component . context . dateEnv , hit0 . component === hit1 . component ?
2019-08-28 10:59:33 +00:00
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 ) {
2020-04-01 22:52:43 +00:00
var options = component . context . options ;
var delay = options . eventLongPressDelay ;
2019-08-28 10:59:33 +00:00
if ( delay == null ) {
2020-04-01 22:52:43 +00:00
delay = options . longPressDelay ;
2019-08-28 10:59:33 +00:00
}
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 ;
2020-04-01 22:52:43 +00:00
_this . dragging . minDistance = component . context . options . eventDragMinDistance ;
2019-08-28 10:59:33 +00:00
// 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 ) {
2020-04-01 22:52:43 +00:00
var _a = _this . component . context , calendar = _a . calendar , view = _a . view ;
2019-08-28 10:59:33 +00:00
var eventRange = _this . eventRange ;
_this . relevantEvents = getRelevantEvents ( calendar . state . eventStore , _this . eventRange . instance . instanceId ) ;
_this . draggingSeg = _this . querySeg ( ev ) ;
calendar . unselect ( ) ;
calendar . publiclyTrigger ( 'eventResizeStart' , [
{
el : _this . draggingSeg . el ,
event : new EventApi ( calendar , eventRange . def , eventRange . instance ) ,
jsEvent : ev . origEvent ,
2020-04-01 22:52:43 +00:00
view : view
2019-08-28 10:59:33 +00:00
}
] ) ;
} ;
_this . handleHitUpdate = function ( hit , isFinal , ev ) {
2020-04-01 22:52:43 +00:00
var calendar = _this . component . context . calendar ;
2019-08-28 10:59:33 +00:00
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 : 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 = 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 ) {
enableCursor ( ) ;
}
else {
disableCursor ( ) ;
}
if ( ! isFinal ) {
if ( mutation && isHitsEqual ( initialHit , hit ) ) {
mutation = null ;
}
_this . validMutation = mutation ;
_this . mutatedRelevantEvents = mutatedRelevantEvents ;
}
} ;
_this . handleDragEnd = function ( ev ) {
2020-04-01 22:52:43 +00:00
var _a = _this . component . context , calendar = _a . calendar , view = _a . view ;
2019-08-28 10:59:33 +00:00
var eventDef = _this . eventRange . def ;
var eventInstance = _this . eventRange . instance ;
var eventApi = new 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 || createDuration ( 0 ) ,
endDelta : _this . validMutation . endDelta || createDuration ( 0 ) ,
prevEvent : eventApi ,
event : new 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 ;
2020-04-01 22:52:43 +00:00
dragging . autoScroller . isEnabled = component . context . options . dragScroll ;
2019-08-28 10:59:33 +00:00
var hitDragging = _this . hitDragging = new HitDragging ( _this . dragging , 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 getElSeg ( elementClosest ( ev . subjectEl , this . component . fgSegSelector ) ) ;
} ;
return EventDragging ;
} ( Interaction ) ) ;
function computeMutation ( hit0 , hit1 , isFromStart , instanceRange , transforms ) {
2020-04-01 22:52:43 +00:00
var dateEnv = hit0 . component . context . dateEnv ;
2019-08-28 10:59:33 +00:00
var date0 = hit0 . dateSpan . range . start ;
var date1 = hit1 . dateSpan . range . start ;
var delta = 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 || ! elementClosest ( documentPointer . downEl , unselectCancel ) ) ) {
calendar . unselect ( pev ) ;
}
}
if ( state . eventSelection && // an existing event selected?
! 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 : createEmptyEventStore ( ) ,
mutatedEvents : createEmptyEventStore ( ) ,
isEvent : _this . dragMeta . create ,
origSeg : null
} ;
if ( hit ) {
2020-04-01 22:52:43 +00:00
receivingCalendar = hit . component . context . calendar ;
2019-08-28 10:59:33 +00:00
if ( _this . canDropElOnCalendar ( ev . subjectEl , receivingCalendar ) ) {
droppableEvent = computeEventForDateSpan ( hit . dateSpan , _this . dragMeta , receivingCalendar ) ;
interaction . mutatedEvents = eventTupleToStore ( droppableEvent ) ;
isInvalid = ! isInteractionValid ( interaction , receivingCalendar ) ;
if ( isInvalid ) {
interaction . mutatedEvents = 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 ) {
enableCursor ( ) ;
}
else {
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 ;
2020-04-01 22:52:43 +00:00
var finalView = finalHit . component . context . view ;
2019-08-28 10:59:33 +00:00
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 : 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 EventApi ( receivingCalendar , droppableEvent . def , droppableEvent . instance ) ,
view : finalView
}
] ) ;
}
}
_this . receivingCalendar = null ;
_this . droppableEvent = null ;
} ;
var hitDragging = this . hitDragging = new HitDragging ( dragging , 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 parseDragMeta ( this . suppliedDragMeta ) ;
}
else if ( typeof this . suppliedDragMeta === 'function' ) {
return 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 ( 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 = 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 = 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 parseDragMeta ( obj ) ;
}
config . dataAttrPrefix = '' ;
function getEmbeddedElData ( el , name ) {
var prefix = 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 : globalDefaults . eventDragMinDistance ) ;
dragging . delay =
ev . isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
( longPressDelay != null ? longPressDelay : 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 ;
} ( 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 = createPlugin ( {
componentInteractions : [ DateClicking , DateSelecting , EventDragging , EventDragging$1 ] ,
calendarInteractions : [ UnselectAuto ] ,
elementDraggingImpl : FeaturefulElementDragging
} ) ;
export default main ;
export { ExternalDraggable as Draggable , FeaturefulElementDragging , PointerDragging , ThirdPartyDraggable } ;