diff options
author | polo <ordipolo@gmx.fr> | 2025-06-23 03:33:38 +0200 |
---|---|---|
committer | polo <ordipolo@gmx.fr> | 2025-06-23 03:33:38 +0200 |
commit | cebc19ef236aac2968d2ffccfcff9b975b63fa8d (patch) | |
tree | 5b8e08045a45063475f533bfae4b4524720fe7bd /public/js/fullcalendar/packages/interaction | |
parent | 8cf5ac1abf9e2a6134cb82d4582aecaa99b1331a (diff) | |
download | cms-cebc19ef236aac2968d2ffccfcff9b975b63fa8d.zip |
fullcalendar
Diffstat (limited to 'public/js/fullcalendar/packages/interaction')
-rw-r--r-- | public/js/fullcalendar/packages/interaction/index.global.js | 2141 | ||||
-rw-r--r-- | public/js/fullcalendar/packages/interaction/index.global.min.js | 6 |
2 files changed, 2147 insertions, 0 deletions
diff --git a/public/js/fullcalendar/packages/interaction/index.global.js b/public/js/fullcalendar/packages/interaction/index.global.js new file mode 100644 index 0000000..d05a22e --- /dev/null +++ b/public/js/fullcalendar/packages/interaction/index.global.js | |||
@@ -0,0 +1,2141 @@ | |||
1 | /*! | ||
2 | FullCalendar Interaction Plugin v6.1.17 | ||
3 | Docs & License: https://fullcalendar.io/docs/editable | ||
4 | (c) 2024 Adam Shaw | ||
5 | */ | ||
6 | FullCalendar.Interaction = (function (exports, core, internal) { | ||
7 | 'use strict'; | ||
8 | |||
9 | internal.config.touchMouseIgnoreWait = 500; | ||
10 | let ignoreMouseDepth = 0; | ||
11 | let listenerCnt = 0; | ||
12 | let isWindowTouchMoveCancelled = false; | ||
13 | /* | ||
14 | Uses a "pointer" abstraction, which monitors UI events for both mouse and touch. | ||
15 | Tracks when the pointer "drags" on a certain element, meaning down+move+up. | ||
16 | |||
17 | Also, tracks if there was touch-scrolling. | ||
18 | Also, can prevent touch-scrolling from happening. | ||
19 | Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement. | ||
20 | |||
21 | emits: | ||
22 | - pointerdown | ||
23 | - pointermove | ||
24 | - pointerup | ||
25 | */ | ||
26 | class PointerDragging { | ||
27 | constructor(containerEl) { | ||
28 | this.subjectEl = null; | ||
29 | // options that can be directly assigned by caller | ||
30 | this.selector = ''; // will cause subjectEl in all emitted events to be this element | ||
31 | this.handleSelector = ''; | ||
32 | this.shouldIgnoreMove = false; | ||
33 | this.shouldWatchScroll = true; // for simulating pointermove on scroll | ||
34 | // internal states | ||
35 | this.isDragging = false; | ||
36 | this.isTouchDragging = false; | ||
37 | this.wasTouchScroll = false; | ||
38 | // Mouse | ||
39 | // ---------------------------------------------------------------------------------------------------- | ||
40 | this.handleMouseDown = (ev) => { | ||
41 | if (!this.shouldIgnoreMouse() && | ||
42 | isPrimaryMouseButton(ev) && | ||
43 | this.tryStart(ev)) { | ||
44 | let pev = this.createEventFromMouse(ev, true); | ||
45 | this.emitter.trigger('pointerdown', pev); | ||
46 | this.initScrollWatch(pev); | ||
47 | if (!this.shouldIgnoreMove) { | ||
48 | document.addEventListener('mousemove', this.handleMouseMove); | ||
49 | } | ||
50 | document.addEventListener('mouseup', this.handleMouseUp); | ||
51 | } | ||
52 | }; | ||
53 | this.handleMouseMove = (ev) => { | ||
54 | let pev = this.createEventFromMouse(ev); | ||
55 | this.recordCoords(pev); | ||
56 | this.emitter.trigger('pointermove', pev); | ||
57 | }; | ||
58 | this.handleMouseUp = (ev) => { | ||
59 | document.removeEventListener('mousemove', this.handleMouseMove); | ||
60 | document.removeEventListener('mouseup', this.handleMouseUp); | ||
61 | this.emitter.trigger('pointerup', this.createEventFromMouse(ev)); | ||
62 | this.cleanup(); // call last so that pointerup has access to props | ||
63 | }; | ||
64 | // Touch | ||
65 | // ---------------------------------------------------------------------------------------------------- | ||
66 | this.handleTouchStart = (ev) => { | ||
67 | if (this.tryStart(ev)) { | ||
68 | this.isTouchDragging = true; | ||
69 | let pev = this.createEventFromTouch(ev, true); | ||
70 | this.emitter.trigger('pointerdown', pev); | ||
71 | this.initScrollWatch(pev); | ||
72 | // unlike mouse, need to attach to target, not document | ||
73 | // https://stackoverflow.com/a/45760014 | ||
74 | let targetEl = ev.target; | ||
75 | if (!this.shouldIgnoreMove) { | ||
76 | targetEl.addEventListener('touchmove', this.handleTouchMove); | ||
77 | } | ||
78 | targetEl.addEventListener('touchend', this.handleTouchEnd); | ||
79 | targetEl.addEventListener('touchcancel', this.handleTouchEnd); // treat it as a touch end | ||
80 | // attach a handler to get called when ANY scroll action happens on the page. | ||
81 | // this was impossible to do with normal on/off because 'scroll' doesn't bubble. | ||
82 | // http://stackoverflow.com/a/32954565/96342 | ||
83 | window.addEventListener('scroll', this.handleTouchScroll, true); | ||
84 | } | ||
85 | }; | ||
86 | this.handleTouchMove = (ev) => { | ||
87 | let pev = this.createEventFromTouch(ev); | ||
88 | this.recordCoords(pev); | ||
89 | this.emitter.trigger('pointermove', pev); | ||
90 | }; | ||
91 | this.handleTouchEnd = (ev) => { | ||
92 | if (this.isDragging) { // done to guard against touchend followed by touchcancel | ||
93 | let targetEl = ev.target; | ||
94 | targetEl.removeEventListener('touchmove', this.handleTouchMove); | ||
95 | targetEl.removeEventListener('touchend', this.handleTouchEnd); | ||
96 | targetEl.removeEventListener('touchcancel', this.handleTouchEnd); | ||
97 | window.removeEventListener('scroll', this.handleTouchScroll, true); // useCaptured=true | ||
98 | this.emitter.trigger('pointerup', this.createEventFromTouch(ev)); | ||
99 | this.cleanup(); // call last so that pointerup has access to props | ||
100 | this.isTouchDragging = false; | ||
101 | startIgnoringMouse(); | ||
102 | } | ||
103 | }; | ||
104 | this.handleTouchScroll = () => { | ||
105 | this.wasTouchScroll = true; | ||
106 | }; | ||
107 | this.handleScroll = (ev) => { | ||
108 | if (!this.shouldIgnoreMove) { | ||
109 | let pageX = (window.scrollX - this.prevScrollX) + this.prevPageX; | ||
110 | let pageY = (window.scrollY - this.prevScrollY) + this.prevPageY; | ||
111 | this.emitter.trigger('pointermove', { | ||
112 | origEvent: ev, | ||
113 | isTouch: this.isTouchDragging, | ||
114 | subjectEl: this.subjectEl, | ||
115 | pageX, | ||
116 | pageY, | ||
117 | deltaX: pageX - this.origPageX, | ||
118 | deltaY: pageY - this.origPageY, | ||
119 | }); | ||
120 | } | ||
121 | }; | ||
122 | this.containerEl = containerEl; | ||
123 | this.emitter = new internal.Emitter(); | ||
124 | containerEl.addEventListener('mousedown', this.handleMouseDown); | ||
125 | containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true }); | ||
126 | listenerCreated(); | ||
127 | } | ||
128 | destroy() { | ||
129 | this.containerEl.removeEventListener('mousedown', this.handleMouseDown); | ||
130 | this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true }); | ||
131 | listenerDestroyed(); | ||
132 | } | ||
133 | tryStart(ev) { | ||
134 | let subjectEl = this.querySubjectEl(ev); | ||
135 | let downEl = ev.target; | ||
136 | if (subjectEl && | ||
137 | (!this.handleSelector || internal.elementClosest(downEl, this.handleSelector))) { | ||
138 | this.subjectEl = subjectEl; | ||
139 | this.isDragging = true; // do this first so cancelTouchScroll will work | ||
140 | this.wasTouchScroll = false; | ||
141 | return true; | ||
142 | } | ||
143 | return false; | ||
144 | } | ||
145 | cleanup() { | ||
146 | isWindowTouchMoveCancelled = false; | ||
147 | this.isDragging = false; | ||
148 | this.subjectEl = null; | ||
149 | // keep wasTouchScroll around for later access | ||
150 | this.destroyScrollWatch(); | ||
151 | } | ||
152 | querySubjectEl(ev) { | ||
153 | if (this.selector) { | ||
154 | return internal.elementClosest(ev.target, this.selector); | ||
155 | } | ||
156 | return this.containerEl; | ||
157 | } | ||
158 | shouldIgnoreMouse() { | ||
159 | return ignoreMouseDepth || this.isTouchDragging; | ||
160 | } | ||
161 | // can be called by user of this class, to cancel touch-based scrolling for the current drag | ||
162 | cancelTouchScroll() { | ||
163 | if (this.isDragging) { | ||
164 | isWindowTouchMoveCancelled = true; | ||
165 | } | ||
166 | } | ||
167 | // Scrolling that simulates pointermoves | ||
168 | // ---------------------------------------------------------------------------------------------------- | ||
169 | initScrollWatch(ev) { | ||
170 | if (this.shouldWatchScroll) { | ||
171 | this.recordCoords(ev); | ||
172 | window.addEventListener('scroll', this.handleScroll, true); // useCapture=true | ||
173 | } | ||
174 | } | ||
175 | recordCoords(ev) { | ||
176 | if (this.shouldWatchScroll) { | ||
177 | this.prevPageX = ev.pageX; | ||
178 | this.prevPageY = ev.pageY; | ||
179 | this.prevScrollX = window.scrollX; | ||
180 | this.prevScrollY = window.scrollY; | ||
181 | } | ||
182 | } | ||
183 | destroyScrollWatch() { | ||
184 | if (this.shouldWatchScroll) { | ||
185 | window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true | ||
186 | } | ||
187 | } | ||
188 | // Event Normalization | ||
189 | // ---------------------------------------------------------------------------------------------------- | ||
190 | createEventFromMouse(ev, isFirst) { | ||
191 | let deltaX = 0; | ||
192 | let deltaY = 0; | ||
193 | // TODO: repeat code | ||
194 | if (isFirst) { | ||
195 | this.origPageX = ev.pageX; | ||
196 | this.origPageY = ev.pageY; | ||
197 | } | ||
198 | else { | ||
199 | deltaX = ev.pageX - this.origPageX; | ||
200 | deltaY = ev.pageY - this.origPageY; | ||
201 | } | ||
202 | return { | ||
203 | origEvent: ev, | ||
204 | isTouch: false, | ||
205 | subjectEl: this.subjectEl, | ||
206 | pageX: ev.pageX, | ||
207 | pageY: ev.pageY, | ||
208 | deltaX, | ||
209 | deltaY, | ||
210 | }; | ||
211 | } | ||
212 | createEventFromTouch(ev, isFirst) { | ||
213 | let touches = ev.touches; | ||
214 | let pageX; | ||
215 | let pageY; | ||
216 | let deltaX = 0; | ||
217 | let deltaY = 0; | ||
218 | // if touch coords available, prefer, | ||
219 | // because FF would give bad ev.pageX ev.pageY | ||
220 | if (touches && touches.length) { | ||
221 | pageX = touches[0].pageX; | ||
222 | pageY = touches[0].pageY; | ||
223 | } | ||
224 | else { | ||
225 | pageX = ev.pageX; | ||
226 | pageY = ev.pageY; | ||
227 | } | ||
228 | // TODO: repeat code | ||
229 | if (isFirst) { | ||
230 | this.origPageX = pageX; | ||
231 | this.origPageY = pageY; | ||
232 | } | ||
233 | else { | ||
234 | deltaX = pageX - this.origPageX; | ||
235 | deltaY = pageY - this.origPageY; | ||
236 | } | ||
237 | return { | ||
238 | origEvent: ev, | ||
239 | isTouch: true, | ||
240 | subjectEl: this.subjectEl, | ||
241 | pageX, | ||
242 | pageY, | ||
243 | deltaX, | ||
244 | deltaY, | ||
245 | }; | ||
246 | } | ||
247 | } | ||
248 | // Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac) | ||
249 | function isPrimaryMouseButton(ev) { | ||
250 | return ev.button === 0 && !ev.ctrlKey; | ||
251 | } | ||
252 | // Ignoring fake mouse events generated by touch | ||
253 | // ---------------------------------------------------------------------------------------------------- | ||
254 | function startIgnoringMouse() { | ||
255 | ignoreMouseDepth += 1; | ||
256 | setTimeout(() => { | ||
257 | ignoreMouseDepth -= 1; | ||
258 | }, internal.config.touchMouseIgnoreWait); | ||
259 | } | ||
260 | // We want to attach touchmove as early as possible for Safari | ||
261 | // ---------------------------------------------------------------------------------------------------- | ||
262 | function listenerCreated() { | ||
263 | listenerCnt += 1; | ||
264 | if (listenerCnt === 1) { | ||
265 | window.addEventListener('touchmove', onWindowTouchMove, { passive: false }); | ||
266 | } | ||
267 | } | ||
268 | function listenerDestroyed() { | ||
269 | listenerCnt -= 1; | ||
270 | if (!listenerCnt) { | ||
271 | window.removeEventListener('touchmove', onWindowTouchMove, { passive: false }); | ||
272 | } | ||
273 | } | ||
274 | function onWindowTouchMove(ev) { | ||
275 | if (isWindowTouchMoveCancelled) { | ||
276 | ev.preventDefault(); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | An effect in which an element follows the movement of a pointer across the screen. | ||
282 | The moving element is a clone of some other element. | ||
283 | Must call start + handleMove + stop. | ||
284 | */ | ||
285 | class ElementMirror { | ||
286 | constructor() { | ||
287 | this.isVisible = false; // must be explicitly enabled | ||
288 | this.sourceEl = null; | ||
289 | this.mirrorEl = null; | ||
290 | this.sourceElRect = null; // screen coords relative to viewport | ||
291 | // options that can be set directly by caller | ||
292 | this.parentNode = document.body; // HIGHLY SUGGESTED to set this to sidestep ShadowDOM issues | ||
293 | this.zIndex = 9999; | ||
294 | this.revertDuration = 0; | ||
295 | } | ||
296 | start(sourceEl, pageX, pageY) { | ||
297 | this.sourceEl = sourceEl; | ||
298 | this.sourceElRect = this.sourceEl.getBoundingClientRect(); | ||
299 | this.origScreenX = pageX - window.scrollX; | ||
300 | this.origScreenY = pageY - window.scrollY; | ||
301 | this.deltaX = 0; | ||
302 | this.deltaY = 0; | ||
303 | this.updateElPosition(); | ||
304 | } | ||
305 | handleMove(pageX, pageY) { | ||
306 | this.deltaX = (pageX - window.scrollX) - this.origScreenX; | ||
307 | this.deltaY = (pageY - window.scrollY) - this.origScreenY; | ||
308 | this.updateElPosition(); | ||
309 | } | ||
310 | // can be called before start | ||
311 | setIsVisible(bool) { | ||
312 | if (bool) { | ||
313 | if (!this.isVisible) { | ||
314 | if (this.mirrorEl) { | ||
315 | this.mirrorEl.style.display = ''; | ||
316 | } | ||
317 | this.isVisible = bool; // needs to happen before updateElPosition | ||
318 | this.updateElPosition(); // because was not updating the position while invisible | ||
319 | } | ||
320 | } | ||
321 | else if (this.isVisible) { | ||
322 | if (this.mirrorEl) { | ||
323 | this.mirrorEl.style.display = 'none'; | ||
324 | } | ||
325 | this.isVisible = bool; | ||
326 | } | ||
327 | } | ||
328 | // always async | ||
329 | stop(needsRevertAnimation, callback) { | ||
330 | let done = () => { | ||
331 | this.cleanup(); | ||
332 | callback(); | ||
333 | }; | ||
334 | if (needsRevertAnimation && | ||
335 | this.mirrorEl && | ||
336 | this.isVisible && | ||
337 | this.revertDuration && // if 0, transition won't work | ||
338 | (this.deltaX || this.deltaY) // if same coords, transition won't work | ||
339 | ) { | ||
340 | this.doRevertAnimation(done, this.revertDuration); | ||
341 | } | ||
342 | else { | ||
343 | setTimeout(done, 0); | ||
344 | } | ||
345 | } | ||
346 | doRevertAnimation(callback, revertDuration) { | ||
347 | let mirrorEl = this.mirrorEl; | ||
348 | let finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened | ||
349 | mirrorEl.style.transition = | ||
350 | 'top ' + revertDuration + 'ms,' + | ||
351 | 'left ' + revertDuration + 'ms'; | ||
352 | internal.applyStyle(mirrorEl, { | ||
353 | left: finalSourceElRect.left, | ||
354 | top: finalSourceElRect.top, | ||
355 | }); | ||
356 | internal.whenTransitionDone(mirrorEl, () => { | ||
357 | mirrorEl.style.transition = ''; | ||
358 | callback(); | ||
359 | }); | ||
360 | } | ||
361 | cleanup() { | ||
362 | if (this.mirrorEl) { | ||
363 | internal.removeElement(this.mirrorEl); | ||
364 | this.mirrorEl = null; | ||
365 | } | ||
366 | this.sourceEl = null; | ||
367 | } | ||
368 | updateElPosition() { | ||
369 | if (this.sourceEl && this.isVisible) { | ||
370 | internal.applyStyle(this.getMirrorEl(), { | ||
371 | left: this.sourceElRect.left + this.deltaX, | ||
372 | top: this.sourceElRect.top + this.deltaY, | ||
373 | }); | ||
374 | } | ||
375 | } | ||
376 | getMirrorEl() { | ||
377 | let sourceElRect = this.sourceElRect; | ||
378 | let mirrorEl = this.mirrorEl; | ||
379 | if (!mirrorEl) { | ||
380 | mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true | ||
381 | // we don't want long taps or any mouse interaction causing selection/menus. | ||
382 | // would use preventSelection(), but that prevents selectstart, causing problems. | ||
383 | mirrorEl.style.userSelect = 'none'; | ||
384 | mirrorEl.style.webkitUserSelect = 'none'; | ||
385 | mirrorEl.style.pointerEvents = 'none'; | ||
386 | mirrorEl.classList.add('fc-event-dragging'); | ||
387 | internal.applyStyle(mirrorEl, { | ||
388 | position: 'fixed', | ||
389 | zIndex: this.zIndex, | ||
390 | visibility: '', | ||
391 | boxSizing: 'border-box', | ||
392 | width: sourceElRect.right - sourceElRect.left, | ||
393 | height: sourceElRect.bottom - sourceElRect.top, | ||
394 | right: 'auto', | ||
395 | bottom: 'auto', | ||
396 | margin: 0, | ||
397 | }); | ||
398 | this.parentNode.appendChild(mirrorEl); | ||
399 | } | ||
400 | return mirrorEl; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | /* | ||
405 | Is a cache for a given element's scroll information (all the info that ScrollController stores) | ||
406 | in addition the "client rectangle" of the element.. the area within the scrollbars. | ||
407 | |||
408 | The cache can be in one of two modes: | ||
409 | - doesListening:false - ignores when the container is scrolled by someone else | ||
410 | - doesListening:true - watch for scrolling and update the cache | ||
411 | */ | ||
412 | class ScrollGeomCache extends internal.ScrollController { | ||
413 | constructor(scrollController, doesListening) { | ||
414 | super(); | ||
415 | this.handleScroll = () => { | ||
416 | this.scrollTop = this.scrollController.getScrollTop(); | ||
417 | this.scrollLeft = this.scrollController.getScrollLeft(); | ||
418 | this.handleScrollChange(); | ||
419 | }; | ||
420 | this.scrollController = scrollController; | ||
421 | this.doesListening = doesListening; | ||
422 | this.scrollTop = this.origScrollTop = scrollController.getScrollTop(); | ||
423 | this.scrollLeft = this.origScrollLeft = scrollController.getScrollLeft(); | ||
424 | this.scrollWidth = scrollController.getScrollWidth(); | ||
425 | this.scrollHeight = scrollController.getScrollHeight(); | ||
426 | this.clientWidth = scrollController.getClientWidth(); | ||
427 | this.clientHeight = scrollController.getClientHeight(); | ||
428 | this.clientRect = this.computeClientRect(); // do last in case it needs cached values | ||
429 | if (this.doesListening) { | ||
430 | this.getEventTarget().addEventListener('scroll', this.handleScroll); | ||
431 | } | ||
432 | } | ||
433 | destroy() { | ||
434 | if (this.doesListening) { | ||
435 | this.getEventTarget().removeEventListener('scroll', this.handleScroll); | ||
436 | } | ||
437 | } | ||
438 | getScrollTop() { | ||
439 | return this.scrollTop; | ||
440 | } | ||
441 | getScrollLeft() { | ||
442 | return this.scrollLeft; | ||
443 | } | ||
444 | setScrollTop(top) { | ||
445 | this.scrollController.setScrollTop(top); | ||
446 | if (!this.doesListening) { | ||
447 | // we are not relying on the element to normalize out-of-bounds scroll values | ||
448 | // so we need to sanitize ourselves | ||
449 | this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0); | ||
450 | this.handleScrollChange(); | ||
451 | } | ||
452 | } | ||
453 | setScrollLeft(top) { | ||
454 | this.scrollController.setScrollLeft(top); | ||
455 | if (!this.doesListening) { | ||
456 | // we are not relying on the element to normalize out-of-bounds scroll values | ||
457 | // so we need to sanitize ourselves | ||
458 | this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0); | ||
459 | this.handleScrollChange(); | ||
460 | } | ||
461 | } | ||
462 | getClientWidth() { | ||
463 | return this.clientWidth; | ||
464 | } | ||
465 | getClientHeight() { | ||
466 | return this.clientHeight; | ||
467 | } | ||
468 | getScrollWidth() { | ||
469 | return this.scrollWidth; | ||
470 | } | ||
471 | getScrollHeight() { | ||
472 | return this.scrollHeight; | ||
473 | } | ||
474 | handleScrollChange() { | ||
475 | } | ||
476 | } | ||
477 | |||
478 | class ElementScrollGeomCache extends ScrollGeomCache { | ||
479 | constructor(el, doesListening) { | ||
480 | super(new internal.ElementScrollController(el), doesListening); | ||
481 | } | ||
482 | getEventTarget() { | ||
483 | return this.scrollController.el; | ||
484 | } | ||
485 | computeClientRect() { | ||
486 | return internal.computeInnerRect(this.scrollController.el); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | class WindowScrollGeomCache extends ScrollGeomCache { | ||
491 | constructor(doesListening) { | ||
492 | super(new internal.WindowScrollController(), doesListening); | ||
493 | } | ||
494 | getEventTarget() { | ||
495 | return window; | ||
496 | } | ||
497 | computeClientRect() { | ||
498 | return { | ||
499 | left: this.scrollLeft, | ||
500 | right: this.scrollLeft + this.clientWidth, | ||
501 | top: this.scrollTop, | ||
502 | bottom: this.scrollTop + this.clientHeight, | ||
503 | }; | ||
504 | } | ||
505 | // the window is the only scroll object that changes it's rectangle relative | ||
506 | // to the document's topleft as it scrolls | ||
507 | handleScrollChange() { | ||
508 | this.clientRect = this.computeClientRect(); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | // If available we are using native "performance" API instead of "Date" | ||
513 | // Read more about it on MDN: | ||
514 | // https://developer.mozilla.org/en-US/docs/Web/API/Performance | ||
515 | const getTime = typeof performance === 'function' ? performance.now : Date.now; | ||
516 | /* | ||
517 | For a pointer interaction, automatically scrolls certain scroll containers when the pointer | ||
518 | approaches the edge. | ||
519 | |||
520 | The caller must call start + handleMove + stop. | ||
521 | */ | ||
522 | class AutoScroller { | ||
523 | constructor() { | ||
524 | // options that can be set by caller | ||
525 | this.isEnabled = true; | ||
526 | this.scrollQuery = [window, '.fc-scroller']; | ||
527 | this.edgeThreshold = 50; // pixels | ||
528 | this.maxVelocity = 300; // pixels per second | ||
529 | // internal state | ||
530 | this.pointerScreenX = null; | ||
531 | this.pointerScreenY = null; | ||
532 | this.isAnimating = false; | ||
533 | this.scrollCaches = null; | ||
534 | // protect against the initial pointerdown being too close to an edge and starting the scroll | ||
535 | this.everMovedUp = false; | ||
536 | this.everMovedDown = false; | ||
537 | this.everMovedLeft = false; | ||
538 | this.everMovedRight = false; | ||
539 | this.animate = () => { | ||
540 | if (this.isAnimating) { // wasn't cancelled between animation calls | ||
541 | let edge = this.computeBestEdge(this.pointerScreenX + window.scrollX, this.pointerScreenY + window.scrollY); | ||
542 | if (edge) { | ||
543 | let now = getTime(); | ||
544 | this.handleSide(edge, (now - this.msSinceRequest) / 1000); | ||
545 | this.requestAnimation(now); | ||
546 | } | ||
547 | else { | ||
548 | this.isAnimating = false; // will stop animation | ||
549 | } | ||
550 | } | ||
551 | }; | ||
552 | } | ||
553 | start(pageX, pageY, scrollStartEl) { | ||
554 | if (this.isEnabled) { | ||
555 | this.scrollCaches = this.buildCaches(scrollStartEl); | ||
556 | this.pointerScreenX = null; | ||
557 | this.pointerScreenY = null; | ||
558 | this.everMovedUp = false; | ||
559 | this.everMovedDown = false; | ||
560 | this.everMovedLeft = false; | ||
561 | this.everMovedRight = false; | ||
562 | this.handleMove(pageX, pageY); | ||
563 | } | ||
564 | } | ||
565 | handleMove(pageX, pageY) { | ||
566 | if (this.isEnabled) { | ||
567 | let pointerScreenX = pageX - window.scrollX; | ||
568 | let pointerScreenY = pageY - window.scrollY; | ||
569 | let yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY; | ||
570 | let xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX; | ||
571 | if (yDelta < 0) { | ||
572 | this.everMovedUp = true; | ||
573 | } | ||
574 | else if (yDelta > 0) { | ||
575 | this.everMovedDown = true; | ||
576 | } | ||
577 | if (xDelta < 0) { | ||
578 | this.everMovedLeft = true; | ||
579 | } | ||
580 | else if (xDelta > 0) { | ||
581 | this.everMovedRight = true; | ||
582 | } | ||
583 | this.pointerScreenX = pointerScreenX; | ||
584 | this.pointerScreenY = pointerScreenY; | ||
585 | if (!this.isAnimating) { | ||
586 | this.isAnimating = true; | ||
587 | this.requestAnimation(getTime()); | ||
588 | } | ||
589 | } | ||
590 | } | ||
591 | stop() { | ||
592 | if (this.isEnabled) { | ||
593 | this.isAnimating = false; // will stop animation | ||
594 | for (let scrollCache of this.scrollCaches) { | ||
595 | scrollCache.destroy(); | ||
596 | } | ||
597 | this.scrollCaches = null; | ||
598 | } | ||
599 | } | ||
600 | requestAnimation(now) { | ||
601 | this.msSinceRequest = now; | ||
602 | requestAnimationFrame(this.animate); | ||
603 | } | ||
604 | handleSide(edge, seconds) { | ||
605 | let { scrollCache } = edge; | ||
606 | let { edgeThreshold } = this; | ||
607 | let invDistance = edgeThreshold - edge.distance; | ||
608 | let velocity = // the closer to the edge, the faster we scroll | ||
609 | ((invDistance * invDistance) / (edgeThreshold * edgeThreshold)) * // quadratic | ||
610 | this.maxVelocity * seconds; | ||
611 | let sign = 1; | ||
612 | switch (edge.name) { | ||
613 | case 'left': | ||
614 | sign = -1; | ||
615 | // falls through | ||
616 | case 'right': | ||
617 | scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign); | ||
618 | break; | ||
619 | case 'top': | ||
620 | sign = -1; | ||
621 | // falls through | ||
622 | case 'bottom': | ||
623 | scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign); | ||
624 | break; | ||
625 | } | ||
626 | } | ||
627 | // left/top are relative to document topleft | ||
628 | computeBestEdge(left, top) { | ||
629 | let { edgeThreshold } = this; | ||
630 | let bestSide = null; | ||
631 | let scrollCaches = this.scrollCaches || []; | ||
632 | for (let scrollCache of scrollCaches) { | ||
633 | let rect = scrollCache.clientRect; | ||
634 | let leftDist = left - rect.left; | ||
635 | let rightDist = rect.right - left; | ||
636 | let topDist = top - rect.top; | ||
637 | let bottomDist = rect.bottom - top; | ||
638 | // completely within the rect? | ||
639 | if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) { | ||
640 | if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() && | ||
641 | (!bestSide || bestSide.distance > topDist)) { | ||
642 | bestSide = { scrollCache, name: 'top', distance: topDist }; | ||
643 | } | ||
644 | if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() && | ||
645 | (!bestSide || bestSide.distance > bottomDist)) { | ||
646 | bestSide = { scrollCache, name: 'bottom', distance: bottomDist }; | ||
647 | } | ||
648 | /* | ||
649 | TODO: fix broken RTL scrolling. canScrollLeft always returning false | ||
650 | https://github.com/fullcalendar/fullcalendar/issues/4837 | ||
651 | */ | ||
652 | if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() && | ||
653 | (!bestSide || bestSide.distance > leftDist)) { | ||
654 | bestSide = { scrollCache, name: 'left', distance: leftDist }; | ||
655 | } | ||
656 | if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() && | ||
657 | (!bestSide || bestSide.distance > rightDist)) { | ||
658 | bestSide = { scrollCache, name: 'right', distance: rightDist }; | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | return bestSide; | ||
663 | } | ||
664 | buildCaches(scrollStartEl) { | ||
665 | return this.queryScrollEls(scrollStartEl).map((el) => { | ||
666 | if (el === window) { | ||
667 | return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls | ||
668 | } | ||
669 | return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls | ||
670 | }); | ||
671 | } | ||
672 | queryScrollEls(scrollStartEl) { | ||
673 | let els = []; | ||
674 | for (let query of this.scrollQuery) { | ||
675 | if (typeof query === 'object') { | ||
676 | els.push(query); | ||
677 | } | ||
678 | else { | ||
679 | /* | ||
680 | TODO: in the future, always have auto-scroll happen on element where current Hit came from | ||
681 | Ticket: https://github.com/fullcalendar/fullcalendar/issues/4593 | ||
682 | */ | ||
683 | els.push(...Array.prototype.slice.call(scrollStartEl.getRootNode().querySelectorAll(query))); | ||
684 | } | ||
685 | } | ||
686 | return els; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | Monitors dragging on an element. Has a number of high-level features: | ||
692 | - minimum distance required before dragging | ||
693 | - minimum wait time ("delay") before dragging | ||
694 | - a mirror element that follows the pointer | ||
695 | */ | ||
696 | class FeaturefulElementDragging extends internal.ElementDragging { | ||
697 | constructor(containerEl, selector) { | ||
698 | super(containerEl); | ||
699 | this.containerEl = containerEl; | ||
700 | // options that can be directly set by caller | ||
701 | // the caller can also set the PointerDragging's options as well | ||
702 | this.delay = null; | ||
703 | this.minDistance = 0; | ||
704 | this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag | ||
705 | this.mirrorNeedsRevert = false; | ||
706 | this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup | ||
707 | this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation | ||
708 | this.isDelayEnded = false; | ||
709 | this.isDistanceSurpassed = false; | ||
710 | this.delayTimeoutId = null; | ||
711 | this.onPointerDown = (ev) => { | ||
712 | if (!this.isDragging) { // so new drag doesn't happen while revert animation is going | ||
713 | this.isInteracting = true; | ||
714 | this.isDelayEnded = false; | ||
715 | this.isDistanceSurpassed = false; | ||
716 | internal.preventSelection(document.body); | ||
717 | internal.preventContextMenu(document.body); | ||
718 | // prevent links from being visited if there's an eventual drag. | ||
719 | // also prevents selection in older browsers (maybe?). | ||
720 | // not necessary for touch, besides, browser would complain about passiveness. | ||
721 | if (!ev.isTouch) { | ||
722 | ev.origEvent.preventDefault(); | ||
723 | } | ||
724 | this.emitter.trigger('pointerdown', ev); | ||
725 | if (this.isInteracting && // not destroyed via pointerdown handler | ||
726 | !this.pointer.shouldIgnoreMove) { | ||
727 | // actions related to initiating dragstart+dragmove+dragend... | ||
728 | this.mirror.setIsVisible(false); // reset. caller must set-visible | ||
729 | this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down | ||
730 | this.startDelay(ev); | ||
731 | if (!this.minDistance) { | ||
732 | this.handleDistanceSurpassed(ev); | ||
733 | } | ||
734 | } | ||
735 | } | ||
736 | }; | ||
737 | this.onPointerMove = (ev) => { | ||
738 | if (this.isInteracting) { | ||
739 | this.emitter.trigger('pointermove', ev); | ||
740 | if (!this.isDistanceSurpassed) { | ||
741 | let minDistance = this.minDistance; | ||
742 | let distanceSq; // current distance from the origin, squared | ||
743 | let { deltaX, deltaY } = ev; | ||
744 | distanceSq = deltaX * deltaX + deltaY * deltaY; | ||
745 | if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem | ||
746 | this.handleDistanceSurpassed(ev); | ||
747 | } | ||
748 | } | ||
749 | if (this.isDragging) { | ||
750 | // a real pointer move? (not one simulated by scrolling) | ||
751 | if (ev.origEvent.type !== 'scroll') { | ||
752 | this.mirror.handleMove(ev.pageX, ev.pageY); | ||
753 | this.autoScroller.handleMove(ev.pageX, ev.pageY); | ||
754 | } | ||
755 | this.emitter.trigger('dragmove', ev); | ||
756 | } | ||
757 | } | ||
758 | }; | ||
759 | this.onPointerUp = (ev) => { | ||
760 | if (this.isInteracting) { | ||
761 | this.isInteracting = false; | ||
762 | internal.allowSelection(document.body); | ||
763 | internal.allowContextMenu(document.body); | ||
764 | this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert | ||
765 | if (this.isDragging) { | ||
766 | this.autoScroller.stop(); | ||
767 | this.tryStopDrag(ev); // which will stop the mirror | ||
768 | } | ||
769 | if (this.delayTimeoutId) { | ||
770 | clearTimeout(this.delayTimeoutId); | ||
771 | this.delayTimeoutId = null; | ||
772 | } | ||
773 | } | ||
774 | }; | ||
775 | let pointer = this.pointer = new PointerDragging(containerEl); | ||
776 | pointer.emitter.on('pointerdown', this.onPointerDown); | ||
777 | pointer.emitter.on('pointermove', this.onPointerMove); | ||
778 | pointer.emitter.on('pointerup', this.onPointerUp); | ||
779 | if (selector) { | ||
780 | pointer.selector = selector; | ||
781 | } | ||
782 | this.mirror = new ElementMirror(); | ||
783 | this.autoScroller = new AutoScroller(); | ||
784 | } | ||
785 | destroy() { | ||
786 | this.pointer.destroy(); | ||
787 | // HACK: simulate a pointer-up to end the current drag | ||
788 | // TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire) | ||
789 | this.onPointerUp({}); | ||
790 | } | ||
791 | startDelay(ev) { | ||
792 | if (typeof this.delay === 'number') { | ||
793 | this.delayTimeoutId = setTimeout(() => { | ||
794 | this.delayTimeoutId = null; | ||
795 | this.handleDelayEnd(ev); | ||
796 | }, this.delay); // not assignable to number! | ||
797 | } | ||
798 | else { | ||
799 | this.handleDelayEnd(ev); | ||
800 | } | ||
801 | } | ||
802 | handleDelayEnd(ev) { | ||
803 | this.isDelayEnded = true; | ||
804 | this.tryStartDrag(ev); | ||
805 | } | ||
806 | handleDistanceSurpassed(ev) { | ||
807 | this.isDistanceSurpassed = true; | ||
808 | this.tryStartDrag(ev); | ||
809 | } | ||
810 | tryStartDrag(ev) { | ||
811 | if (this.isDelayEnded && this.isDistanceSurpassed) { | ||
812 | if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) { | ||
813 | this.isDragging = true; | ||
814 | this.mirrorNeedsRevert = false; | ||
815 | this.autoScroller.start(ev.pageX, ev.pageY, this.containerEl); | ||
816 | this.emitter.trigger('dragstart', ev); | ||
817 | if (this.touchScrollAllowed === false) { | ||
818 | this.pointer.cancelTouchScroll(); | ||
819 | } | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | tryStopDrag(ev) { | ||
824 | // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events | ||
825 | // that come from the document to fire beforehand. much more convenient this way. | ||
826 | this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev)); | ||
827 | } | ||
828 | stopDrag(ev) { | ||
829 | this.isDragging = false; | ||
830 | this.emitter.trigger('dragend', ev); | ||
831 | } | ||
832 | // fill in the implementations... | ||
833 | setIgnoreMove(bool) { | ||
834 | this.pointer.shouldIgnoreMove = bool; | ||
835 | } | ||
836 | setMirrorIsVisible(bool) { | ||
837 | this.mirror.setIsVisible(bool); | ||
838 | } | ||
839 | setMirrorNeedsRevert(bool) { | ||
840 | this.mirrorNeedsRevert = bool; | ||
841 | } | ||
842 | setAutoScrollEnabled(bool) { | ||
843 | this.autoScroller.isEnabled = bool; | ||
844 | } | ||
845 | } | ||
846 | |||
847 | /* | ||
848 | When this class is instantiated, it records the offset of an element (relative to the document topleft), | ||
849 | and continues to monitor scrolling, updating the cached coordinates if it needs to. | ||
850 | Does not access the DOM after instantiation, so highly performant. | ||
851 | |||
852 | Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element | ||
853 | and an determine if a given point is inside the combined clipping rectangle. | ||
854 | */ | ||
855 | class OffsetTracker { | ||
856 | constructor(el) { | ||
857 | this.el = el; | ||
858 | this.origRect = internal.computeRect(el); | ||
859 | // will work fine for divs that have overflow:hidden | ||
860 | this.scrollCaches = internal.getClippingParents(el).map((scrollEl) => new ElementScrollGeomCache(scrollEl, true)); | ||
861 | } | ||
862 | destroy() { | ||
863 | for (let scrollCache of this.scrollCaches) { | ||
864 | scrollCache.destroy(); | ||
865 | } | ||
866 | } | ||
867 | computeLeft() { | ||
868 | let left = this.origRect.left; | ||
869 | for (let scrollCache of this.scrollCaches) { | ||
870 | left += scrollCache.origScrollLeft - scrollCache.getScrollLeft(); | ||
871 | } | ||
872 | return left; | ||
873 | } | ||
874 | computeTop() { | ||
875 | let top = this.origRect.top; | ||
876 | for (let scrollCache of this.scrollCaches) { | ||
877 | top += scrollCache.origScrollTop - scrollCache.getScrollTop(); | ||
878 | } | ||
879 | return top; | ||
880 | } | ||
881 | isWithinClipping(pageX, pageY) { | ||
882 | let point = { left: pageX, top: pageY }; | ||
883 | for (let scrollCache of this.scrollCaches) { | ||
884 | if (!isIgnoredClipping(scrollCache.getEventTarget()) && | ||
885 | !internal.pointInsideRect(point, scrollCache.clientRect)) { | ||
886 | return false; | ||
887 | } | ||
888 | } | ||
889 | return true; | ||
890 | } | ||
891 | } | ||
892 | // certain clipping containers should never constrain interactions, like <html> and <body> | ||
893 | // https://github.com/fullcalendar/fullcalendar/issues/3615 | ||
894 | function isIgnoredClipping(node) { | ||
895 | let tagName = node.tagName; | ||
896 | return tagName === 'HTML' || tagName === 'BODY'; | ||
897 | } | ||
898 | |||
899 | /* | ||
900 | Tracks movement over multiple droppable areas (aka "hits") | ||
901 | that exist in one or more DateComponents. | ||
902 | Relies on an existing draggable. | ||
903 | |||
904 | emits: | ||
905 | - pointerdown | ||
906 | - dragstart | ||
907 | - hitchange - fires initially, even if not over a hit | ||
908 | - pointerup | ||
909 | - (hitchange - again, to null, if ended over a hit) | ||
910 | - dragend | ||
911 | */ | ||
912 | class HitDragging { | ||
913 | constructor(dragging, droppableStore) { | ||
914 | // options that can be set by caller | ||
915 | this.useSubjectCenter = false; | ||
916 | this.requireInitial = true; // if doesn't start out on a hit, won't emit any events | ||
917 | this.disablePointCheck = false; | ||
918 | this.initialHit = null; | ||
919 | this.movingHit = null; | ||
920 | this.finalHit = null; // won't ever be populated if shouldIgnoreMove | ||
921 | this.handlePointerDown = (ev) => { | ||
922 | let { dragging } = this; | ||
923 | this.initialHit = null; | ||
924 | this.movingHit = null; | ||
925 | this.finalHit = null; | ||
926 | this.prepareHits(); | ||
927 | this.processFirstCoord(ev); | ||
928 | if (this.initialHit || !this.requireInitial) { | ||
929 | dragging.setIgnoreMove(false); | ||
930 | // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :( | ||
931 | this.emitter.trigger('pointerdown', ev); | ||
932 | } | ||
933 | else { | ||
934 | dragging.setIgnoreMove(true); | ||
935 | } | ||
936 | }; | ||
937 | this.handleDragStart = (ev) => { | ||
938 | this.emitter.trigger('dragstart', ev); | ||
939 | this.handleMove(ev, true); // force = fire even if initially null | ||
940 | }; | ||
941 | this.handleDragMove = (ev) => { | ||
942 | this.emitter.trigger('dragmove', ev); | ||
943 | this.handleMove(ev); | ||
944 | }; | ||
945 | this.handlePointerUp = (ev) => { | ||
946 | this.releaseHits(); | ||
947 | this.emitter.trigger('pointerup', ev); | ||
948 | }; | ||
949 | this.handleDragEnd = (ev) => { | ||
950 | if (this.movingHit) { | ||
951 | this.emitter.trigger('hitupdate', null, true, ev); | ||
952 | } | ||
953 | this.finalHit = this.movingHit; | ||
954 | this.movingHit = null; | ||
955 | this.emitter.trigger('dragend', ev); | ||
956 | }; | ||
957 | this.droppableStore = droppableStore; | ||
958 | dragging.emitter.on('pointerdown', this.handlePointerDown); | ||
959 | dragging.emitter.on('dragstart', this.handleDragStart); | ||
960 | dragging.emitter.on('dragmove', this.handleDragMove); | ||
961 | dragging.emitter.on('pointerup', this.handlePointerUp); | ||
962 | dragging.emitter.on('dragend', this.handleDragEnd); | ||
963 | this.dragging = dragging; | ||
964 | this.emitter = new internal.Emitter(); | ||
965 | } | ||
966 | // sets initialHit | ||
967 | // sets coordAdjust | ||
968 | processFirstCoord(ev) { | ||
969 | let origPoint = { left: ev.pageX, top: ev.pageY }; | ||
970 | let adjustedPoint = origPoint; | ||
971 | let subjectEl = ev.subjectEl; | ||
972 | let subjectRect; | ||
973 | if (subjectEl instanceof HTMLElement) { // i.e. not a Document/ShadowRoot | ||
974 | subjectRect = internal.computeRect(subjectEl); | ||
975 | adjustedPoint = internal.constrainPoint(adjustedPoint, subjectRect); | ||
976 | } | ||
977 | let initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top); | ||
978 | if (initialHit) { | ||
979 | if (this.useSubjectCenter && subjectRect) { | ||
980 | let slicedSubjectRect = internal.intersectRects(subjectRect, initialHit.rect); | ||
981 | if (slicedSubjectRect) { | ||
982 | adjustedPoint = internal.getRectCenter(slicedSubjectRect); | ||
983 | } | ||
984 | } | ||
985 | this.coordAdjust = internal.diffPoints(adjustedPoint, origPoint); | ||
986 | } | ||
987 | else { | ||
988 | this.coordAdjust = { left: 0, top: 0 }; | ||
989 | } | ||
990 | } | ||
991 | handleMove(ev, forceHandle) { | ||
992 | let hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top); | ||
993 | if (forceHandle || !isHitsEqual(this.movingHit, hit)) { | ||
994 | this.movingHit = hit; | ||
995 | this.emitter.trigger('hitupdate', hit, false, ev); | ||
996 | } | ||
997 | } | ||
998 | prepareHits() { | ||
999 | this.offsetTrackers = internal.mapHash(this.droppableStore, (interactionSettings) => { | ||
1000 | interactionSettings.component.prepareHits(); | ||
1001 | return new OffsetTracker(interactionSettings.el); | ||
1002 | }); | ||
1003 | } | ||
1004 | releaseHits() { | ||
1005 | let { offsetTrackers } = this; | ||
1006 | for (let id in offsetTrackers) { | ||
1007 | offsetTrackers[id].destroy(); | ||
1008 | } | ||
1009 | this.offsetTrackers = {}; | ||
1010 | } | ||
1011 | queryHitForOffset(offsetLeft, offsetTop) { | ||
1012 | let { droppableStore, offsetTrackers } = this; | ||
1013 | let bestHit = null; | ||
1014 | for (let id in droppableStore) { | ||
1015 | let component = droppableStore[id].component; | ||
1016 | let offsetTracker = offsetTrackers[id]; | ||
1017 | if (offsetTracker && // wasn't destroyed mid-drag | ||
1018 | offsetTracker.isWithinClipping(offsetLeft, offsetTop)) { | ||
1019 | let originLeft = offsetTracker.computeLeft(); | ||
1020 | let originTop = offsetTracker.computeTop(); | ||
1021 | let positionLeft = offsetLeft - originLeft; | ||
1022 | let positionTop = offsetTop - originTop; | ||
1023 | let { origRect } = offsetTracker; | ||
1024 | let width = origRect.right - origRect.left; | ||
1025 | let height = origRect.bottom - origRect.top; | ||
1026 | if ( | ||
1027 | // must be within the element's bounds | ||
1028 | positionLeft >= 0 && positionLeft < width && | ||
1029 | positionTop >= 0 && positionTop < height) { | ||
1030 | let hit = component.queryHit(positionLeft, positionTop, width, height); | ||
1031 | if (hit && ( | ||
1032 | // make sure the hit is within activeRange, meaning it's not a dead cell | ||
1033 | internal.rangeContainsRange(hit.dateProfile.activeRange, hit.dateSpan.range)) && | ||
1034 | // Ensure the component we are querying for the hit is accessibly my the pointer | ||
1035 | // Prevents obscured calendars (ex: under a modal dialog) from accepting hit | ||
1036 | // https://github.com/fullcalendar/fullcalendar/issues/5026 | ||
1037 | (this.disablePointCheck || | ||
1038 | offsetTracker.el.contains(offsetTracker.el.getRootNode().elementFromPoint( | ||
1039 | // add-back origins to get coordinate relative to top-left of window viewport | ||
1040 | positionLeft + originLeft - window.scrollX, positionTop + originTop - window.scrollY))) && | ||
1041 | (!bestHit || hit.layer > bestHit.layer)) { | ||
1042 | hit.componentId = id; | ||
1043 | hit.context = component.context; | ||
1044 | // TODO: better way to re-orient rectangle | ||
1045 | hit.rect.left += originLeft; | ||
1046 | hit.rect.right += originLeft; | ||
1047 | hit.rect.top += originTop; | ||
1048 | hit.rect.bottom += originTop; | ||
1049 | bestHit = hit; | ||
1050 | } | ||
1051 | } | ||
1052 | } | ||
1053 | } | ||
1054 | return bestHit; | ||
1055 | } | ||
1056 | } | ||
1057 | function isHitsEqual(hit0, hit1) { | ||
1058 | if (!hit0 && !hit1) { | ||
1059 | return true; | ||
1060 | } | ||
1061 | if (Boolean(hit0) !== Boolean(hit1)) { | ||
1062 | return false; | ||
1063 | } | ||
1064 | return internal.isDateSpansEqual(hit0.dateSpan, hit1.dateSpan); | ||
1065 | } | ||
1066 | |||
1067 | function buildDatePointApiWithContext(dateSpan, context) { | ||
1068 | let props = {}; | ||
1069 | for (let transform of context.pluginHooks.datePointTransforms) { | ||
1070 | Object.assign(props, transform(dateSpan, context)); | ||
1071 | } | ||
1072 | Object.assign(props, buildDatePointApi(dateSpan, context.dateEnv)); | ||
1073 | return props; | ||
1074 | } | ||
1075 | function buildDatePointApi(span, dateEnv) { | ||
1076 | return { | ||
1077 | date: dateEnv.toDate(span.range.start), | ||
1078 | dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }), | ||
1079 | allDay: span.allDay, | ||
1080 | }; | ||
1081 | } | ||
1082 | |||
1083 | /* | ||
1084 | Monitors when the user clicks on a specific date/time of a component. | ||
1085 | A pointerdown+pointerup on the same "hit" constitutes a click. | ||
1086 | */ | ||
1087 | class DateClicking extends internal.Interaction { | ||
1088 | constructor(settings) { | ||
1089 | super(settings); | ||
1090 | this.handlePointerDown = (pev) => { | ||
1091 | let { dragging } = this; | ||
1092 | let downEl = pev.origEvent.target; | ||
1093 | // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired | ||
1094 | dragging.setIgnoreMove(!this.component.isValidDateDownEl(downEl)); | ||
1095 | }; | ||
1096 | // won't even fire if moving was ignored | ||
1097 | this.handleDragEnd = (ev) => { | ||
1098 | let { component } = this; | ||
1099 | let { pointer } = this.dragging; | ||
1100 | if (!pointer.wasTouchScroll) { | ||
1101 | let { initialHit, finalHit } = this.hitDragging; | ||
1102 | if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) { | ||
1103 | let { context } = component; | ||
1104 | let arg = Object.assign(Object.assign({}, buildDatePointApiWithContext(initialHit.dateSpan, context)), { dayEl: initialHit.dayEl, jsEvent: ev.origEvent, view: context.viewApi || context.calendarApi.view }); | ||
1105 | context.emitter.trigger('dateClick', arg); | ||
1106 | } | ||
1107 | } | ||
1108 | }; | ||
1109 | // we DO want to watch pointer moves because otherwise finalHit won't get populated | ||
1110 | this.dragging = new FeaturefulElementDragging(settings.el); | ||
1111 | this.dragging.autoScroller.isEnabled = false; | ||
1112 | let hitDragging = this.hitDragging = new HitDragging(this.dragging, internal.interactionSettingsToStore(settings)); | ||
1113 | hitDragging.emitter.on('pointerdown', this.handlePointerDown); | ||
1114 | hitDragging.emitter.on('dragend', this.handleDragEnd); | ||
1115 | } | ||
1116 | destroy() { | ||
1117 | this.dragging.destroy(); | ||
1118 | } | ||
1119 | } | ||
1120 | |||
1121 | /* | ||
1122 | Tracks when the user selects a portion of time of a component, | ||
1123 | constituted by a drag over date cells, with a possible delay at the beginning of the drag. | ||
1124 | */ | ||
1125 | class DateSelecting extends internal.Interaction { | ||
1126 | constructor(settings) { | ||
1127 | super(settings); | ||
1128 | this.dragSelection = null; | ||
1129 | this.handlePointerDown = (ev) => { | ||
1130 | let { component, dragging } = this; | ||
1131 | let { options } = component.context; | ||
1132 | let canSelect = options.selectable && | ||
1133 | component.isValidDateDownEl(ev.origEvent.target); | ||
1134 | // don't bother to watch expensive moves if component won't do selection | ||
1135 | dragging.setIgnoreMove(!canSelect); | ||
1136 | // if touch, require user to hold down | ||
1137 | dragging.delay = ev.isTouch ? getComponentTouchDelay$1(component) : null; | ||
1138 | }; | ||
1139 | this.handleDragStart = (ev) => { | ||
1140 | this.component.context.calendarApi.unselect(ev); // unselect previous selections | ||
1141 | }; | ||
1142 | this.handleHitUpdate = (hit, isFinal) => { | ||
1143 | let { context } = this.component; | ||
1144 | let dragSelection = null; | ||
1145 | let isInvalid = false; | ||
1146 | if (hit) { | ||
1147 | let initialHit = this.hitDragging.initialHit; | ||
1148 | let disallowed = hit.componentId === initialHit.componentId | ||
1149 | && this.isHitComboAllowed | ||
1150 | && !this.isHitComboAllowed(initialHit, hit); | ||
1151 | if (!disallowed) { | ||
1152 | dragSelection = joinHitsIntoSelection(initialHit, hit, context.pluginHooks.dateSelectionTransformers); | ||
1153 | } | ||
1154 | if (!dragSelection || !internal.isDateSelectionValid(dragSelection, hit.dateProfile, context)) { | ||
1155 | isInvalid = true; | ||
1156 | dragSelection = null; | ||
1157 | } | ||
1158 | } | ||
1159 | if (dragSelection) { | ||
1160 | context.dispatch({ type: 'SELECT_DATES', selection: dragSelection }); | ||
1161 | } | ||
1162 | else if (!isFinal) { // only unselect if moved away while dragging | ||
1163 | context.dispatch({ type: 'UNSELECT_DATES' }); | ||
1164 | } | ||
1165 | if (!isInvalid) { | ||
1166 | internal.enableCursor(); | ||
1167 | } | ||
1168 | else { | ||
1169 | internal.disableCursor(); | ||
1170 | } | ||
1171 | if (!isFinal) { | ||
1172 | this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging | ||
1173 | } | ||
1174 | }; | ||
1175 | this.handlePointerUp = (pev) => { | ||
1176 | if (this.dragSelection) { | ||
1177 | // selection is already rendered, so just need to report selection | ||
1178 | internal.triggerDateSelect(this.dragSelection, pev, this.component.context); | ||
1179 | this.dragSelection = null; | ||
1180 | } | ||
1181 | }; | ||
1182 | let { component } = settings; | ||
1183 | let { options } = component.context; | ||
1184 | let dragging = this.dragging = new FeaturefulElementDragging(settings.el); | ||
1185 | dragging.touchScrollAllowed = false; | ||
1186 | dragging.minDistance = options.selectMinDistance || 0; | ||
1187 | dragging.autoScroller.isEnabled = options.dragScroll; | ||
1188 | let hitDragging = this.hitDragging = new HitDragging(this.dragging, internal.interactionSettingsToStore(settings)); | ||
1189 | hitDragging.emitter.on('pointerdown', this.handlePointerDown); | ||
1190 | hitDragging.emitter.on('dragstart', this.handleDragStart); | ||
1191 | hitDragging.emitter.on('hitupdate', this.handleHitUpdate); | ||
1192 | hitDragging.emitter.on('pointerup', this.handlePointerUp); | ||
1193 | } | ||
1194 | destroy() { | ||
1195 | this.dragging.destroy(); | ||
1196 | } | ||
1197 | } | ||
1198 | function getComponentTouchDelay$1(component) { | ||
1199 | let { options } = component.context; | ||
1200 | let delay = options.selectLongPressDelay; | ||
1201 | if (delay == null) { | ||
1202 | delay = options.longPressDelay; | ||
1203 | } | ||
1204 | return delay; | ||
1205 | } | ||
1206 | function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) { | ||
1207 | let dateSpan0 = hit0.dateSpan; | ||
1208 | let dateSpan1 = hit1.dateSpan; | ||
1209 | let ms = [ | ||
1210 | dateSpan0.range.start, | ||
1211 | dateSpan0.range.end, | ||
1212 | dateSpan1.range.start, | ||
1213 | dateSpan1.range.end, | ||
1214 | ]; | ||
1215 | ms.sort(internal.compareNumbers); | ||
1216 | let props = {}; | ||
1217 | for (let transformer of dateSelectionTransformers) { | ||
1218 | let res = transformer(hit0, hit1); | ||
1219 | if (res === false) { | ||
1220 | return null; | ||
1221 | } | ||
1222 | if (res) { | ||
1223 | Object.assign(props, res); | ||
1224 | } | ||
1225 | } | ||
1226 | props.range = { start: ms[0], end: ms[3] }; | ||
1227 | props.allDay = dateSpan0.allDay; | ||
1228 | return props; | ||
1229 | } | ||
1230 | |||
1231 | class EventDragging extends internal.Interaction { | ||
1232 | constructor(settings) { | ||
1233 | super(settings); | ||
1234 | // internal state | ||
1235 | this.subjectEl = null; | ||
1236 | this.subjectSeg = null; // the seg being selected/dragged | ||
1237 | this.isDragging = false; | ||
1238 | this.eventRange = null; | ||
1239 | this.relevantEvents = null; // the events being dragged | ||
1240 | this.receivingContext = null; | ||
1241 | this.validMutation = null; | ||
1242 | this.mutatedRelevantEvents = null; | ||
1243 | this.handlePointerDown = (ev) => { | ||
1244 | let origTarget = ev.origEvent.target; | ||
1245 | let { component, dragging } = this; | ||
1246 | let { mirror } = dragging; | ||
1247 | let { options } = component.context; | ||
1248 | let initialContext = component.context; | ||
1249 | this.subjectEl = ev.subjectEl; | ||
1250 | let subjectSeg = this.subjectSeg = internal.getElSeg(ev.subjectEl); | ||
1251 | let eventRange = this.eventRange = subjectSeg.eventRange; | ||
1252 | let eventInstanceId = eventRange.instance.instanceId; | ||
1253 | this.relevantEvents = internal.getRelevantEvents(initialContext.getCurrentData().eventStore, eventInstanceId); | ||
1254 | dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance; | ||
1255 | dragging.delay = | ||
1256 | // only do a touch delay if touch and this event hasn't been selected yet | ||
1257 | (ev.isTouch && eventInstanceId !== component.props.eventSelection) ? | ||
1258 | getComponentTouchDelay(component) : | ||
1259 | null; | ||
1260 | if (options.fixedMirrorParent) { | ||
1261 | mirror.parentNode = options.fixedMirrorParent; | ||
1262 | } | ||
1263 | else { | ||
1264 | mirror.parentNode = internal.elementClosest(origTarget, '.fc'); | ||
1265 | } | ||
1266 | mirror.revertDuration = options.dragRevertDuration; | ||
1267 | let isValid = component.isValidSegDownEl(origTarget) && | ||
1268 | !internal.elementClosest(origTarget, '.fc-event-resizer'); // NOT on a resizer | ||
1269 | dragging.setIgnoreMove(!isValid); | ||
1270 | // disable dragging for elements that are resizable (ie, selectable) | ||
1271 | // but are not draggable | ||
1272 | this.isDragging = isValid && | ||
1273 | ev.subjectEl.classList.contains('fc-event-draggable'); | ||
1274 | }; | ||
1275 | this.handleDragStart = (ev) => { | ||
1276 | let initialContext = this.component.context; | ||
1277 | let eventRange = this.eventRange; | ||
1278 | let eventInstanceId = eventRange.instance.instanceId; | ||
1279 | if (ev.isTouch) { | ||
1280 | // need to select a different event? | ||
1281 | if (eventInstanceId !== this.component.props.eventSelection) { | ||
1282 | initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId }); | ||
1283 | } | ||
1284 | } | ||
1285 | else { | ||
1286 | // if now using mouse, but was previous touch interaction, clear selected event | ||
1287 | initialContext.dispatch({ type: 'UNSELECT_EVENT' }); | ||
1288 | } | ||
1289 | if (this.isDragging) { | ||
1290 | initialContext.calendarApi.unselect(ev); // unselect *date* selection | ||
1291 | initialContext.emitter.trigger('eventDragStart', { | ||
1292 | el: this.subjectEl, | ||
1293 | event: new internal.EventImpl(initialContext, eventRange.def, eventRange.instance), | ||
1294 | jsEvent: ev.origEvent, | ||
1295 | view: initialContext.viewApi, | ||
1296 | }); | ||
1297 | } | ||
1298 | }; | ||
1299 | this.handleHitUpdate = (hit, isFinal) => { | ||
1300 | if (!this.isDragging) { | ||
1301 | return; | ||
1302 | } | ||
1303 | let relevantEvents = this.relevantEvents; | ||
1304 | let initialHit = this.hitDragging.initialHit; | ||
1305 | let initialContext = this.component.context; | ||
1306 | // states based on new hit | ||
1307 | let receivingContext = null; | ||
1308 | let mutation = null; | ||
1309 | let mutatedRelevantEvents = null; | ||
1310 | let isInvalid = false; | ||
1311 | let interaction = { | ||
1312 | affectedEvents: relevantEvents, | ||
1313 | mutatedEvents: internal.createEmptyEventStore(), | ||
1314 | isEvent: true, | ||
1315 | }; | ||
1316 | if (hit) { | ||
1317 | receivingContext = hit.context; | ||
1318 | let receivingOptions = receivingContext.options; | ||
1319 | if (initialContext === receivingContext || | ||
1320 | (receivingOptions.editable && receivingOptions.droppable)) { | ||
1321 | mutation = computeEventMutation(initialHit, hit, this.eventRange.instance.range.start, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers); | ||
1322 | if (mutation) { | ||
1323 | mutatedRelevantEvents = internal.applyMutationToEventStore(relevantEvents, receivingContext.getCurrentData().eventUiBases, mutation, receivingContext); | ||
1324 | interaction.mutatedEvents = mutatedRelevantEvents; | ||
1325 | if (!internal.isInteractionValid(interaction, hit.dateProfile, receivingContext)) { | ||
1326 | isInvalid = true; | ||
1327 | mutation = null; | ||
1328 | mutatedRelevantEvents = null; | ||
1329 | interaction.mutatedEvents = internal.createEmptyEventStore(); | ||
1330 | } | ||
1331 | } | ||
1332 | } | ||
1333 | else { | ||
1334 | receivingContext = null; | ||
1335 | } | ||
1336 | } | ||
1337 | this.displayDrag(receivingContext, interaction); | ||
1338 | if (!isInvalid) { | ||
1339 | internal.enableCursor(); | ||
1340 | } | ||
1341 | else { | ||
1342 | internal.disableCursor(); | ||
1343 | } | ||
1344 | if (!isFinal) { | ||
1345 | if (initialContext === receivingContext && // TODO: write test for this | ||
1346 | isHitsEqual(initialHit, hit)) { | ||
1347 | mutation = null; | ||
1348 | } | ||
1349 | this.dragging.setMirrorNeedsRevert(!mutation); | ||
1350 | // render the mirror if no already-rendered mirror | ||
1351 | // TODO: wish we could somehow wait for dispatch to guarantee render | ||
1352 | this.dragging.setMirrorIsVisible(!hit || !this.subjectEl.getRootNode().querySelector('.fc-event-mirror')); | ||
1353 | // assign states based on new hit | ||
1354 | this.receivingContext = receivingContext; | ||
1355 | this.validMutation = mutation; | ||
1356 | this.mutatedRelevantEvents = mutatedRelevantEvents; | ||
1357 | } | ||
1358 | }; | ||
1359 | this.handlePointerUp = () => { | ||
1360 | if (!this.isDragging) { | ||
1361 | this.cleanup(); // because handleDragEnd won't fire | ||
1362 | } | ||
1363 | }; | ||
1364 | this.handleDragEnd = (ev) => { | ||
1365 | if (this.isDragging) { | ||
1366 | let initialContext = this.component.context; | ||
1367 | let initialView = initialContext.viewApi; | ||
1368 | let { receivingContext, validMutation } = this; | ||
1369 | let eventDef = this.eventRange.def; | ||
1370 | let eventInstance = this.eventRange.instance; | ||
1371 | let eventApi = new internal.EventImpl(initialContext, eventDef, eventInstance); | ||
1372 | let relevantEvents = this.relevantEvents; | ||
1373 | let mutatedRelevantEvents = this.mutatedRelevantEvents; | ||
1374 | let { finalHit } = this.hitDragging; | ||
1375 | this.clearDrag(); // must happen after revert animation | ||
1376 | initialContext.emitter.trigger('eventDragStop', { | ||
1377 | el: this.subjectEl, | ||
1378 | event: eventApi, | ||
1379 | jsEvent: ev.origEvent, | ||
1380 | view: initialView, | ||
1381 | }); | ||
1382 | if (validMutation) { | ||
1383 | // dropped within same calendar | ||
1384 | if (receivingContext === initialContext) { | ||
1385 | let updatedEventApi = new internal.EventImpl(initialContext, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null); | ||
1386 | initialContext.dispatch({ | ||
1387 | type: 'MERGE_EVENTS', | ||
1388 | eventStore: mutatedRelevantEvents, | ||
1389 | }); | ||
1390 | let eventChangeArg = { | ||
1391 | oldEvent: eventApi, | ||
1392 | event: updatedEventApi, | ||
1393 | relatedEvents: internal.buildEventApis(mutatedRelevantEvents, initialContext, eventInstance), | ||
1394 | revert() { | ||
1395 | initialContext.dispatch({ | ||
1396 | type: 'MERGE_EVENTS', | ||
1397 | eventStore: relevantEvents, // the pre-change data | ||
1398 | }); | ||
1399 | }, | ||
1400 | }; | ||
1401 | let transformed = {}; | ||
1402 | for (let transformer of initialContext.getCurrentData().pluginHooks.eventDropTransformers) { | ||
1403 | Object.assign(transformed, transformer(validMutation, initialContext)); | ||
1404 | } | ||
1405 | initialContext.emitter.trigger('eventDrop', Object.assign(Object.assign(Object.assign({}, eventChangeArg), transformed), { el: ev.subjectEl, delta: validMutation.datesDelta, jsEvent: ev.origEvent, view: initialView })); | ||
1406 | initialContext.emitter.trigger('eventChange', eventChangeArg); | ||
1407 | // dropped in different calendar | ||
1408 | } | ||
1409 | else if (receivingContext) { | ||
1410 | let eventRemoveArg = { | ||
1411 | event: eventApi, | ||
1412 | relatedEvents: internal.buildEventApis(relevantEvents, initialContext, eventInstance), | ||
1413 | revert() { | ||
1414 | initialContext.dispatch({ | ||
1415 | type: 'MERGE_EVENTS', | ||
1416 | eventStore: relevantEvents, | ||
1417 | }); | ||
1418 | }, | ||
1419 | }; | ||
1420 | initialContext.emitter.trigger('eventLeave', Object.assign(Object.assign({}, eventRemoveArg), { draggedEl: ev.subjectEl, view: initialView })); | ||
1421 | initialContext.dispatch({ | ||
1422 | type: 'REMOVE_EVENTS', | ||
1423 | eventStore: relevantEvents, | ||
1424 | }); | ||
1425 | initialContext.emitter.trigger('eventRemove', eventRemoveArg); | ||
1426 | let addedEventDef = mutatedRelevantEvents.defs[eventDef.defId]; | ||
1427 | let addedEventInstance = mutatedRelevantEvents.instances[eventInstance.instanceId]; | ||
1428 | let addedEventApi = new internal.EventImpl(receivingContext, addedEventDef, addedEventInstance); | ||
1429 | receivingContext.dispatch({ | ||
1430 | type: 'MERGE_EVENTS', | ||
1431 | eventStore: mutatedRelevantEvents, | ||
1432 | }); | ||
1433 | let eventAddArg = { | ||
1434 | event: addedEventApi, | ||
1435 | relatedEvents: internal.buildEventApis(mutatedRelevantEvents, receivingContext, addedEventInstance), | ||
1436 | revert() { | ||
1437 | receivingContext.dispatch({ | ||
1438 | type: 'REMOVE_EVENTS', | ||
1439 | eventStore: mutatedRelevantEvents, | ||
1440 | }); | ||
1441 | }, | ||
1442 | }; | ||
1443 | receivingContext.emitter.trigger('eventAdd', eventAddArg); | ||
1444 | if (ev.isTouch) { | ||
1445 | receivingContext.dispatch({ | ||
1446 | type: 'SELECT_EVENT', | ||
1447 | eventInstanceId: eventInstance.instanceId, | ||
1448 | }); | ||
1449 | } | ||
1450 | receivingContext.emitter.trigger('drop', Object.assign(Object.assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.context.viewApi })); | ||
1451 | receivingContext.emitter.trigger('eventReceive', Object.assign(Object.assign({}, eventAddArg), { draggedEl: ev.subjectEl, view: finalHit.context.viewApi })); | ||
1452 | } | ||
1453 | } | ||
1454 | else { | ||
1455 | initialContext.emitter.trigger('_noEventDrop'); | ||
1456 | } | ||
1457 | } | ||
1458 | this.cleanup(); | ||
1459 | }; | ||
1460 | let { component } = this; | ||
1461 | let { options } = component.context; | ||
1462 | let dragging = this.dragging = new FeaturefulElementDragging(settings.el); | ||
1463 | dragging.pointer.selector = EventDragging.SELECTOR; | ||
1464 | dragging.touchScrollAllowed = false; | ||
1465 | dragging.autoScroller.isEnabled = options.dragScroll; | ||
1466 | let hitDragging = this.hitDragging = new HitDragging(this.dragging, internal.interactionSettingsStore); | ||
1467 | hitDragging.useSubjectCenter = settings.useEventCenter; | ||
1468 | hitDragging.emitter.on('pointerdown', this.handlePointerDown); | ||
1469 | hitDragging.emitter.on('dragstart', this.handleDragStart); | ||
1470 | hitDragging.emitter.on('hitupdate', this.handleHitUpdate); | ||
1471 | hitDragging.emitter.on('pointerup', this.handlePointerUp); | ||
1472 | hitDragging.emitter.on('dragend', this.handleDragEnd); | ||
1473 | } | ||
1474 | destroy() { | ||
1475 | this.dragging.destroy(); | ||
1476 | } | ||
1477 | // render a drag state on the next receivingCalendar | ||
1478 | displayDrag(nextContext, state) { | ||
1479 | let initialContext = this.component.context; | ||
1480 | let prevContext = this.receivingContext; | ||
1481 | // does the previous calendar need to be cleared? | ||
1482 | if (prevContext && prevContext !== nextContext) { | ||
1483 | // does the initial calendar need to be cleared? | ||
1484 | // if so, don't clear all the way. we still need to to hide the affectedEvents | ||
1485 | if (prevContext === initialContext) { | ||
1486 | prevContext.dispatch({ | ||
1487 | type: 'SET_EVENT_DRAG', | ||
1488 | state: { | ||
1489 | affectedEvents: state.affectedEvents, | ||
1490 | mutatedEvents: internal.createEmptyEventStore(), | ||
1491 | isEvent: true, | ||
1492 | }, | ||
1493 | }); | ||
1494 | // completely clear the old calendar if it wasn't the initial | ||
1495 | } | ||
1496 | else { | ||
1497 | prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); | ||
1498 | } | ||
1499 | } | ||
1500 | if (nextContext) { | ||
1501 | nextContext.dispatch({ type: 'SET_EVENT_DRAG', state }); | ||
1502 | } | ||
1503 | } | ||
1504 | clearDrag() { | ||
1505 | let initialCalendar = this.component.context; | ||
1506 | let { receivingContext } = this; | ||
1507 | if (receivingContext) { | ||
1508 | receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); | ||
1509 | } | ||
1510 | // the initial calendar might have an dummy drag state from displayDrag | ||
1511 | if (initialCalendar !== receivingContext) { | ||
1512 | initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' }); | ||
1513 | } | ||
1514 | } | ||
1515 | cleanup() { | ||
1516 | this.subjectSeg = null; | ||
1517 | this.isDragging = false; | ||
1518 | this.eventRange = null; | ||
1519 | this.relevantEvents = null; | ||
1520 | this.receivingContext = null; | ||
1521 | this.validMutation = null; | ||
1522 | this.mutatedRelevantEvents = null; | ||
1523 | } | ||
1524 | } | ||
1525 | // TODO: test this in IE11 | ||
1526 | // QUESTION: why do we need it on the resizable??? | ||
1527 | EventDragging.SELECTOR = '.fc-event-draggable, .fc-event-resizable'; | ||
1528 | function computeEventMutation(hit0, hit1, eventInstanceStart, massagers) { | ||
1529 | let dateSpan0 = hit0.dateSpan; | ||
1530 | let dateSpan1 = hit1.dateSpan; | ||
1531 | let date0 = dateSpan0.range.start; | ||
1532 | let date1 = dateSpan1.range.start; | ||
1533 | let standardProps = {}; | ||
1534 | if (dateSpan0.allDay !== dateSpan1.allDay) { | ||
1535 | standardProps.allDay = dateSpan1.allDay; | ||
1536 | standardProps.hasEnd = hit1.context.options.allDayMaintainDuration; | ||
1537 | if (dateSpan1.allDay) { | ||
1538 | // means date1 is already start-of-day, | ||
1539 | // but date0 needs to be converted | ||
1540 | date0 = internal.startOfDay(eventInstanceStart); | ||
1541 | } | ||
1542 | else { | ||
1543 | // Moving from allDate->timed | ||
1544 | // Doesn't matter where on the event the drag began, mutate the event's start-date to date1 | ||
1545 | date0 = eventInstanceStart; | ||
1546 | } | ||
1547 | } | ||
1548 | let delta = internal.diffDates(date0, date1, hit0.context.dateEnv, hit0.componentId === hit1.componentId ? | ||
1549 | hit0.largeUnit : | ||
1550 | null); | ||
1551 | if (delta.milliseconds) { // has hours/minutes/seconds | ||
1552 | standardProps.allDay = false; | ||
1553 | } | ||
1554 | let mutation = { | ||
1555 | datesDelta: delta, | ||
1556 | standardProps, | ||
1557 | }; | ||
1558 | for (let massager of massagers) { | ||
1559 | massager(mutation, hit0, hit1); | ||
1560 | } | ||
1561 | return mutation; | ||
1562 | } | ||
1563 | function getComponentTouchDelay(component) { | ||
1564 | let { options } = component.context; | ||
1565 | let delay = options.eventLongPressDelay; | ||
1566 | if (delay == null) { | ||
1567 | delay = options.longPressDelay; | ||
1568 | } | ||
1569 | return delay; | ||
1570 | } | ||
1571 | |||
1572 | class EventResizing extends internal.Interaction { | ||
1573 | constructor(settings) { | ||
1574 | super(settings); | ||
1575 | // internal state | ||
1576 | this.draggingSegEl = null; | ||
1577 | this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg? | ||
1578 | this.eventRange = null; | ||
1579 | this.relevantEvents = null; | ||
1580 | this.validMutation = null; | ||
1581 | this.mutatedRelevantEvents = null; | ||
1582 | this.handlePointerDown = (ev) => { | ||
1583 | let { component } = this; | ||
1584 | let segEl = this.querySegEl(ev); | ||
1585 | let seg = internal.getElSeg(segEl); | ||
1586 | let eventRange = this.eventRange = seg.eventRange; | ||
1587 | this.dragging.minDistance = component.context.options.eventDragMinDistance; | ||
1588 | // if touch, need to be working with a selected event | ||
1589 | this.dragging.setIgnoreMove(!this.component.isValidSegDownEl(ev.origEvent.target) || | ||
1590 | (ev.isTouch && this.component.props.eventSelection !== eventRange.instance.instanceId)); | ||
1591 | }; | ||
1592 | this.handleDragStart = (ev) => { | ||
1593 | let { context } = this.component; | ||
1594 | let eventRange = this.eventRange; | ||
1595 | this.relevantEvents = internal.getRelevantEvents(context.getCurrentData().eventStore, this.eventRange.instance.instanceId); | ||
1596 | let segEl = this.querySegEl(ev); | ||
1597 | this.draggingSegEl = segEl; | ||
1598 | this.draggingSeg = internal.getElSeg(segEl); | ||
1599 | context.calendarApi.unselect(); | ||
1600 | context.emitter.trigger('eventResizeStart', { | ||
1601 | el: segEl, | ||
1602 | event: new internal.EventImpl(context, eventRange.def, eventRange.instance), | ||
1603 | jsEvent: ev.origEvent, | ||
1604 | view: context.viewApi, | ||
1605 | }); | ||
1606 | }; | ||
1607 | this.handleHitUpdate = (hit, isFinal, ev) => { | ||
1608 | let { context } = this.component; | ||
1609 | let relevantEvents = this.relevantEvents; | ||
1610 | let initialHit = this.hitDragging.initialHit; | ||
1611 | let eventInstance = this.eventRange.instance; | ||
1612 | let mutation = null; | ||
1613 | let mutatedRelevantEvents = null; | ||
1614 | let isInvalid = false; | ||
1615 | let interaction = { | ||
1616 | affectedEvents: relevantEvents, | ||
1617 | mutatedEvents: internal.createEmptyEventStore(), | ||
1618 | isEvent: true, | ||
1619 | }; | ||
1620 | if (hit) { | ||
1621 | let disallowed = hit.componentId === initialHit.componentId | ||
1622 | && this.isHitComboAllowed | ||
1623 | && !this.isHitComboAllowed(initialHit, hit); | ||
1624 | if (!disallowed) { | ||
1625 | mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-event-resizer-start'), eventInstance.range); | ||
1626 | } | ||
1627 | } | ||
1628 | if (mutation) { | ||
1629 | mutatedRelevantEvents = internal.applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context); | ||
1630 | interaction.mutatedEvents = mutatedRelevantEvents; | ||
1631 | if (!internal.isInteractionValid(interaction, hit.dateProfile, context)) { | ||
1632 | isInvalid = true; | ||
1633 | mutation = null; | ||
1634 | mutatedRelevantEvents = null; | ||
1635 | interaction.mutatedEvents = null; | ||
1636 | } | ||
1637 | } | ||
1638 | if (mutatedRelevantEvents) { | ||
1639 | context.dispatch({ | ||
1640 | type: 'SET_EVENT_RESIZE', | ||
1641 | state: interaction, | ||
1642 | }); | ||
1643 | } | ||
1644 | else { | ||
1645 | context.dispatch({ type: 'UNSET_EVENT_RESIZE' }); | ||
1646 | } | ||
1647 | if (!isInvalid) { | ||
1648 | internal.enableCursor(); | ||
1649 | } | ||
1650 | else { | ||
1651 | internal.disableCursor(); | ||
1652 | } | ||
1653 | if (!isFinal) { | ||
1654 | if (mutation && isHitsEqual(initialHit, hit)) { | ||
1655 | mutation = null; | ||
1656 | } | ||
1657 | this.validMutation = mutation; | ||
1658 | this.mutatedRelevantEvents = mutatedRelevantEvents; | ||
1659 | } | ||
1660 | }; | ||
1661 | this.handleDragEnd = (ev) => { | ||
1662 | let { context } = this.component; | ||
1663 | let eventDef = this.eventRange.def; | ||
1664 | let eventInstance = this.eventRange.instance; | ||
1665 | let eventApi = new internal.EventImpl(context, eventDef, eventInstance); | ||
1666 | let relevantEvents = this.relevantEvents; | ||
1667 | let mutatedRelevantEvents = this.mutatedRelevantEvents; | ||
1668 | context.emitter.trigger('eventResizeStop', { | ||
1669 | el: this.draggingSegEl, | ||
1670 | event: eventApi, | ||
1671 | jsEvent: ev.origEvent, | ||
1672 | view: context.viewApi, | ||
1673 | }); | ||
1674 | if (this.validMutation) { | ||
1675 | let updatedEventApi = new internal.EventImpl(context, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null); | ||
1676 | context.dispatch({ | ||
1677 | type: 'MERGE_EVENTS', | ||
1678 | eventStore: mutatedRelevantEvents, | ||
1679 | }); | ||
1680 | let eventChangeArg = { | ||
1681 | oldEvent: eventApi, | ||
1682 | event: updatedEventApi, | ||
1683 | relatedEvents: internal.buildEventApis(mutatedRelevantEvents, context, eventInstance), | ||
1684 | revert() { | ||
1685 | context.dispatch({ | ||
1686 | type: 'MERGE_EVENTS', | ||
1687 | eventStore: relevantEvents, // the pre-change events | ||
1688 | }); | ||
1689 | }, | ||
1690 | }; | ||
1691 | context.emitter.trigger('eventResize', Object.assign(Object.assign({}, eventChangeArg), { el: this.draggingSegEl, startDelta: this.validMutation.startDelta || internal.createDuration(0), endDelta: this.validMutation.endDelta || internal.createDuration(0), jsEvent: ev.origEvent, view: context.viewApi })); | ||
1692 | context.emitter.trigger('eventChange', eventChangeArg); | ||
1693 | } | ||
1694 | else { | ||
1695 | context.emitter.trigger('_noEventResize'); | ||
1696 | } | ||
1697 | // reset all internal state | ||
1698 | this.draggingSeg = null; | ||
1699 | this.relevantEvents = null; | ||
1700 | this.validMutation = null; | ||
1701 | // okay to keep eventInstance around. useful to set it in handlePointerDown | ||
1702 | }; | ||
1703 | let { component } = settings; | ||
1704 | let dragging = this.dragging = new FeaturefulElementDragging(settings.el); | ||
1705 | dragging.pointer.selector = '.fc-event-resizer'; | ||
1706 | dragging.touchScrollAllowed = false; | ||
1707 | dragging.autoScroller.isEnabled = component.context.options.dragScroll; | ||
1708 | let hitDragging = this.hitDragging = new HitDragging(this.dragging, internal.interactionSettingsToStore(settings)); | ||
1709 | hitDragging.emitter.on('pointerdown', this.handlePointerDown); | ||
1710 | hitDragging.emitter.on('dragstart', this.handleDragStart); | ||
1711 | hitDragging.emitter.on('hitupdate', this.handleHitUpdate); | ||
1712 | hitDragging.emitter.on('dragend', this.handleDragEnd); | ||
1713 | } | ||
1714 | destroy() { | ||
1715 | this.dragging.destroy(); | ||
1716 | } | ||
1717 | querySegEl(ev) { | ||
1718 | return internal.elementClosest(ev.subjectEl, '.fc-event'); | ||
1719 | } | ||
1720 | } | ||
1721 | function computeMutation(hit0, hit1, isFromStart, instanceRange) { | ||
1722 | let dateEnv = hit0.context.dateEnv; | ||
1723 | let date0 = hit0.dateSpan.range.start; | ||
1724 | let date1 = hit1.dateSpan.range.start; | ||
1725 | let delta = internal.diffDates(date0, date1, dateEnv, hit0.largeUnit); | ||
1726 | if (isFromStart) { | ||
1727 | if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) { | ||
1728 | return { startDelta: delta }; | ||
1729 | } | ||
1730 | } | ||
1731 | else if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) { | ||
1732 | return { endDelta: delta }; | ||
1733 | } | ||
1734 | return null; | ||
1735 | } | ||
1736 | |||
1737 | class UnselectAuto { | ||
1738 | constructor(context) { | ||
1739 | this.context = context; | ||
1740 | this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system | ||
1741 | this.matchesCancel = false; | ||
1742 | this.matchesEvent = false; | ||
1743 | this.onSelect = (selectInfo) => { | ||
1744 | if (selectInfo.jsEvent) { | ||
1745 | this.isRecentPointerDateSelect = true; | ||
1746 | } | ||
1747 | }; | ||
1748 | this.onDocumentPointerDown = (pev) => { | ||
1749 | let unselectCancel = this.context.options.unselectCancel; | ||
1750 | let downEl = internal.getEventTargetViaRoot(pev.origEvent); | ||
1751 | this.matchesCancel = !!internal.elementClosest(downEl, unselectCancel); | ||
1752 | this.matchesEvent = !!internal.elementClosest(downEl, EventDragging.SELECTOR); // interaction started on an event? | ||
1753 | }; | ||
1754 | this.onDocumentPointerUp = (pev) => { | ||
1755 | let { context } = this; | ||
1756 | let { documentPointer } = this; | ||
1757 | let calendarState = context.getCurrentData(); | ||
1758 | // touch-scrolling should never unfocus any type of selection | ||
1759 | if (!documentPointer.wasTouchScroll) { | ||
1760 | if (calendarState.dateSelection && // an existing date selection? | ||
1761 | !this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp? | ||
1762 | ) { | ||
1763 | let unselectAuto = context.options.unselectAuto; | ||
1764 | if (unselectAuto && (!unselectAuto || !this.matchesCancel)) { | ||
1765 | context.calendarApi.unselect(pev); | ||
1766 | } | ||
1767 | } | ||
1768 | if (calendarState.eventSelection && // an existing event selected? | ||
1769 | !this.matchesEvent // interaction DIDN'T start on an event | ||
1770 | ) { | ||
1771 | context.dispatch({ type: 'UNSELECT_EVENT' }); | ||
1772 | } | ||
1773 | } | ||
1774 | this.isRecentPointerDateSelect = false; | ||
1775 | }; | ||
1776 | let documentPointer = this.documentPointer = new PointerDragging(document); | ||
1777 | documentPointer.shouldIgnoreMove = true; | ||
1778 | documentPointer.shouldWatchScroll = false; | ||
1779 | documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown); | ||
1780 | documentPointer.emitter.on('pointerup', this.onDocumentPointerUp); | ||
1781 | /* | ||
1782 | TODO: better way to know about whether there was a selection with the pointer | ||
1783 | */ | ||
1784 | context.emitter.on('select', this.onSelect); | ||
1785 | } | ||
1786 | destroy() { | ||
1787 | this.context.emitter.off('select', this.onSelect); | ||
1788 | this.documentPointer.destroy(); | ||
1789 | } | ||
1790 | } | ||
1791 | |||
1792 | const OPTION_REFINERS = { | ||
1793 | fixedMirrorParent: internal.identity, | ||
1794 | }; | ||
1795 | const LISTENER_REFINERS = { | ||
1796 | dateClick: internal.identity, | ||
1797 | eventDragStart: internal.identity, | ||
1798 | eventDragStop: internal.identity, | ||
1799 | eventDrop: internal.identity, | ||
1800 | eventResizeStart: internal.identity, | ||
1801 | eventResizeStop: internal.identity, | ||
1802 | eventResize: internal.identity, | ||
1803 | drop: internal.identity, | ||
1804 | eventReceive: internal.identity, | ||
1805 | eventLeave: internal.identity, | ||
1806 | }; | ||
1807 | |||
1808 | /* | ||
1809 | Given an already instantiated draggable object for one-or-more elements, | ||
1810 | Interprets any dragging as an attempt to drag an events that lives outside | ||
1811 | of a calendar onto a calendar. | ||
1812 | */ | ||
1813 | class ExternalElementDragging { | ||
1814 | constructor(dragging, suppliedDragMeta) { | ||
1815 | this.receivingContext = null; | ||
1816 | this.droppableEvent = null; // will exist for all drags, even if create:false | ||
1817 | this.suppliedDragMeta = null; | ||
1818 | this.dragMeta = null; | ||
1819 | this.handleDragStart = (ev) => { | ||
1820 | this.dragMeta = this.buildDragMeta(ev.subjectEl); | ||
1821 | }; | ||
1822 | this.handleHitUpdate = (hit, isFinal, ev) => { | ||
1823 | let { dragging } = this.hitDragging; | ||
1824 | let receivingContext = null; | ||
1825 | let droppableEvent = null; | ||
1826 | let isInvalid = false; | ||
1827 | let interaction = { | ||
1828 | affectedEvents: internal.createEmptyEventStore(), | ||
1829 | mutatedEvents: internal.createEmptyEventStore(), | ||
1830 | isEvent: this.dragMeta.create, | ||
1831 | }; | ||
1832 | if (hit) { | ||
1833 | receivingContext = hit.context; | ||
1834 | if (this.canDropElOnCalendar(ev.subjectEl, receivingContext)) { | ||
1835 | droppableEvent = computeEventForDateSpan(hit.dateSpan, this.dragMeta, receivingContext); | ||
1836 | interaction.mutatedEvents = internal.eventTupleToStore(droppableEvent); | ||
1837 | isInvalid = !internal.isInteractionValid(interaction, hit.dateProfile, receivingContext); | ||
1838 | if (isInvalid) { | ||
1839 | interaction.mutatedEvents = internal.createEmptyEventStore(); | ||
1840 | droppableEvent = null; | ||
1841 | } | ||
1842 | } | ||
1843 | } | ||
1844 | this.displayDrag(receivingContext, interaction); | ||
1845 | // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?) | ||
1846 | // TODO: wish we could somehow wait for dispatch to guarantee render | ||
1847 | dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-event-mirror')); | ||
1848 | if (!isInvalid) { | ||
1849 | internal.enableCursor(); | ||
1850 | } | ||
1851 | else { | ||
1852 | internal.disableCursor(); | ||
1853 | } | ||
1854 | if (!isFinal) { | ||
1855 | dragging.setMirrorNeedsRevert(!droppableEvent); | ||
1856 | this.receivingContext = receivingContext; | ||
1857 | this.droppableEvent = droppableEvent; | ||
1858 | } | ||
1859 | }; | ||
1860 | this.handleDragEnd = (pev) => { | ||
1861 | let { receivingContext, droppableEvent } = this; | ||
1862 | this.clearDrag(); | ||
1863 | if (receivingContext && droppableEvent) { | ||
1864 | let finalHit = this.hitDragging.finalHit; | ||
1865 | let finalView = finalHit.context.viewApi; | ||
1866 | let dragMeta = this.dragMeta; | ||
1867 | receivingContext.emitter.trigger('drop', Object.assign(Object.assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView })); | ||
1868 | if (dragMeta.create) { | ||
1869 | let addingEvents = internal.eventTupleToStore(droppableEvent); | ||
1870 | receivingContext.dispatch({ | ||
1871 | type: 'MERGE_EVENTS', | ||
1872 | eventStore: addingEvents, | ||
1873 | }); | ||
1874 | if (pev.isTouch) { | ||
1875 | receivingContext.dispatch({ | ||
1876 | type: 'SELECT_EVENT', | ||
1877 | eventInstanceId: droppableEvent.instance.instanceId, | ||
1878 | }); | ||
1879 | } | ||
1880 | // signal that an external event landed | ||
1881 | receivingContext.emitter.trigger('eventReceive', { | ||
1882 | event: new internal.EventImpl(receivingContext, droppableEvent.def, droppableEvent.instance), | ||
1883 | relatedEvents: [], | ||
1884 | revert() { | ||
1885 | receivingContext.dispatch({ | ||
1886 | type: 'REMOVE_EVENTS', | ||
1887 | eventStore: addingEvents, | ||
1888 | }); | ||
1889 | }, | ||
1890 | draggedEl: pev.subjectEl, | ||
1891 | view: finalView, | ||
1892 | }); | ||
1893 | } | ||
1894 | } | ||
1895 | this.receivingContext = null; | ||
1896 | this.droppableEvent = null; | ||
1897 | }; | ||
1898 | let hitDragging = this.hitDragging = new HitDragging(dragging, internal.interactionSettingsStore); | ||
1899 | hitDragging.requireInitial = false; // will start outside of a component | ||
1900 | hitDragging.emitter.on('dragstart', this.handleDragStart); | ||
1901 | hitDragging.emitter.on('hitupdate', this.handleHitUpdate); | ||
1902 | hitDragging.emitter.on('dragend', this.handleDragEnd); | ||
1903 | this.suppliedDragMeta = suppliedDragMeta; | ||
1904 | } | ||
1905 | buildDragMeta(subjectEl) { | ||
1906 | if (typeof this.suppliedDragMeta === 'object') { | ||
1907 | return internal.parseDragMeta(this.suppliedDragMeta); | ||
1908 | } | ||
1909 | if (typeof this.suppliedDragMeta === 'function') { | ||
1910 | return internal.parseDragMeta(this.suppliedDragMeta(subjectEl)); | ||
1911 | } | ||
1912 | return getDragMetaFromEl(subjectEl); | ||
1913 | } | ||
1914 | displayDrag(nextContext, state) { | ||
1915 | let prevContext = this.receivingContext; | ||
1916 | if (prevContext && prevContext !== nextContext) { | ||
1917 | prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); | ||
1918 | } | ||
1919 | if (nextContext) { | ||
1920 | nextContext.dispatch({ type: 'SET_EVENT_DRAG', state }); | ||
1921 | } | ||
1922 | } | ||
1923 | clearDrag() { | ||
1924 | if (this.receivingContext) { | ||
1925 | this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' }); | ||
1926 | } | ||
1927 | } | ||
1928 | canDropElOnCalendar(el, receivingContext) { | ||
1929 | let dropAccept = receivingContext.options.dropAccept; | ||
1930 | if (typeof dropAccept === 'function') { | ||
1931 | return dropAccept.call(receivingContext.calendarApi, el); | ||
1932 | } | ||
1933 | if (typeof dropAccept === 'string' && dropAccept) { | ||
1934 | return Boolean(internal.elementMatches(el, dropAccept)); | ||
1935 | } | ||
1936 | return true; | ||
1937 | } | ||
1938 | } | ||
1939 | // Utils for computing event store from the DragMeta | ||
1940 | // ---------------------------------------------------------------------------------------------------- | ||
1941 | function computeEventForDateSpan(dateSpan, dragMeta, context) { | ||
1942 | let defProps = Object.assign({}, dragMeta.leftoverProps); | ||
1943 | for (let transform of context.pluginHooks.externalDefTransforms) { | ||
1944 | Object.assign(defProps, transform(dateSpan, dragMeta)); | ||
1945 | } | ||
1946 | let { refined, extra } = internal.refineEventDef(defProps, context); | ||
1947 | let def = internal.parseEventDef(refined, extra, dragMeta.sourceId, dateSpan.allDay, context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd | ||
1948 | context); | ||
1949 | let start = dateSpan.range.start; | ||
1950 | // only rely on time info if drop zone is all-day, | ||
1951 | // otherwise, we already know the time | ||
1952 | if (dateSpan.allDay && dragMeta.startTime) { | ||
1953 | start = context.dateEnv.add(start, dragMeta.startTime); | ||
1954 | } | ||
1955 | let end = dragMeta.duration ? | ||
1956 | context.dateEnv.add(start, dragMeta.duration) : | ||
1957 | internal.getDefaultEventEnd(dateSpan.allDay, start, context); | ||
1958 | let instance = internal.createEventInstance(def.defId, { start, end }); | ||
1959 | return { def, instance }; | ||
1960 | } | ||
1961 | // Utils for extracting data from element | ||
1962 | // ---------------------------------------------------------------------------------------------------- | ||
1963 | function getDragMetaFromEl(el) { | ||
1964 | let str = getEmbeddedElData(el, 'event'); | ||
1965 | let obj = str ? | ||
1966 | JSON.parse(str) : | ||
1967 | { create: false }; // if no embedded data, assume no event creation | ||
1968 | return internal.parseDragMeta(obj); | ||
1969 | } | ||
1970 | internal.config.dataAttrPrefix = ''; | ||
1971 | function getEmbeddedElData(el, name) { | ||
1972 | let prefix = internal.config.dataAttrPrefix; | ||
1973 | let prefixedName = (prefix ? prefix + '-' : '') + name; | ||
1974 | return el.getAttribute('data-' + prefixedName) || ''; | ||
1975 | } | ||
1976 | |||
1977 | /* | ||
1978 | Makes an element (that is *external* to any calendar) draggable. | ||
1979 | Can pass in data that determines how an event will be created when dropped onto a calendar. | ||
1980 | Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system. | ||
1981 | */ | ||
1982 | class ExternalDraggable { | ||
1983 | constructor(el, settings = {}) { | ||
1984 | this.handlePointerDown = (ev) => { | ||
1985 | let { dragging } = this; | ||
1986 | let { minDistance, longPressDelay } = this.settings; | ||
1987 | dragging.minDistance = | ||
1988 | minDistance != null ? | ||
1989 | minDistance : | ||
1990 | (ev.isTouch ? 0 : internal.BASE_OPTION_DEFAULTS.eventDragMinDistance); | ||
1991 | dragging.delay = | ||
1992 | ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv | ||
1993 | (longPressDelay != null ? longPressDelay : internal.BASE_OPTION_DEFAULTS.longPressDelay) : | ||
1994 | 0; | ||
1995 | }; | ||
1996 | this.handleDragStart = (ev) => { | ||
1997 | if (ev.isTouch && | ||
1998 | this.dragging.delay && | ||
1999 | ev.subjectEl.classList.contains('fc-event')) { | ||
2000 | this.dragging.mirror.getMirrorEl().classList.add('fc-event-selected'); | ||
2001 | } | ||
2002 | }; | ||
2003 | this.settings = settings; | ||
2004 | let dragging = this.dragging = new FeaturefulElementDragging(el); | ||
2005 | dragging.touchScrollAllowed = false; | ||
2006 | if (settings.itemSelector != null) { | ||
2007 | dragging.pointer.selector = settings.itemSelector; | ||
2008 | } | ||
2009 | if (settings.appendTo != null) { | ||
2010 | dragging.mirror.parentNode = settings.appendTo; // TODO: write tests | ||
2011 | } | ||
2012 | dragging.emitter.on('pointerdown', this.handlePointerDown); | ||
2013 | dragging.emitter.on('dragstart', this.handleDragStart); | ||
2014 | new ExternalElementDragging(dragging, settings.eventData); // eslint-disable-line no-new | ||
2015 | } | ||
2016 | destroy() { | ||
2017 | this.dragging.destroy(); | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | /* | ||
2022 | Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements. | ||
2023 | The third-party system is responsible for drawing the visuals effects of the drag. | ||
2024 | This class simply monitors for pointer movements and fires events. | ||
2025 | It also has the ability to hide the moving element (the "mirror") during the drag. | ||
2026 | */ | ||
2027 | class InferredElementDragging extends internal.ElementDragging { | ||
2028 | constructor(containerEl) { | ||
2029 | super(containerEl); | ||
2030 | this.shouldIgnoreMove = false; | ||
2031 | this.mirrorSelector = ''; | ||
2032 | this.currentMirrorEl = null; | ||
2033 | this.handlePointerDown = (ev) => { | ||
2034 | this.emitter.trigger('pointerdown', ev); | ||
2035 | if (!this.shouldIgnoreMove) { | ||
2036 | // fire dragstart right away. does not support delay or min-distance | ||
2037 | this.emitter.trigger('dragstart', ev); | ||
2038 | } | ||
2039 | }; | ||
2040 | this.handlePointerMove = (ev) => { | ||
2041 | if (!this.shouldIgnoreMove) { | ||
2042 | this.emitter.trigger('dragmove', ev); | ||
2043 | } | ||
2044 | }; | ||
2045 | this.handlePointerUp = (ev) => { | ||
2046 | this.emitter.trigger('pointerup', ev); | ||
2047 | if (!this.shouldIgnoreMove) { | ||
2048 | // fire dragend right away. does not support a revert animation | ||
2049 | this.emitter.trigger('dragend', ev); | ||
2050 | } | ||
2051 | }; | ||
2052 | let pointer = this.pointer = new PointerDragging(containerEl); | ||
2053 | pointer.emitter.on('pointerdown', this.handlePointerDown); | ||
2054 | pointer.emitter.on('pointermove', this.handlePointerMove); | ||
2055 | pointer.emitter.on('pointerup', this.handlePointerUp); | ||
2056 | } | ||
2057 | destroy() { | ||
2058 | this.pointer.destroy(); | ||
2059 | } | ||
2060 | setIgnoreMove(bool) { | ||
2061 | this.shouldIgnoreMove = bool; | ||
2062 | } | ||
2063 | setMirrorIsVisible(bool) { | ||
2064 | if (bool) { | ||
2065 | // restore a previously hidden element. | ||
2066 | // use the reference in case the selector class has already been removed. | ||
2067 | if (this.currentMirrorEl) { | ||
2068 | this.currentMirrorEl.style.visibility = ''; | ||
2069 | this.currentMirrorEl = null; | ||
2070 | } | ||
2071 | } | ||
2072 | else { | ||
2073 | let mirrorEl = this.mirrorSelector | ||
2074 | // TODO: somehow query FullCalendars WITHIN shadow-roots | ||
2075 | ? document.querySelector(this.mirrorSelector) | ||
2076 | : null; | ||
2077 | if (mirrorEl) { | ||
2078 | this.currentMirrorEl = mirrorEl; | ||
2079 | mirrorEl.style.visibility = 'hidden'; | ||
2080 | } | ||
2081 | } | ||
2082 | } | ||
2083 | } | ||
2084 | |||
2085 | /* | ||
2086 | Bridges third-party drag-n-drop systems with FullCalendar. | ||
2087 | Must be instantiated and destroyed by caller. | ||
2088 | */ | ||
2089 | class ThirdPartyDraggable { | ||
2090 | constructor(containerOrSettings, settings) { | ||
2091 | let containerEl = document; | ||
2092 | if ( | ||
2093 | // wish we could just test instanceof EventTarget, but doesn't work in IE11 | ||
2094 | containerOrSettings === document || | ||
2095 | containerOrSettings instanceof Element) { | ||
2096 | containerEl = containerOrSettings; | ||
2097 | settings = settings || {}; | ||
2098 | } | ||
2099 | else { | ||
2100 | settings = (containerOrSettings || {}); | ||
2101 | } | ||
2102 | let dragging = this.dragging = new InferredElementDragging(containerEl); | ||
2103 | if (typeof settings.itemSelector === 'string') { | ||
2104 | dragging.pointer.selector = settings.itemSelector; | ||
2105 | } | ||
2106 | else if (containerEl === document) { | ||
2107 | dragging.pointer.selector = '[data-event]'; | ||
2108 | } | ||
2109 | if (typeof settings.mirrorSelector === 'string') { | ||
2110 | dragging.mirrorSelector = settings.mirrorSelector; | ||
2111 | } | ||
2112 | let externalDragging = new ExternalElementDragging(dragging, settings.eventData); | ||
2113 | // The hit-detection system requires that the dnd-mirror-element be pointer-events:none, | ||
2114 | // but this can't be guaranteed for third-party draggables, so disable | ||
2115 | externalDragging.hitDragging.disablePointCheck = true; | ||
2116 | } | ||
2117 | destroy() { | ||
2118 | this.dragging.destroy(); | ||
2119 | } | ||
2120 | } | ||
2121 | |||
2122 | var plugin = core.createPlugin({ | ||
2123 | name: '@fullcalendar/interaction', | ||
2124 | componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing], | ||
2125 | calendarInteractions: [UnselectAuto], | ||
2126 | elementDraggingImpl: FeaturefulElementDragging, | ||
2127 | optionRefiners: OPTION_REFINERS, | ||
2128 | listenerRefiners: LISTENER_REFINERS, | ||
2129 | }); | ||
2130 | |||
2131 | core.globalPlugins.push(plugin); | ||
2132 | |||
2133 | exports.Draggable = ExternalDraggable; | ||
2134 | exports.ThirdPartyDraggable = ThirdPartyDraggable; | ||
2135 | exports["default"] = plugin; | ||
2136 | |||
2137 | Object.defineProperty(exports, '__esModule', { value: true }); | ||
2138 | |||
2139 | return exports; | ||
2140 | |||
2141 | })({}, FullCalendar, FullCalendar.Internal); | ||
diff --git a/public/js/fullcalendar/packages/interaction/index.global.min.js b/public/js/fullcalendar/packages/interaction/index.global.min.js new file mode 100644 index 0000000..1522b55 --- /dev/null +++ b/public/js/fullcalendar/packages/interaction/index.global.min.js | |||
@@ -0,0 +1,6 @@ | |||
1 | /*! | ||
2 | FullCalendar Interaction Plugin v6.1.17 | ||
3 | Docs & License: https://fullcalendar.io/docs/editable | ||
4 | (c) 2024 Adam Shaw | ||
5 | */ | ||
6 | FullCalendar.Interaction=function(t,e,i){"use strict";i.config.touchMouseIgnoreWait=500;let n=0,s=0,r=!1;class o{constructor(t){this.subjectEl=null,this.selector="",this.handleSelector="",this.shouldIgnoreMove=!1,this.shouldWatchScroll=!0,this.isDragging=!1,this.isTouchDragging=!1,this.wasTouchScroll=!1,this.handleMouseDown=t=>{if(!this.shouldIgnoreMouse()&&function(t){return 0===t.button&&!t.ctrlKey}(t)&&this.tryStart(t)){let e=this.createEventFromMouse(t,!0);this.emitter.trigger("pointerdown",e),this.initScrollWatch(e),this.shouldIgnoreMove||document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp)}},this.handleMouseMove=t=>{let e=this.createEventFromMouse(t);this.recordCoords(e),this.emitter.trigger("pointermove",e)},this.handleMouseUp=t=>{document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp),this.emitter.trigger("pointerup",this.createEventFromMouse(t)),this.cleanup()},this.handleTouchStart=t=>{if(this.tryStart(t)){this.isTouchDragging=!0;let e=this.createEventFromTouch(t,!0);this.emitter.trigger("pointerdown",e),this.initScrollWatch(e);let i=t.target;this.shouldIgnoreMove||i.addEventListener("touchmove",this.handleTouchMove),i.addEventListener("touchend",this.handleTouchEnd),i.addEventListener("touchcancel",this.handleTouchEnd),window.addEventListener("scroll",this.handleTouchScroll,!0)}},this.handleTouchMove=t=>{let e=this.createEventFromTouch(t);this.recordCoords(e),this.emitter.trigger("pointermove",e)},this.handleTouchEnd=t=>{if(this.isDragging){let e=t.target;e.removeEventListener("touchmove",this.handleTouchMove),e.removeEventListener("touchend",this.handleTouchEnd),e.removeEventListener("touchcancel",this.handleTouchEnd),window.removeEventListener("scroll",this.handleTouchScroll,!0),this.emitter.trigger("pointerup",this.createEventFromTouch(t)),this.cleanup(),this.isTouchDragging=!1,n+=1,setTimeout(()=>{n-=1},i.config.touchMouseIgnoreWait)}},this.handleTouchScroll=()=>{this.wasTouchScroll=!0},this.handleScroll=t=>{if(!this.shouldIgnoreMove){let e=window.scrollX-this.prevScrollX+this.prevPageX,i=window.scrollY-this.prevScrollY+this.prevPageY;this.emitter.trigger("pointermove",{origEvent:t,isTouch:this.isTouchDragging,subjectEl:this.subjectEl,pageX:e,pageY:i,deltaX:e-this.origPageX,deltaY:i-this.origPageY})}},this.containerEl=t,this.emitter=new i.Emitter,t.addEventListener("mousedown",this.handleMouseDown),t.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),s+=1,1===s&&window.addEventListener("touchmove",l,{passive:!1})}destroy(){this.containerEl.removeEventListener("mousedown",this.handleMouseDown),this.containerEl.removeEventListener("touchstart",this.handleTouchStart,{passive:!0}),s-=1,s||window.removeEventListener("touchmove",l,{passive:!1})}tryStart(t){let e=this.querySubjectEl(t),n=t.target;return!(!e||this.handleSelector&&!i.elementClosest(n,this.handleSelector))&&(this.subjectEl=e,this.isDragging=!0,this.wasTouchScroll=!1,!0)}cleanup(){r=!1,this.isDragging=!1,this.subjectEl=null,this.destroyScrollWatch()}querySubjectEl(t){return this.selector?i.elementClosest(t.target,this.selector):this.containerEl}shouldIgnoreMouse(){return n||this.isTouchDragging}cancelTouchScroll(){this.isDragging&&(r=!0)}initScrollWatch(t){this.shouldWatchScroll&&(this.recordCoords(t),window.addEventListener("scroll",this.handleScroll,!0))}recordCoords(t){this.shouldWatchScroll&&(this.prevPageX=t.pageX,this.prevPageY=t.pageY,this.prevScrollX=window.scrollX,this.prevScrollY=window.scrollY)}destroyScrollWatch(){this.shouldWatchScroll&&window.removeEventListener("scroll",this.handleScroll,!0)}createEventFromMouse(t,e){let i=0,n=0;return e?(this.origPageX=t.pageX,this.origPageY=t.pageY):(i=t.pageX-this.origPageX,n=t.pageY-this.origPageY),{origEvent:t,isTouch:!1,subjectEl:this.subjectEl,pageX:t.pageX,pageY:t.pageY,deltaX:i,deltaY:n}}createEventFromTouch(t,e){let i,n,s=t.touches,r=0,o=0;return s&&s.length?(i=s[0].pageX,n=s[0].pageY):(i=t.pageX,n=t.pageY),e?(this.origPageX=i,this.origPageY=n):(r=i-this.origPageX,o=n-this.origPageY),{origEvent:t,isTouch:!0,subjectEl:this.subjectEl,pageX:i,pageY:n,deltaX:r,deltaY:o}}}function l(t){r&&t.preventDefault()}class a{constructor(){this.isVisible=!1,this.sourceEl=null,this.mirrorEl=null,this.sourceElRect=null,this.parentNode=document.body,this.zIndex=9999,this.revertDuration=0}start(t,e,i){this.sourceEl=t,this.sourceElRect=this.sourceEl.getBoundingClientRect(),this.origScreenX=e-window.scrollX,this.origScreenY=i-window.scrollY,this.deltaX=0,this.deltaY=0,this.updateElPosition()}handleMove(t,e){this.deltaX=t-window.scrollX-this.origScreenX,this.deltaY=e-window.scrollY-this.origScreenY,this.updateElPosition()}setIsVisible(t){t?this.isVisible||(this.mirrorEl&&(this.mirrorEl.style.display=""),this.isVisible=t,this.updateElPosition()):this.isVisible&&(this.mirrorEl&&(this.mirrorEl.style.display="none"),this.isVisible=t)}stop(t,e){let i=()=>{this.cleanup(),e()};t&&this.mirrorEl&&this.isVisible&&this.revertDuration&&(this.deltaX||this.deltaY)?this.doRevertAnimation(i,this.revertDuration):setTimeout(i,0)}doRevertAnimation(t,e){let n=this.mirrorEl,s=this.sourceEl.getBoundingClientRect();n.style.transition="top "+e+"ms,left "+e+"ms",i.applyStyle(n,{left:s.left,top:s.top}),i.whenTransitionDone(n,()=>{n.style.transition="",t()})}cleanup(){this.mirrorEl&&(i.removeElement(this.mirrorEl),this.mirrorEl=null),this.sourceEl=null}updateElPosition(){this.sourceEl&&this.isVisible&&i.applyStyle(this.getMirrorEl(),{left:this.sourceElRect.left+this.deltaX,top:this.sourceElRect.top+this.deltaY})}getMirrorEl(){let t=this.sourceElRect,e=this.mirrorEl;return e||(e=this.mirrorEl=this.sourceEl.cloneNode(!0),e.style.userSelect="none",e.style.webkitUserSelect="none",e.style.pointerEvents="none",e.classList.add("fc-event-dragging"),i.applyStyle(e,{position:"fixed",zIndex:this.zIndex,visibility:"",boxSizing:"border-box",width:t.right-t.left,height:t.bottom-t.top,right:"auto",bottom:"auto",margin:0}),this.parentNode.appendChild(e)),e}}class h extends i.ScrollController{constructor(t,e){super(),this.handleScroll=()=>{this.scrollTop=this.scrollController.getScrollTop(),this.scrollLeft=this.scrollController.getScrollLeft(),this.handleScrollChange()},this.scrollController=t,this.doesListening=e,this.scrollTop=this.origScrollTop=t.getScrollTop(),this.scrollLeft=this.origScrollLeft=t.getScrollLeft(),this.scrollWidth=t.getScrollWidth(),this.scrollHeight=t.getScrollHeight(),this.clientWidth=t.getClientWidth(),this.clientHeight=t.getClientHeight(),this.clientRect=this.computeClientRect(),this.doesListening&&this.getEventTarget().addEventListener("scroll",this.handleScroll)}destroy(){this.doesListening&&this.getEventTarget().removeEventListener("scroll",this.handleScroll)}getScrollTop(){return this.scrollTop}getScrollLeft(){return this.scrollLeft}setScrollTop(t){this.scrollController.setScrollTop(t),this.doesListening||(this.scrollTop=Math.max(Math.min(t,this.getMaxScrollTop()),0),this.handleScrollChange())}setScrollLeft(t){this.scrollController.setScrollLeft(t),this.doesListening||(this.scrollLeft=Math.max(Math.min(t,this.getMaxScrollLeft()),0),this.handleScrollChange())}getClientWidth(){return this.clientWidth}getClientHeight(){return this.clientHeight}getScrollWidth(){return this.scrollWidth}getScrollHeight(){return this.scrollHeight}handleScrollChange(){}}class c extends h{constructor(t,e){super(new i.ElementScrollController(t),e)}getEventTarget(){return this.scrollController.el}computeClientRect(){return i.computeInnerRect(this.scrollController.el)}}class d extends h{constructor(t){super(new i.WindowScrollController,t)}getEventTarget(){return window}computeClientRect(){return{left:this.scrollLeft,right:this.scrollLeft+this.clientWidth,top:this.scrollTop,bottom:this.scrollTop+this.clientHeight}}handleScrollChange(){this.clientRect=this.computeClientRect()}}const g="function"==typeof performance?performance.now:Date.now;class u{constructor(){this.isEnabled=!0,this.scrollQuery=[window,".fc-scroller"],this.edgeThreshold=50,this.maxVelocity=300,this.pointerScreenX=null,this.pointerScreenY=null,this.isAnimating=!1,this.scrollCaches=null,this.everMovedUp=!1,this.everMovedDown=!1,this.everMovedLeft=!1,this.everMovedRight=!1,this.animate=()=>{if(this.isAnimating){let t=this.computeBestEdge(this.pointerScreenX+window.scrollX,this.pointerScreenY+window.scrollY);if(t){let e=g();this.handleSide(t,(e-this.msSinceRequest)/1e3),this.requestAnimation(e)}else this.isAnimating=!1}}}start(t,e,i){this.isEnabled&&(this.scrollCaches=this.buildCaches(i),this.pointerScreenX=null,this.pointerScreenY=null,this.everMovedUp=!1,this.everMovedDown=!1,this.everMovedLeft=!1,this.everMovedRight=!1,this.handleMove(t,e))}handleMove(t,e){if(this.isEnabled){let i=t-window.scrollX,n=e-window.scrollY,s=null===this.pointerScreenY?0:n-this.pointerScreenY,r=null===this.pointerScreenX?0:i-this.pointerScreenX;s<0?this.everMovedUp=!0:s>0&&(this.everMovedDown=!0),r<0?this.everMovedLeft=!0:r>0&&(this.everMovedRight=!0),this.pointerScreenX=i,this.pointerScreenY=n,this.isAnimating||(this.isAnimating=!0,this.requestAnimation(g()))}}stop(){if(this.isEnabled){this.isAnimating=!1;for(let t of this.scrollCaches)t.destroy();this.scrollCaches=null}}requestAnimation(t){this.msSinceRequest=t,requestAnimationFrame(this.animate)}handleSide(t,e){let{scrollCache:i}=t,{edgeThreshold:n}=this,s=n-t.distance,r=s*s/(n*n)*this.maxVelocity*e,o=1;switch(t.name){case"left":o=-1;case"right":i.setScrollLeft(i.getScrollLeft()+r*o);break;case"top":o=-1;case"bottom":i.setScrollTop(i.getScrollTop()+r*o)}}computeBestEdge(t,e){let{edgeThreshold:i}=this,n=null,s=this.scrollCaches||[];for(let r of s){let s=r.clientRect,o=t-s.left,l=s.right-t,a=e-s.top,h=s.bottom-e;o>=0&&l>=0&&a>=0&&h>=0&&(a<=i&&this.everMovedUp&&r.canScrollUp()&&(!n||n.distance>a)&&(n={scrollCache:r,name:"top",distance:a}),h<=i&&this.everMovedDown&&r.canScrollDown()&&(!n||n.distance>h)&&(n={scrollCache:r,name:"bottom",distance:h}),o<=i&&this.everMovedLeft&&r.canScrollLeft()&&(!n||n.distance>o)&&(n={scrollCache:r,name:"left",distance:o}),l<=i&&this.everMovedRight&&r.canScrollRight()&&(!n||n.distance>l)&&(n={scrollCache:r,name:"right",distance:l}))}return n}buildCaches(t){return this.queryScrollEls(t).map(t=>t===window?new d(!1):new c(t,!1))}queryScrollEls(t){let e=[];for(let i of this.scrollQuery)"object"==typeof i?e.push(i):e.push(...Array.prototype.slice.call(t.getRootNode().querySelectorAll(i)));return e}}class p extends i.ElementDragging{constructor(t,e){super(t),this.containerEl=t,this.delay=null,this.minDistance=0,this.touchScrollAllowed=!0,this.mirrorNeedsRevert=!1,this.isInteracting=!1,this.isDragging=!1,this.isDelayEnded=!1,this.isDistanceSurpassed=!1,this.delayTimeoutId=null,this.onPointerDown=t=>{this.isDragging||(this.isInteracting=!0,this.isDelayEnded=!1,this.isDistanceSurpassed=!1,i.preventSelection(document.body),i.preventContextMenu(document.body),t.isTouch||t.origEvent.preventDefault(),this.emitter.trigger("pointerdown",t),this.isInteracting&&!this.pointer.shouldIgnoreMove&&(this.mirror.setIsVisible(!1),this.mirror.start(t.subjectEl,t.pageX,t.pageY),this.startDelay(t),this.minDistance||this.handleDistanceSurpassed(t)))},this.onPointerMove=t=>{if(this.isInteracting){if(this.emitter.trigger("pointermove",t),!this.isDistanceSurpassed){let e,i=this.minDistance,{deltaX:n,deltaY:s}=t;e=n*n+s*s,e>=i*i&&this.handleDistanceSurpassed(t)}this.isDragging&&("scroll"!==t.origEvent.type&&(this.mirror.handleMove(t.pageX,t.pageY),this.autoScroller.handleMove(t.pageX,t.pageY)),this.emitter.trigger("dragmove",t))}},this.onPointerUp=t=>{this.isInteracting&&(this.isInteracting=!1,i.allowSelection(document.body),i.allowContextMenu(document.body),this.emitter.trigger("pointerup",t),this.isDragging&&(this.autoScroller.stop(),this.tryStopDrag(t)),this.delayTimeoutId&&(clearTimeout(this.delayTimeoutId),this.delayTimeoutId=null))};let n=this.pointer=new o(t);n.emitter.on("pointerdown",this.onPointerDown),n.emitter.on("pointermove",this.onPointerMove),n.emitter.on("pointerup",this.onPointerUp),e&&(n.selector=e),this.mirror=new a,this.autoScroller=new u}destroy(){this.pointer.destroy(),this.onPointerUp({})}startDelay(t){"number"==typeof this.delay?this.delayTimeoutId=setTimeout(()=>{this.delayTimeoutId=null,this.handleDelayEnd(t)},this.delay):this.handleDelayEnd(t)}handleDelayEnd(t){this.isDelayEnded=!0,this.tryStartDrag(t)}handleDistanceSurpassed(t){this.isDistanceSurpassed=!0,this.tryStartDrag(t)}tryStartDrag(t){this.isDelayEnded&&this.isDistanceSurpassed&&(this.pointer.wasTouchScroll&&!this.touchScrollAllowed||(this.isDragging=!0,this.mirrorNeedsRevert=!1,this.autoScroller.start(t.pageX,t.pageY,this.containerEl),this.emitter.trigger("dragstart",t),!1===this.touchScrollAllowed&&this.pointer.cancelTouchScroll()))}tryStopDrag(t){this.mirror.stop(this.mirrorNeedsRevert,this.stopDrag.bind(this,t))}stopDrag(t){this.isDragging=!1,this.emitter.trigger("dragend",t)}setIgnoreMove(t){this.pointer.shouldIgnoreMove=t}setMirrorIsVisible(t){this.mirror.setIsVisible(t)}setMirrorNeedsRevert(t){this.mirrorNeedsRevert=t}setAutoScrollEnabled(t){this.autoScroller.isEnabled=t}}class v{constructor(t){this.el=t,this.origRect=i.computeRect(t),this.scrollCaches=i.getClippingParents(t).map(t=>new c(t,!0))}destroy(){for(let t of this.scrollCaches)t.destroy()}computeLeft(){let t=this.origRect.left;for(let e of this.scrollCaches)t+=e.origScrollLeft-e.getScrollLeft();return t}computeTop(){let t=this.origRect.top;for(let e of this.scrollCaches)t+=e.origScrollTop-e.getScrollTop();return t}isWithinClipping(t,e){let n={left:t,top:e};for(let t of this.scrollCaches)if(!E(t.getEventTarget())&&!i.pointInsideRect(n,t.clientRect))return!1;return!0}}function E(t){let e=t.tagName;return"HTML"===e||"BODY"===e}class m{constructor(t,e){this.useSubjectCenter=!1,this.requireInitial=!0,this.disablePointCheck=!1,this.initialHit=null,this.movingHit=null,this.finalHit=null,this.handlePointerDown=t=>{let{dragging:e}=this;this.initialHit=null,this.movingHit=null,this.finalHit=null,this.prepareHits(),this.processFirstCoord(t),this.initialHit||!this.requireInitial?(e.setIgnoreMove(!1),this.emitter.trigger("pointerdown",t)):e.setIgnoreMove(!0)},this.handleDragStart=t=>{this.emitter.trigger("dragstart",t),this.handleMove(t,!0)},this.handleDragMove=t=>{this.emitter.trigger("dragmove",t),this.handleMove(t)},this.handlePointerUp=t=>{this.releaseHits(),this.emitter.trigger("pointerup",t)},this.handleDragEnd=t=>{this.movingHit&&this.emitter.trigger("hitupdate",null,!0,t),this.finalHit=this.movingHit,this.movingHit=null,this.emitter.trigger("dragend",t)},this.droppableStore=e,t.emitter.on("pointerdown",this.handlePointerDown),t.emitter.on("dragstart",this.handleDragStart),t.emitter.on("dragmove",this.handleDragMove),t.emitter.on("pointerup",this.handlePointerUp),t.emitter.on("dragend",this.handleDragEnd),this.dragging=t,this.emitter=new i.Emitter}processFirstCoord(t){let e,n={left:t.pageX,top:t.pageY},s=n,r=t.subjectEl;r instanceof HTMLElement&&(e=i.computeRect(r),s=i.constrainPoint(s,e));let o=this.initialHit=this.queryHitForOffset(s.left,s.top);if(o){if(this.useSubjectCenter&&e){let t=i.intersectRects(e,o.rect);t&&(s=i.getRectCenter(t))}this.coordAdjust=i.diffPoints(s,n)}else this.coordAdjust={left:0,top:0}}handleMove(t,e){let i=this.queryHitForOffset(t.pageX+this.coordAdjust.left,t.pageY+this.coordAdjust.top);!e&&S(this.movingHit,i)||(this.movingHit=i,this.emitter.trigger("hitupdate",i,!1,t))}prepareHits(){this.offsetTrackers=i.mapHash(this.droppableStore,t=>(t.component.prepareHits(),new v(t.el)))}releaseHits(){let{offsetTrackers:t}=this;for(let e in t)t[e].destroy();this.offsetTrackers={}}queryHitForOffset(t,e){let{droppableStore:n,offsetTrackers:s}=this,r=null;for(let o in n){let l=n[o].component,a=s[o];if(a&&a.isWithinClipping(t,e)){let n=a.computeLeft(),s=a.computeTop(),h=t-n,c=e-s,{origRect:d}=a,g=d.right-d.left,u=d.bottom-d.top;if(h>=0&&h<g&&c>=0&&c<u){let t=l.queryHit(h,c,g,u);t&&i.rangeContainsRange(t.dateProfile.activeRange,t.dateSpan.range)&&(this.disablePointCheck||a.el.contains(a.el.getRootNode().elementFromPoint(h+n-window.scrollX,c+s-window.scrollY)))&&(!r||t.layer>r.layer)&&(t.componentId=o,t.context=l.context,t.rect.left+=n,t.rect.right+=n,t.rect.top+=s,t.rect.bottom+=s,r=t)}}}return r}}function S(t,e){return!t&&!e||Boolean(t)===Boolean(e)&&i.isDateSpansEqual(t.dateSpan,e.dateSpan)}function D(t,e){let i={};for(let n of e.pluginHooks.datePointTransforms)Object.assign(i,n(t,e));var n,s;return Object.assign(i,(n=t,{date:(s=e.dateEnv).toDate(n.range.start),dateStr:s.formatIso(n.range.start,{omitTime:n.allDay}),allDay:n.allDay})),i}class f extends i.Interaction{constructor(t){super(t),this.handlePointerDown=t=>{let{dragging:e}=this,i=t.origEvent.target;e.setIgnoreMove(!this.component.isValidDateDownEl(i))},this.handleDragEnd=t=>{let{component:e}=this,{pointer:i}=this.dragging;if(!i.wasTouchScroll){let{initialHit:i,finalHit:n}=this.hitDragging;if(i&&n&&S(i,n)){let{context:n}=e,s=Object.assign(Object.assign({},D(i.dateSpan,n)),{dayEl:i.dayEl,jsEvent:t.origEvent,view:n.viewApi||n.calendarApi.view});n.emitter.trigger("dateClick",s)}}},this.dragging=new p(t.el),this.dragging.autoScroller.isEnabled=!1;let e=this.hitDragging=new m(this.dragging,i.interactionSettingsToStore(t));e.emitter.on("pointerdown",this.handlePointerDown),e.emitter.on("dragend",this.handleDragEnd)}destroy(){this.dragging.destroy()}}class y extends i.Interaction{constructor(t){super(t),this.dragSelection=null,this.handlePointerDown=t=>{let{component:e,dragging:i}=this,{options:n}=e.context,s=n.selectable&&e.isValidDateDownEl(t.origEvent.target);i.setIgnoreMove(!s),i.delay=t.isTouch?function(t){let{options:e}=t.context,i=e.selectLongPressDelay;null==i&&(i=e.longPressDelay);return i}(e):null},this.handleDragStart=t=>{this.component.context.calendarApi.unselect(t)},this.handleHitUpdate=(t,e)=>{let{context:n}=this.component,s=null,r=!1;if(t){let e=this.hitDragging.initialHit;t.componentId===e.componentId&&this.isHitComboAllowed&&!this.isHitComboAllowed(e,t)||(s=function(t,e,n){let s=t.dateSpan,r=e.dateSpan,o=[s.range.start,s.range.end,r.range.start,r.range.end];o.sort(i.compareNumbers);let l={};for(let i of n){let n=i(t,e);if(!1===n)return null;n&&Object.assign(l,n)}return l.range={start:o[0],end:o[3]},l.allDay=s.allDay,l}(e,t,n.pluginHooks.dateSelectionTransformers)),s&&i.isDateSelectionValid(s,t.dateProfile,n)||(r=!0,s=null)}s?n.dispatch({type:"SELECT_DATES",selection:s}):e||n.dispatch({type:"UNSELECT_DATES"}),r?i.disableCursor():i.enableCursor(),e||(this.dragSelection=s)},this.handlePointerUp=t=>{this.dragSelection&&(i.triggerDateSelect(this.dragSelection,t,this.component.context),this.dragSelection=null)};let{component:e}=t,{options:n}=e.context,s=this.dragging=new p(t.el);s.touchScrollAllowed=!1,s.minDistance=n.selectMinDistance||0,s.autoScroller.isEnabled=n.dragScroll;let r=this.hitDragging=new m(this.dragging,i.interactionSettingsToStore(t));r.emitter.on("pointerdown",this.handlePointerDown),r.emitter.on("dragstart",this.handleDragStart),r.emitter.on("hitupdate",this.handleHitUpdate),r.emitter.on("pointerup",this.handlePointerUp)}destroy(){this.dragging.destroy()}}class w extends i.Interaction{constructor(t){super(t),this.subjectEl=null,this.subjectSeg=null,this.isDragging=!1,this.eventRange=null,this.relevantEvents=null,this.receivingContext=null,this.validMutation=null,this.mutatedRelevantEvents=null,this.handlePointerDown=t=>{let e=t.origEvent.target,{component:n,dragging:s}=this,{mirror:r}=s,{options:o}=n.context,l=n.context;this.subjectEl=t.subjectEl;let a=this.subjectSeg=i.getElSeg(t.subjectEl),h=(this.eventRange=a.eventRange).instance.instanceId;this.relevantEvents=i.getRelevantEvents(l.getCurrentData().eventStore,h),s.minDistance=t.isTouch?0:o.eventDragMinDistance,s.delay=t.isTouch&&h!==n.props.eventSelection?function(t){let{options:e}=t.context,i=e.eventLongPressDelay;null==i&&(i=e.longPressDelay);return i}(n):null,o.fixedMirrorParent?r.parentNode=o.fixedMirrorParent:r.parentNode=i.elementClosest(e,".fc"),r.revertDuration=o.dragRevertDuration;let c=n.isValidSegDownEl(e)&&!i.elementClosest(e,".fc-event-resizer");s.setIgnoreMove(!c),this.isDragging=c&&t.subjectEl.classList.contains("fc-event-draggable")},this.handleDragStart=t=>{let e=this.component.context,n=this.eventRange,s=n.instance.instanceId;t.isTouch?s!==this.component.props.eventSelection&&e.dispatch({type:"SELECT_EVENT",eventInstanceId:s}):e.dispatch({type:"UNSELECT_EVENT"}),this.isDragging&&(e.calendarApi.unselect(t),e.emitter.trigger("eventDragStart",{el:this.subjectEl,event:new i.EventImpl(e,n.def,n.instance),jsEvent:t.origEvent,view:e.viewApi}))},this.handleHitUpdate=(t,e)=>{if(!this.isDragging)return;let n=this.relevantEvents,s=this.hitDragging.initialHit,r=this.component.context,o=null,l=null,a=null,h=!1,c={affectedEvents:n,mutatedEvents:i.createEmptyEventStore(),isEvent:!0};if(t){o=t.context;let e=o.options;r===o||e.editable&&e.droppable?(l=function(t,e,n,s){let r=t.dateSpan,o=e.dateSpan,l=r.range.start,a=o.range.start,h={};r.allDay!==o.allDay&&(h.allDay=o.allDay,h.hasEnd=e.context.options.allDayMaintainDuration,l=o.allDay?i.startOfDay(n):n);let c=i.diffDates(l,a,t.context.dateEnv,t.componentId===e.componentId?t.largeUnit:null);c.milliseconds&&(h.allDay=!1);let d={datesDelta:c,standardProps:h};for(let i of s)i(d,t,e);return d}(s,t,this.eventRange.instance.range.start,o.getCurrentData().pluginHooks.eventDragMutationMassagers),l&&(a=i.applyMutationToEventStore(n,o.getCurrentData().eventUiBases,l,o),c.mutatedEvents=a,i.isInteractionValid(c,t.dateProfile,o)||(h=!0,l=null,a=null,c.mutatedEvents=i.createEmptyEventStore()))):o=null}this.displayDrag(o,c),h?i.disableCursor():i.enableCursor(),e||(r===o&&S(s,t)&&(l=null),this.dragging.setMirrorNeedsRevert(!l),this.dragging.setMirrorIsVisible(!t||!this.subjectEl.getRootNode().querySelector(".fc-event-mirror")),this.receivingContext=o,this.validMutation=l,this.mutatedRelevantEvents=a)},this.handlePointerUp=()=>{this.isDragging||this.cleanup()},this.handleDragEnd=t=>{if(this.isDragging){let e=this.component.context,n=e.viewApi,{receivingContext:s,validMutation:r}=this,o=this.eventRange.def,l=this.eventRange.instance,a=new i.EventImpl(e,o,l),h=this.relevantEvents,c=this.mutatedRelevantEvents,{finalHit:d}=this.hitDragging;if(this.clearDrag(),e.emitter.trigger("eventDragStop",{el:this.subjectEl,event:a,jsEvent:t.origEvent,view:n}),r){if(s===e){let s=new i.EventImpl(e,c.defs[o.defId],l?c.instances[l.instanceId]:null);e.dispatch({type:"MERGE_EVENTS",eventStore:c});let d={oldEvent:a,event:s,relatedEvents:i.buildEventApis(c,e,l),revert(){e.dispatch({type:"MERGE_EVENTS",eventStore:h})}},g={};for(let t of e.getCurrentData().pluginHooks.eventDropTransformers)Object.assign(g,t(r,e));e.emitter.trigger("eventDrop",Object.assign(Object.assign(Object.assign({},d),g),{el:t.subjectEl,delta:r.datesDelta,jsEvent:t.origEvent,view:n})),e.emitter.trigger("eventChange",d)}else if(s){let r={event:a,relatedEvents:i.buildEventApis(h,e,l),revert(){e.dispatch({type:"MERGE_EVENTS",eventStore:h})}};e.emitter.trigger("eventLeave",Object.assign(Object.assign({},r),{draggedEl:t.subjectEl,view:n})),e.dispatch({type:"REMOVE_EVENTS",eventStore:h}),e.emitter.trigger("eventRemove",r);let g=c.defs[o.defId],u=c.instances[l.instanceId],p=new i.EventImpl(s,g,u);s.dispatch({type:"MERGE_EVENTS",eventStore:c});let v={event:p,relatedEvents:i.buildEventApis(c,s,u),revert(){s.dispatch({type:"REMOVE_EVENTS",eventStore:c})}};s.emitter.trigger("eventAdd",v),t.isTouch&&s.dispatch({type:"SELECT_EVENT",eventInstanceId:l.instanceId}),s.emitter.trigger("drop",Object.assign(Object.assign({},D(d.dateSpan,s)),{draggedEl:t.subjectEl,jsEvent:t.origEvent,view:d.context.viewApi})),s.emitter.trigger("eventReceive",Object.assign(Object.assign({},v),{draggedEl:t.subjectEl,view:d.context.viewApi}))}}else e.emitter.trigger("_noEventDrop")}this.cleanup()};let{component:e}=this,{options:n}=e.context,s=this.dragging=new p(t.el);s.pointer.selector=w.SELECTOR,s.touchScrollAllowed=!1,s.autoScroller.isEnabled=n.dragScroll;let r=this.hitDragging=new m(this.dragging,i.interactionSettingsStore);r.useSubjectCenter=t.useEventCenter,r.emitter.on("pointerdown",this.handlePointerDown),r.emitter.on("dragstart",this.handleDragStart),r.emitter.on("hitupdate",this.handleHitUpdate),r.emitter.on("pointerup",this.handlePointerUp),r.emitter.on("dragend",this.handleDragEnd)}destroy(){this.dragging.destroy()}displayDrag(t,e){let n=this.component.context,s=this.receivingContext;s&&s!==t&&(s===n?s.dispatch({type:"SET_EVENT_DRAG",state:{affectedEvents:e.affectedEvents,mutatedEvents:i.createEmptyEventStore(),isEvent:!0}}):s.dispatch({type:"UNSET_EVENT_DRAG"})),t&&t.dispatch({type:"SET_EVENT_DRAG",state:e})}clearDrag(){let t=this.component.context,{receivingContext:e}=this;e&&e.dispatch({type:"UNSET_EVENT_DRAG"}),t!==e&&t.dispatch({type:"UNSET_EVENT_DRAG"})}cleanup(){this.subjectSeg=null,this.isDragging=!1,this.eventRange=null,this.relevantEvents=null,this.receivingContext=null,this.validMutation=null,this.mutatedRelevantEvents=null}}w.SELECTOR=".fc-event-draggable, .fc-event-resizable";class T extends i.Interaction{constructor(t){super(t),this.draggingSegEl=null,this.draggingSeg=null,this.eventRange=null,this.relevantEvents=null,this.validMutation=null,this.mutatedRelevantEvents=null,this.handlePointerDown=t=>{let{component:e}=this,n=this.querySegEl(t),s=i.getElSeg(n),r=this.eventRange=s.eventRange;this.dragging.minDistance=e.context.options.eventDragMinDistance,this.dragging.setIgnoreMove(!this.component.isValidSegDownEl(t.origEvent.target)||t.isTouch&&this.component.props.eventSelection!==r.instance.instanceId)},this.handleDragStart=t=>{let{context:e}=this.component,n=this.eventRange;this.relevantEvents=i.getRelevantEvents(e.getCurrentData().eventStore,this.eventRange.instance.instanceId);let s=this.querySegEl(t);this.draggingSegEl=s,this.draggingSeg=i.getElSeg(s),e.calendarApi.unselect(),e.emitter.trigger("eventResizeStart",{el:s,event:new i.EventImpl(e,n.def,n.instance),jsEvent:t.origEvent,view:e.viewApi})},this.handleHitUpdate=(t,e,n)=>{let{context:s}=this.component,r=this.relevantEvents,o=this.hitDragging.initialHit,l=this.eventRange.instance,a=null,h=null,c=!1,d={affectedEvents:r,mutatedEvents:i.createEmptyEventStore(),isEvent:!0};if(t){t.componentId===o.componentId&&this.isHitComboAllowed&&!this.isHitComboAllowed(o,t)||(a=function(t,e,n,s){let r=t.context.dateEnv,o=t.dateSpan.range.start,l=e.dateSpan.range.start,a=i.diffDates(o,l,r,t.largeUnit);if(n){if(r.add(s.start,a)<s.end)return{startDelta:a}}else if(r.add(s.end,a)>s.start)return{endDelta:a};return null}(o,t,n.subjectEl.classList.contains("fc-event-resizer-start"),l.range))}a&&(h=i.applyMutationToEventStore(r,s.getCurrentData().eventUiBases,a,s),d.mutatedEvents=h,i.isInteractionValid(d,t.dateProfile,s)||(c=!0,a=null,h=null,d.mutatedEvents=null)),h?s.dispatch({type:"SET_EVENT_RESIZE",state:d}):s.dispatch({type:"UNSET_EVENT_RESIZE"}),c?i.disableCursor():i.enableCursor(),e||(a&&S(o,t)&&(a=null),this.validMutation=a,this.mutatedRelevantEvents=h)},this.handleDragEnd=t=>{let{context:e}=this.component,n=this.eventRange.def,s=this.eventRange.instance,r=new i.EventImpl(e,n,s),o=this.relevantEvents,l=this.mutatedRelevantEvents;if(e.emitter.trigger("eventResizeStop",{el:this.draggingSegEl,event:r,jsEvent:t.origEvent,view:e.viewApi}),this.validMutation){let a=new i.EventImpl(e,l.defs[n.defId],s?l.instances[s.instanceId]:null);e.dispatch({type:"MERGE_EVENTS",eventStore:l});let h={oldEvent:r,event:a,relatedEvents:i.buildEventApis(l,e,s),revert(){e.dispatch({type:"MERGE_EVENTS",eventStore:o})}};e.emitter.trigger("eventResize",Object.assign(Object.assign({},h),{el:this.draggingSegEl,startDelta:this.validMutation.startDelta||i.createDuration(0),endDelta:this.validMutation.endDelta||i.createDuration(0),jsEvent:t.origEvent,view:e.viewApi})),e.emitter.trigger("eventChange",h)}else e.emitter.trigger("_noEventResize");this.draggingSeg=null,this.relevantEvents=null,this.validMutation=null};let{component:e}=t,n=this.dragging=new p(t.el);n.pointer.selector=".fc-event-resizer",n.touchScrollAllowed=!1,n.autoScroller.isEnabled=e.context.options.dragScroll;let s=this.hitDragging=new m(this.dragging,i.interactionSettingsToStore(t));s.emitter.on("pointerdown",this.handlePointerDown),s.emitter.on("dragstart",this.handleDragStart),s.emitter.on("hitupdate",this.handleHitUpdate),s.emitter.on("dragend",this.handleDragEnd)}destroy(){this.dragging.destroy()}querySegEl(t){return i.elementClosest(t.subjectEl,".fc-event")}}const b={fixedMirrorParent:i.identity},M={dateClick:i.identity,eventDragStart:i.identity,eventDragStop:i.identity,eventDrop:i.identity,eventResizeStart:i.identity,eventResizeStop:i.identity,eventResize:i.identity,drop:i.identity,eventReceive:i.identity,eventLeave:i.identity};class C{constructor(t,e){this.receivingContext=null,this.droppableEvent=null,this.suppliedDragMeta=null,this.dragMeta=null,this.handleDragStart=t=>{this.dragMeta=this.buildDragMeta(t.subjectEl)},this.handleHitUpdate=(t,e,n)=>{let{dragging:s}=this.hitDragging,r=null,o=null,l=!1,a={affectedEvents:i.createEmptyEventStore(),mutatedEvents:i.createEmptyEventStore(),isEvent:this.dragMeta.create};t&&(r=t.context,this.canDropElOnCalendar(n.subjectEl,r)&&(o=function(t,e,n){let s=Object.assign({},e.leftoverProps);for(let i of n.pluginHooks.externalDefTransforms)Object.assign(s,i(t,e));let{refined:r,extra:o}=i.refineEventDef(s,n),l=i.parseEventDef(r,o,e.sourceId,t.allDay,n.options.forceEventDuration||Boolean(e.duration),n),a=t.range.start;t.allDay&&e.startTime&&(a=n.dateEnv.add(a,e.startTime));let h=e.duration?n.dateEnv.add(a,e.duration):i.getDefaultEventEnd(t.allDay,a,n),c=i.createEventInstance(l.defId,{start:a,end:h});return{def:l,instance:c}}(t.dateSpan,this.dragMeta,r),a.mutatedEvents=i.eventTupleToStore(o),l=!i.isInteractionValid(a,t.dateProfile,r),l&&(a.mutatedEvents=i.createEmptyEventStore(),o=null))),this.displayDrag(r,a),s.setMirrorIsVisible(e||!o||!document.querySelector(".fc-event-mirror")),l?i.disableCursor():i.enableCursor(),e||(s.setMirrorNeedsRevert(!o),this.receivingContext=r,this.droppableEvent=o)},this.handleDragEnd=t=>{let{receivingContext:e,droppableEvent:n}=this;if(this.clearDrag(),e&&n){let s=this.hitDragging.finalHit,r=s.context.viewApi,o=this.dragMeta;if(e.emitter.trigger("drop",Object.assign(Object.assign({},D(s.dateSpan,e)),{draggedEl:t.subjectEl,jsEvent:t.origEvent,view:r})),o.create){let s=i.eventTupleToStore(n);e.dispatch({type:"MERGE_EVENTS",eventStore:s}),t.isTouch&&e.dispatch({type:"SELECT_EVENT",eventInstanceId:n.instance.instanceId}),e.emitter.trigger("eventReceive",{event:new i.EventImpl(e,n.def,n.instance),relatedEvents:[],revert(){e.dispatch({type:"REMOVE_EVENTS",eventStore:s})},draggedEl:t.subjectEl,view:r})}}this.receivingContext=null,this.droppableEvent=null};let n=this.hitDragging=new m(t,i.interactionSettingsStore);n.requireInitial=!1,n.emitter.on("dragstart",this.handleDragStart),n.emitter.on("hitupdate",this.handleHitUpdate),n.emitter.on("dragend",this.handleDragEnd),this.suppliedDragMeta=e}buildDragMeta(t){return"object"==typeof this.suppliedDragMeta?i.parseDragMeta(this.suppliedDragMeta):"function"==typeof this.suppliedDragMeta?i.parseDragMeta(this.suppliedDragMeta(t)):function(t){let e=function(t,e){let n=i.config.dataAttrPrefix,s=(n?n+"-":"")+e;return t.getAttribute("data-"+s)||""}(t,"event"),n=e?JSON.parse(e):{create:!1};return i.parseDragMeta(n)}(t)}displayDrag(t,e){let i=this.receivingContext;i&&i!==t&&i.dispatch({type:"UNSET_EVENT_DRAG"}),t&&t.dispatch({type:"SET_EVENT_DRAG",state:e})}clearDrag(){this.receivingContext&&this.receivingContext.dispatch({type:"UNSET_EVENT_DRAG"})}canDropElOnCalendar(t,e){let n=e.options.dropAccept;return"function"==typeof n?n.call(e.calendarApi,t):"string"!=typeof n||!n||Boolean(i.elementMatches(t,n))}}i.config.dataAttrPrefix="";class R extends i.ElementDragging{constructor(t){super(t),this.shouldIgnoreMove=!1,this.mirrorSelector="",this.currentMirrorEl=null,this.handlePointerDown=t=>{this.emitter.trigger("pointerdown",t),this.shouldIgnoreMove||this.emitter.trigger("dragstart",t)},this.handlePointerMove=t=>{this.shouldIgnoreMove||this.emitter.trigger("dragmove",t)},this.handlePointerUp=t=>{this.emitter.trigger("pointerup",t),this.shouldIgnoreMove||this.emitter.trigger("dragend",t)};let e=this.pointer=new o(t);e.emitter.on("pointerdown",this.handlePointerDown),e.emitter.on("pointermove",this.handlePointerMove),e.emitter.on("pointerup",this.handlePointerUp)}destroy(){this.pointer.destroy()}setIgnoreMove(t){this.shouldIgnoreMove=t}setMirrorIsVisible(t){if(t)this.currentMirrorEl&&(this.currentMirrorEl.style.visibility="",this.currentMirrorEl=null);else{let t=this.mirrorSelector?document.querySelector(this.mirrorSelector):null;t&&(this.currentMirrorEl=t,t.style.visibility="hidden")}}}var I=e.createPlugin({name:"@fullcalendar/interaction",componentInteractions:[f,y,w,T],calendarInteractions:[class{constructor(t){this.context=t,this.isRecentPointerDateSelect=!1,this.matchesCancel=!1,this.matchesEvent=!1,this.onSelect=t=>{t.jsEvent&&(this.isRecentPointerDateSelect=!0)},this.onDocumentPointerDown=t=>{let e=this.context.options.unselectCancel,n=i.getEventTargetViaRoot(t.origEvent);this.matchesCancel=!!i.elementClosest(n,e),this.matchesEvent=!!i.elementClosest(n,w.SELECTOR)},this.onDocumentPointerUp=t=>{let{context:e}=this,{documentPointer:i}=this,n=e.getCurrentData();if(!i.wasTouchScroll){if(n.dateSelection&&!this.isRecentPointerDateSelect){let i=e.options.unselectAuto;!i||i&&this.matchesCancel||e.calendarApi.unselect(t)}n.eventSelection&&!this.matchesEvent&&e.dispatch({type:"UNSELECT_EVENT"})}this.isRecentPointerDateSelect=!1};let e=this.documentPointer=new o(document);e.shouldIgnoreMove=!0,e.shouldWatchScroll=!1,e.emitter.on("pointerdown",this.onDocumentPointerDown),e.emitter.on("pointerup",this.onDocumentPointerUp),t.emitter.on("select",this.onSelect)}destroy(){this.context.emitter.off("select",this.onSelect),this.documentPointer.destroy()}}],elementDraggingImpl:p,optionRefiners:b,listenerRefiners:M});return e.globalPlugins.push(I),t.Draggable=class{constructor(t,e={}){this.handlePointerDown=t=>{let{dragging:e}=this,{minDistance:n,longPressDelay:s}=this.settings;e.minDistance=null!=n?n:t.isTouch?0:i.BASE_OPTION_DEFAULTS.eventDragMinDistance,e.delay=t.isTouch?null!=s?s:i.BASE_OPTION_DEFAULTS.longPressDelay:0},this.handleDragStart=t=>{t.isTouch&&this.dragging.delay&&t.subjectEl.classList.contains("fc-event")&&this.dragging.mirror.getMirrorEl().classList.add("fc-event-selected")},this.settings=e;let n=this.dragging=new p(t);n.touchScrollAllowed=!1,null!=e.itemSelector&&(n.pointer.selector=e.itemSelector),null!=e.appendTo&&(n.mirror.parentNode=e.appendTo),n.emitter.on("pointerdown",this.handlePointerDown),n.emitter.on("dragstart",this.handleDragStart),new C(n,e.eventData)}destroy(){this.dragging.destroy()}},t.ThirdPartyDraggable=class{constructor(t,e){let i=document;t===document||t instanceof Element?(i=t,e=e||{}):e=t||{};let n=this.dragging=new R(i);"string"==typeof e.itemSelector?n.pointer.selector=e.itemSelector:i===document&&(n.pointer.selector="[data-event]"),"string"==typeof e.mirrorSelector&&(n.mirrorSelector=e.mirrorSelector),new C(n,e.eventData).hitDragging.disablePointCheck=!0}destroy(){this.dragging.destroy()}},t.default=I,Object.defineProperty(t,"__esModule",{value:!0}),t}({},FullCalendar,FullCalendar.Internal); \ No newline at end of file | ||