diff --git a/apps/schoolCalendar/CalenderLogo.png b/apps/schoolCalendar/CalenderLogo.png
new file mode 100644
index 000000000..49d198a15
Binary files /dev/null and b/apps/schoolCalendar/CalenderLogo.png differ
diff --git a/apps/schoolCalendar/ChangeLog.md b/apps/schoolCalendar/ChangeLog.md
new file mode 100644
index 000000000..c1cd00ff7
--- /dev/null
+++ b/apps/schoolCalendar/ChangeLog.md
@@ -0,0 +1,2 @@
+# Changelog for the bangle.hs-calenderapp repository:
+0.01: App is created with gradient background.
diff --git a/apps/schoolCalendar/README.md b/apps/schoolCalendar/README.md
new file mode 100644
index 000000000..1cd152864
--- /dev/null
+++ b/apps/schoolCalendar/README.md
@@ -0,0 +1,9 @@
+# Bangle.js Calendar
+
+School Calendar is a calendar that you can see your upcoming classes or schedule.
+
+## Versions:
+
+Version 1.00: Get Design Working
+
+Version 2.00: Update Graphics
diff --git a/apps/schoolCalendar/app-icon.js b/apps/schoolCalendar/app-icon.js
new file mode 100644
index 000000000..f41623fc7
--- /dev/null
+++ b/apps/schoolCalendar/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwyBC/AH4A/AH4A/AH4A/AH4A/AH4A80s0AIIh/L/5f/EP4ATscsAIo9DBY4BVEJZf/L/5fRznzAIJfdEJZfpymyAJmSBpwPLBZRfqIYYBwL9OMuIBzL9VRAMRTDCJhfymBpkL+GEmABzL9UQAJelinOrPWzQDBymSCpe96+c6YnNL9N794tBAYoFD5152u21t1AoP332MuIPDVIJxB88c///AoODD4gFBLoYJBL9YBT888M4IHDNYPvvoDDznzD5pfq54BT737L4QHCVYQFCL4XTD5pfpueuAKOtqpRBxlRB5JfDEJpfqxwBIG4OOwkw/4AC+++xlxC5ZfBymyDoYlHAIJf0AImEiBbB0s0MIOtuoTJL4glML9NrtoBTLoJTBBpJfCyQfNL9VNAKZfB88cBpJfED5hfslgzEAoT3BxlxBYd795RB2uWC4tjAoQNBC4olFCIZfpFoIBJe4JJB+++AYe964XLL4YPLAIJfqhgBNueuAIJnBCp4BPL9Na9YBBsQBCAoY3BA4YBRC4INPL9oBS5YXWDoxfqJIIByL9NS1QBzL9WKAIgzBAooHFAMBfpMJABqLtYA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4ALA"))
diff --git a/apps/schoolCalendar/fullcalendar/interaction/LICENSE.txt b/apps/schoolCalendar/fullcalendar/interaction/LICENSE.txt
new file mode 100644
index 000000000..84924a980
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/LICENSE.txt
@@ -0,0 +1,22 @@
+MIT License
+
+Copyright (c) 2021 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/apps/schoolCalendar/fullcalendar/interaction/README.md b/apps/schoolCalendar/fullcalendar/interaction/README.md
new file mode 100644
index 000000000..6d5821d71
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Interaction Plugin
+
+Provides functionality for event drag-n-drop, resizing, dateClick, and selectable actions
+
+[View the docs »](https://fullcalendar.io/docs/editable)
+
+This package was created from the [FullCalendar monorepo »](https://github.com/fullcalendar/fullcalendar)
diff --git a/apps/schoolCalendar/fullcalendar/interaction/package.json b/apps/schoolCalendar/fullcalendar/interaction/package.json
new file mode 100644
index 000000000..d382635a8
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@fullcalendar/interaction",
+ "version": "5.9.0",
+ "title": "FullCalendar Interaction Plugin",
+ "description": "Provides functionality for event drag-n-drop, resizing, dateClick, and selectable actions",
+ "docs": "https://fullcalendar.io/docs/editable",
+ "dependencies": {
+ "@fullcalendar/common": "workspace:~5.9.0",
+ "tslib": "^2.1.0"
+ },
+ "main": "main.cjs.js",
+ "module": "main.js",
+ "types": "main.d.ts",
+ "jsdelivr": "main.global.min.js",
+ "browserGlobal": "FullCalendarInteraction",
+ "homepage": "https://fullcalendar.io/",
+ "bugs": "https://fullcalendar.io/reporting-bugs",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/fullcalendar/fullcalendar.git",
+ "homepage": "https://github.com/fullcalendar/fullcalendar"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "Adam Shaw",
+ "email": "arshaw@arshaw.com",
+ "url": "http://arshaw.com/"
+ },
+ "devDependencies": {
+ "@fullcalendar/core-preact": "workspace:*"
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/ElementScrollGeomCache.ts b/apps/schoolCalendar/fullcalendar/interaction/src/ElementScrollGeomCache.ts
new file mode 100644
index 000000000..83be540cd
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/ElementScrollGeomCache.ts
@@ -0,0 +1,16 @@
+import { computeInnerRect, ElementScrollController } from '@fullcalendar/common'
+import { ScrollGeomCache } from './ScrollGeomCache'
+
+export class ElementScrollGeomCache extends ScrollGeomCache {
+ constructor(el: HTMLElement, doesListening: boolean) {
+ super(new ElementScrollController(el), doesListening)
+ }
+
+ getEventTarget(): EventTarget {
+ return (this.scrollController as ElementScrollController).el
+ }
+
+ computeClientRect() {
+ return computeInnerRect((this.scrollController as ElementScrollController).el)
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/OffsetTracker.ts b/apps/schoolCalendar/fullcalendar/interaction/src/OffsetTracker.ts
new file mode 100644
index 000000000..b1fac23e6
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/OffsetTracker.ts
@@ -0,0 +1,76 @@
+import {
+ getClippingParents, computeRect,
+ pointInsideRect, Rect,
+} from '@fullcalendar/common'
+import { ElementScrollGeomCache } from './ElementScrollGeomCache'
+
+/*
+When this class is instantiated, it records the offset of an element (relative to the document topleft),
+and continues to monitor scrolling, updating the cached coordinates if it needs to.
+Does not access the DOM after instantiation, so highly performant.
+
+Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element
+and an determine if a given point is inside the combined clipping rectangle.
+*/
+export class OffsetTracker { // ElementOffsetTracker
+ scrollCaches: ElementScrollGeomCache[]
+ origRect: Rect
+
+ constructor(el: HTMLElement) {
+ this.origRect = computeRect(el)
+
+ // will work fine for divs that have overflow:hidden
+ this.scrollCaches = getClippingParents(el).map(
+ (scrollEl) => new ElementScrollGeomCache(scrollEl, true), // listen=true
+ )
+ }
+
+ destroy() {
+ for (let scrollCache of this.scrollCaches) {
+ scrollCache.destroy()
+ }
+ }
+
+ computeLeft() {
+ let left = this.origRect.left
+
+ for (let scrollCache of this.scrollCaches) {
+ left += scrollCache.origScrollLeft - scrollCache.getScrollLeft()
+ }
+
+ return left
+ }
+
+ computeTop() {
+ let top = this.origRect.top
+
+ for (let scrollCache of this.scrollCaches) {
+ top += scrollCache.origScrollTop - scrollCache.getScrollTop()
+ }
+
+ return top
+ }
+
+ isWithinClipping(pageX: number, pageY: number): boolean {
+ let point = { left: pageX, top: pageY }
+
+ for (let scrollCache of this.scrollCaches) {
+ if (
+ !isIgnoredClipping(scrollCache.getEventTarget()) &&
+ !pointInsideRect(point, scrollCache.clientRect)
+ ) {
+ return false
+ }
+ }
+
+ return true
+ }
+}
+
+// certain clipping containers should never constrain interactions, like and
+// https://github.com/fullcalendar/fullcalendar/issues/3615
+function isIgnoredClipping(node: EventTarget) {
+ let tagName = (node as HTMLElement).tagName
+
+ return tagName === 'HTML' || tagName === 'BODY'
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/ScrollGeomCache.ts b/apps/schoolCalendar/fullcalendar/interaction/src/ScrollGeomCache.ts
new file mode 100644
index 000000000..63ec6a5e1
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/ScrollGeomCache.ts
@@ -0,0 +1,107 @@
+import { Rect, ScrollController } from '@fullcalendar/common'
+
+/*
+Is a cache for a given element's scroll information (all the info that ScrollController stores)
+in addition the "client rectangle" of the element.. the area within the scrollbars.
+
+The cache can be in one of two modes:
+- doesListening:false - ignores when the container is scrolled by someone else
+- doesListening:true - watch for scrolling and update the cache
+*/
+export abstract class ScrollGeomCache extends ScrollController {
+ clientRect: Rect
+ origScrollTop: number
+ origScrollLeft: number
+
+ protected scrollController: ScrollController
+ protected doesListening: boolean
+ protected scrollTop: number
+ protected scrollLeft: number
+ protected scrollWidth: number
+ protected scrollHeight: number
+ protected clientWidth: number
+ protected clientHeight: number
+
+ constructor(scrollController: ScrollController, doesListening: boolean) {
+ super()
+ this.scrollController = scrollController
+ this.doesListening = doesListening
+ this.scrollTop = this.origScrollTop = scrollController.getScrollTop()
+ this.scrollLeft = this.origScrollLeft = scrollController.getScrollLeft()
+ this.scrollWidth = scrollController.getScrollWidth()
+ this.scrollHeight = scrollController.getScrollHeight()
+ this.clientWidth = scrollController.getClientWidth()
+ this.clientHeight = scrollController.getClientHeight()
+ this.clientRect = this.computeClientRect() // do last in case it needs cached values
+
+ if (this.doesListening) {
+ this.getEventTarget().addEventListener('scroll', this.handleScroll)
+ }
+ }
+
+ abstract getEventTarget(): EventTarget
+ abstract computeClientRect(): Rect
+
+ destroy() {
+ if (this.doesListening) {
+ this.getEventTarget().removeEventListener('scroll', this.handleScroll)
+ }
+ }
+
+ handleScroll = () => {
+ this.scrollTop = this.scrollController.getScrollTop()
+ this.scrollLeft = this.scrollController.getScrollLeft()
+ this.handleScrollChange()
+ }
+
+ getScrollTop() {
+ return this.scrollTop
+ }
+
+ getScrollLeft() {
+ return this.scrollLeft
+ }
+
+ setScrollTop(top: number) {
+ this.scrollController.setScrollTop(top)
+
+ if (!this.doesListening) {
+ // we are not relying on the element to normalize out-of-bounds scroll values
+ // so we need to sanitize ourselves
+ this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0)
+
+ this.handleScrollChange()
+ }
+ }
+
+ setScrollLeft(top: number) {
+ this.scrollController.setScrollLeft(top)
+
+ if (!this.doesListening) {
+ // we are not relying on the element to normalize out-of-bounds scroll values
+ // so we need to sanitize ourselves
+ this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0)
+
+ this.handleScrollChange()
+ }
+ }
+
+ getClientWidth() {
+ return this.clientWidth
+ }
+
+ getClientHeight() {
+ return this.clientHeight
+ }
+
+ getScrollWidth() {
+ return this.scrollWidth
+ }
+
+ getScrollHeight() {
+ return this.scrollHeight
+ }
+
+ handleScrollChange() {
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/WindowScrollGeomCache.ts b/apps/schoolCalendar/fullcalendar/interaction/src/WindowScrollGeomCache.ts
new file mode 100644
index 000000000..ca65dee6e
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/WindowScrollGeomCache.ts
@@ -0,0 +1,27 @@
+import { Rect, WindowScrollController } from '@fullcalendar/common'
+import { ScrollGeomCache } from './ScrollGeomCache'
+
+export class WindowScrollGeomCache extends ScrollGeomCache {
+ constructor(doesListening: boolean) {
+ super(new WindowScrollController(), doesListening)
+ }
+
+ getEventTarget(): EventTarget {
+ return window
+ }
+
+ computeClientRect(): Rect {
+ return {
+ left: this.scrollLeft,
+ right: this.scrollLeft + this.clientWidth,
+ top: this.scrollTop,
+ bottom: this.scrollTop + this.clientHeight,
+ }
+ }
+
+ // the window is the only scroll object that changes it's rectangle relative
+ // to the document's topleft as it scrolls
+ handleScrollChange() {
+ this.clientRect = this.computeClientRect()
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/api-type-deps.ts b/apps/schoolCalendar/fullcalendar/interaction/src/api-type-deps.ts
new file mode 100644
index 000000000..2b1b51b06
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/api-type-deps.ts
@@ -0,0 +1,6 @@
+// TODO: rename file to public-types.ts
+
+export { DateClickArg } from './interactions/DateClicking'
+export { EventDragStartArg, EventDragStopArg } from './interactions/EventDragging'
+export { EventResizeStartArg, EventResizeStopArg, EventResizeDoneArg } from './interactions/EventResizing'
+export { DropArg, EventReceiveArg, EventLeaveArg } from './utils'
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/dnd/AutoScroller.ts b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/AutoScroller.ts
new file mode 100644
index 000000000..8d4e8f015
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/AutoScroller.ts
@@ -0,0 +1,217 @@
+import { getElRoot } from '@fullcalendar/common'
+import { ScrollGeomCache } from '../ScrollGeomCache'
+import { ElementScrollGeomCache } from '../ElementScrollGeomCache'
+import { WindowScrollGeomCache } from '../WindowScrollGeomCache'
+
+interface Edge {
+ scrollCache: ScrollGeomCache
+ name: 'top' | 'left' | 'right' | 'bottom'
+ distance: number // how many pixels the current pointer is from the edge
+}
+
+// If available we are using native "performance" API instead of "Date"
+// Read more about it on MDN:
+// https://developer.mozilla.org/en-US/docs/Web/API/Performance
+const getTime = typeof performance === 'function' ? (performance as any).now : Date.now
+
+/*
+For a pointer interaction, automatically scrolls certain scroll containers when the pointer
+approaches the edge.
+
+The caller must call start + handleMove + stop.
+*/
+export class AutoScroller {
+ // options that can be set by caller
+ isEnabled: boolean = true
+ scrollQuery: (Window | string)[] = [window, '.fc-scroller']
+ edgeThreshold: number = 50 // pixels
+ maxVelocity: number = 300 // pixels per second
+
+ // internal state
+ pointerScreenX: number | null = null
+ pointerScreenY: number | null = null
+ isAnimating: boolean = false
+ scrollCaches: ScrollGeomCache[] | null = null
+ msSinceRequest?: number
+
+ // protect against the initial pointerdown being too close to an edge and starting the scroll
+ everMovedUp: boolean = false
+ everMovedDown: boolean = false
+ everMovedLeft: boolean = false
+ everMovedRight: boolean = false
+
+ start(pageX: number, pageY: number, scrollStartEl: HTMLElement) {
+ if (this.isEnabled) {
+ this.scrollCaches = this.buildCaches(scrollStartEl)
+ this.pointerScreenX = null
+ this.pointerScreenY = null
+ this.everMovedUp = false
+ this.everMovedDown = false
+ this.everMovedLeft = false
+ this.everMovedRight = false
+ this.handleMove(pageX, pageY)
+ }
+ }
+
+ handleMove(pageX: number, pageY: number) {
+ if (this.isEnabled) {
+ let pointerScreenX = pageX - window.pageXOffset
+ let pointerScreenY = pageY - window.pageYOffset
+
+ let yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY
+ let xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX
+
+ if (yDelta < 0) {
+ this.everMovedUp = true
+ } else if (yDelta > 0) {
+ this.everMovedDown = true
+ }
+
+ if (xDelta < 0) {
+ this.everMovedLeft = true
+ } else if (xDelta > 0) {
+ this.everMovedRight = true
+ }
+
+ this.pointerScreenX = pointerScreenX
+ this.pointerScreenY = pointerScreenY
+
+ if (!this.isAnimating) {
+ this.isAnimating = true
+ this.requestAnimation(getTime())
+ }
+ }
+ }
+
+ stop() {
+ if (this.isEnabled) {
+ this.isAnimating = false // will stop animation
+
+ for (let scrollCache of this.scrollCaches!) {
+ scrollCache.destroy()
+ }
+
+ this.scrollCaches = null
+ }
+ }
+
+ requestAnimation(now: number) {
+ this.msSinceRequest = now
+ requestAnimationFrame(this.animate)
+ }
+
+ private animate = () => {
+ if (this.isAnimating) { // wasn't cancelled between animation calls
+ let edge = this.computeBestEdge(
+ this.pointerScreenX! + window.pageXOffset,
+ this.pointerScreenY! + window.pageYOffset,
+ )
+
+ if (edge) {
+ let now = getTime()
+ this.handleSide(edge, (now - this.msSinceRequest!) / 1000)
+ this.requestAnimation(now)
+ } else {
+ this.isAnimating = false // will stop animation
+ }
+ }
+ }
+
+ private handleSide(edge: Edge, seconds: number) {
+ let { scrollCache } = edge
+ let { edgeThreshold } = this
+ let invDistance = edgeThreshold - edge.distance
+ let velocity = // the closer to the edge, the faster we scroll
+ ((invDistance * invDistance) / (edgeThreshold * edgeThreshold)) * // quadratic
+ this.maxVelocity * seconds
+ let sign = 1
+
+ switch (edge.name) {
+ case 'left':
+ sign = -1
+ // falls through
+ case 'right':
+ scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign)
+ break
+
+ case 'top':
+ sign = -1
+ // falls through
+ case 'bottom':
+ scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign)
+ break
+ }
+ }
+
+ // left/top are relative to document topleft
+ private computeBestEdge(left: number, top: number): Edge | null {
+ let { edgeThreshold } = this
+ let bestSide: Edge | null = null
+
+ for (let scrollCache of this.scrollCaches!) {
+ let rect = scrollCache.clientRect
+ let leftDist = left - rect.left
+ let rightDist = rect.right - left
+ let topDist = top - rect.top
+ let bottomDist = rect.bottom - top
+
+ // completely within the rect?
+ if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) {
+ if (
+ topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() &&
+ (!bestSide || bestSide.distance > topDist)
+ ) {
+ bestSide = { scrollCache, name: 'top', distance: topDist }
+ }
+
+ if (
+ bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() &&
+ (!bestSide || bestSide.distance > bottomDist)
+ ) {
+ bestSide = { scrollCache, name: 'bottom', distance: bottomDist }
+ }
+
+ if (
+ leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() &&
+ (!bestSide || bestSide.distance > leftDist)
+ ) {
+ bestSide = { scrollCache, name: 'left', distance: leftDist }
+ }
+
+ if (
+ rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() &&
+ (!bestSide || bestSide.distance > rightDist)
+ ) {
+ bestSide = { scrollCache, name: 'right', distance: rightDist }
+ }
+ }
+ }
+
+ return bestSide
+ }
+
+ private buildCaches(scrollStartEl: HTMLElement) {
+ return this.queryScrollEls(scrollStartEl).map((el) => {
+ if (el === window) {
+ return new WindowScrollGeomCache(false) // false = don't listen to user-generated scrolls
+ }
+ return new ElementScrollGeomCache(el, false) // false = don't listen to user-generated scrolls
+ })
+ }
+
+ private queryScrollEls(scrollStartEl: HTMLElement) {
+ let els = []
+
+ for (let query of this.scrollQuery) {
+ if (typeof query === 'object') {
+ els.push(query)
+ } else {
+ els.push(...Array.prototype.slice.call(
+ getElRoot(scrollStartEl).querySelectorAll(query),
+ ))
+ }
+ }
+
+ return els
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/dnd/ElementMirror.ts b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/ElementMirror.ts
new file mode 100644
index 000000000..6c1e9f4a1
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/ElementMirror.ts
@@ -0,0 +1,146 @@
+import { removeElement, applyStyle, whenTransitionDone, Rect } from '@fullcalendar/common'
+
+/*
+An effect in which an element follows the movement of a pointer across the screen.
+The moving element is a clone of some other element.
+Must call start + handleMove + stop.
+*/
+export class ElementMirror {
+ isVisible: boolean = false // must be explicitly enabled
+ origScreenX?: number
+ origScreenY?: number
+ deltaX?: number
+ deltaY?: number
+ sourceEl: HTMLElement | null = null
+ mirrorEl: HTMLElement | null = null
+ sourceElRect: Rect | null = null // screen coords relative to viewport
+
+ // options that can be set directly by caller
+ parentNode: HTMLElement = document.body // HIGHLY SUGGESTED to set this to sidestep ShadowDOM issues
+ zIndex: number = 9999
+ revertDuration: number = 0
+
+ start(sourceEl: HTMLElement, pageX: number, pageY: number) {
+ this.sourceEl = sourceEl
+ this.sourceElRect = this.sourceEl.getBoundingClientRect()
+ this.origScreenX = pageX - window.pageXOffset
+ this.origScreenY = pageY - window.pageYOffset
+ this.deltaX = 0
+ this.deltaY = 0
+ this.updateElPosition()
+ }
+
+ handleMove(pageX: number, pageY: number) {
+ this.deltaX = (pageX - window.pageXOffset) - this.origScreenX!
+ this.deltaY = (pageY - window.pageYOffset) - this.origScreenY!
+ this.updateElPosition()
+ }
+
+ // can be called before start
+ setIsVisible(bool: boolean) {
+ if (bool) {
+ if (!this.isVisible) {
+ if (this.mirrorEl) {
+ this.mirrorEl.style.display = ''
+ }
+
+ this.isVisible = bool // needs to happen before updateElPosition
+ this.updateElPosition() // because was not updating the position while invisible
+ }
+ } else if (this.isVisible) {
+ if (this.mirrorEl) {
+ this.mirrorEl.style.display = 'none'
+ }
+
+ this.isVisible = bool
+ }
+ }
+
+ // always async
+ stop(needsRevertAnimation: boolean, callback: () => void) {
+ let done = () => {
+ this.cleanup()
+ callback()
+ }
+
+ if (
+ needsRevertAnimation &&
+ this.mirrorEl &&
+ this.isVisible &&
+ this.revertDuration && // if 0, transition won't work
+ (this.deltaX || this.deltaY) // if same coords, transition won't work
+ ) {
+ this.doRevertAnimation(done, this.revertDuration)
+ } else {
+ setTimeout(done, 0)
+ }
+ }
+
+ doRevertAnimation(callback: () => void, revertDuration: number) {
+ let mirrorEl = this.mirrorEl!
+ let finalSourceElRect = this.sourceEl!.getBoundingClientRect() // because autoscrolling might have happened
+
+ mirrorEl.style.transition =
+ 'top ' + revertDuration + 'ms,' +
+ 'left ' + revertDuration + 'ms'
+
+ applyStyle(mirrorEl, {
+ left: finalSourceElRect.left,
+ top: finalSourceElRect.top,
+ })
+
+ whenTransitionDone(mirrorEl, () => {
+ mirrorEl.style.transition = ''
+ callback()
+ })
+ }
+
+ cleanup() {
+ if (this.mirrorEl) {
+ removeElement(this.mirrorEl)
+ this.mirrorEl = null
+ }
+
+ this.sourceEl = null
+ }
+
+ updateElPosition() {
+ if (this.sourceEl && this.isVisible) {
+ applyStyle(this.getMirrorEl(), {
+ left: this.sourceElRect!.left + this.deltaX!,
+ top: this.sourceElRect!.top + this.deltaY!,
+ })
+ }
+ }
+
+ getMirrorEl(): HTMLElement {
+ let sourceElRect = this.sourceElRect!
+ let mirrorEl = this.mirrorEl
+
+ if (!mirrorEl) {
+ mirrorEl = this.mirrorEl = this.sourceEl!.cloneNode(true) as HTMLElement // cloneChildren=true
+
+ // we don't want long taps or any mouse interaction causing selection/menus.
+ // would use preventSelection(), but that prevents selectstart, causing problems.
+ mirrorEl.classList.add('fc-unselectable')
+
+ mirrorEl.classList.add('fc-event-dragging')
+
+ applyStyle(mirrorEl, {
+ position: 'fixed',
+ zIndex: this.zIndex,
+ visibility: '', // in case original element was hidden by the drag effect
+ boxSizing: 'border-box', // for easy width/height
+ width: sourceElRect.right - sourceElRect.left, // explicit height in case there was a 'right' value
+ height: sourceElRect.bottom - sourceElRect.top, // explicit width in case there was a 'bottom' value
+ right: 'auto', // erase and set width instead
+ bottom: 'auto', // erase and set height instead
+ margin: 0,
+ })
+
+ this.parentNode.appendChild(mirrorEl)
+ }
+
+ return mirrorEl
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/dnd/FeaturefulElementDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/FeaturefulElementDragging.ts
new file mode 100644
index 000000000..3f1c7826b
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/FeaturefulElementDragging.ts
@@ -0,0 +1,213 @@
+import {
+ PointerDragEvent,
+ preventSelection,
+ allowSelection,
+ preventContextMenu,
+ allowContextMenu,
+ ElementDragging,
+} from '@fullcalendar/common'
+import { PointerDragging } from './PointerDragging'
+import { ElementMirror } from './ElementMirror'
+import { AutoScroller } from './AutoScroller'
+
+/*
+Monitors dragging on an element. Has a number of high-level features:
+- minimum distance required before dragging
+- minimum wait time ("delay") before dragging
+- a mirror element that follows the pointer
+*/
+export class FeaturefulElementDragging extends ElementDragging {
+ pointer: PointerDragging
+ mirror: ElementMirror
+ autoScroller: AutoScroller
+
+ // options that can be directly set by caller
+ // the caller can also set the PointerDragging's options as well
+ delay: number | null = null
+ minDistance: number = 0
+ touchScrollAllowed: boolean = true // prevents drag from starting and blocks scrolling during drag
+
+ mirrorNeedsRevert: boolean = false
+ isInteracting: boolean = false // is the user validly moving the pointer? lasts until pointerup
+ isDragging: boolean = false // is it INTENTFULLY dragging? lasts until after revert animation
+ isDelayEnded: boolean = false
+ isDistanceSurpassed: boolean = false
+ delayTimeoutId: number | null = null
+
+ constructor(private containerEl: HTMLElement, selector?: string) {
+ super(containerEl)
+
+ let pointer = this.pointer = new PointerDragging(containerEl)
+ pointer.emitter.on('pointerdown', this.onPointerDown)
+ pointer.emitter.on('pointermove', this.onPointerMove)
+ pointer.emitter.on('pointerup', this.onPointerUp)
+
+ if (selector) {
+ pointer.selector = selector
+ }
+
+ this.mirror = new ElementMirror()
+ this.autoScroller = new AutoScroller()
+ }
+
+ destroy() {
+ this.pointer.destroy()
+
+ // HACK: simulate a pointer-up to end the current drag
+ // TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire)
+ this.onPointerUp({} as any)
+ }
+
+ onPointerDown = (ev: PointerDragEvent) => {
+ if (!this.isDragging) { // so new drag doesn't happen while revert animation is going
+ this.isInteracting = true
+ this.isDelayEnded = false
+ this.isDistanceSurpassed = false
+
+ preventSelection(document.body)
+ preventContextMenu(document.body)
+
+ // prevent links from being visited if there's an eventual drag.
+ // also prevents selection in older browsers (maybe?).
+ // not necessary for touch, besides, browser would complain about passiveness.
+ if (!ev.isTouch) {
+ ev.origEvent.preventDefault()
+ }
+
+ this.emitter.trigger('pointerdown', ev)
+
+ if (
+ this.isInteracting && // not destroyed via pointerdown handler
+ !this.pointer.shouldIgnoreMove
+ ) {
+ // actions related to initiating dragstart+dragmove+dragend...
+
+ this.mirror.setIsVisible(false) // reset. caller must set-visible
+ this.mirror.start(ev.subjectEl as HTMLElement, ev.pageX, ev.pageY) // must happen on first pointer down
+
+ this.startDelay(ev)
+
+ if (!this.minDistance) {
+ this.handleDistanceSurpassed(ev)
+ }
+ }
+ }
+ }
+
+ onPointerMove = (ev: PointerDragEvent) => {
+ if (this.isInteracting) {
+ this.emitter.trigger('pointermove', ev)
+
+ if (!this.isDistanceSurpassed) {
+ let minDistance = this.minDistance
+ let distanceSq // current distance from the origin, squared
+ let { deltaX, deltaY } = ev
+
+ distanceSq = deltaX * deltaX + deltaY * deltaY
+ if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
+ this.handleDistanceSurpassed(ev)
+ }
+ }
+
+ if (this.isDragging) {
+ // a real pointer move? (not one simulated by scrolling)
+ if (ev.origEvent.type !== 'scroll') {
+ this.mirror.handleMove(ev.pageX, ev.pageY)
+ this.autoScroller.handleMove(ev.pageX, ev.pageY)
+ }
+
+ this.emitter.trigger('dragmove', ev)
+ }
+ }
+ }
+
+ onPointerUp = (ev: PointerDragEvent) => {
+ if (this.isInteracting) {
+ this.isInteracting = false
+
+ allowSelection(document.body)
+ allowContextMenu(document.body)
+
+ this.emitter.trigger('pointerup', ev) // can potentially set mirrorNeedsRevert
+
+ if (this.isDragging) {
+ this.autoScroller.stop()
+ this.tryStopDrag(ev) // which will stop the mirror
+ }
+
+ if (this.delayTimeoutId) {
+ clearTimeout(this.delayTimeoutId)
+ this.delayTimeoutId = null
+ }
+ }
+ }
+
+ startDelay(ev: PointerDragEvent) {
+ if (typeof this.delay === 'number') {
+ this.delayTimeoutId = setTimeout(() => {
+ this.delayTimeoutId = null
+ this.handleDelayEnd(ev)
+ }, this.delay) as any // not assignable to number!
+ } else {
+ this.handleDelayEnd(ev)
+ }
+ }
+
+ handleDelayEnd(ev: PointerDragEvent) {
+ this.isDelayEnded = true
+ this.tryStartDrag(ev)
+ }
+
+ handleDistanceSurpassed(ev: PointerDragEvent) {
+ this.isDistanceSurpassed = true
+ this.tryStartDrag(ev)
+ }
+
+ tryStartDrag(ev: PointerDragEvent) {
+ if (this.isDelayEnded && this.isDistanceSurpassed) {
+ if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) {
+ this.isDragging = true
+ this.mirrorNeedsRevert = false
+
+ this.autoScroller.start(ev.pageX, ev.pageY, this.containerEl)
+ this.emitter.trigger('dragstart', ev)
+
+ if (this.touchScrollAllowed === false) {
+ this.pointer.cancelTouchScroll()
+ }
+ }
+ }
+ }
+
+ tryStopDrag(ev: PointerDragEvent) {
+ // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events
+ // that come from the document to fire beforehand. much more convenient this way.
+ this.mirror.stop(
+ this.mirrorNeedsRevert,
+ this.stopDrag.bind(this, ev), // bound with args
+ )
+ }
+
+ stopDrag(ev: PointerDragEvent) {
+ this.isDragging = false
+ this.emitter.trigger('dragend', ev)
+ }
+
+ // fill in the implementations...
+
+ setIgnoreMove(bool: boolean) {
+ this.pointer.shouldIgnoreMove = bool
+ }
+
+ setMirrorIsVisible(bool: boolean) {
+ this.mirror.setIsVisible(bool)
+ }
+
+ setMirrorNeedsRevert(bool: boolean) {
+ this.mirrorNeedsRevert = bool
+ }
+
+ setAutoScrollEnabled(bool: boolean) {
+ this.autoScroller.isEnabled = bool
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/dnd/PointerDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/PointerDragging.ts
new file mode 100644
index 000000000..dbe7d8abb
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/dnd/PointerDragging.ts
@@ -0,0 +1,344 @@
+import { config, elementClosest, Emitter, PointerDragEvent } from '@fullcalendar/common'
+
+config.touchMouseIgnoreWait = 500
+
+let ignoreMouseDepth = 0
+let listenerCnt = 0
+let isWindowTouchMoveCancelled = false
+
+/*
+Uses a "pointer" abstraction, which monitors UI events for both mouse and touch.
+Tracks when the pointer "drags" on a certain element, meaning down+move+up.
+
+Also, tracks if there was touch-scrolling.
+Also, can prevent touch-scrolling from happening.
+Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement.
+
+emits:
+- pointerdown
+- pointermove
+- pointerup
+*/
+export class PointerDragging {
+ containerEl: EventTarget
+ subjectEl: HTMLElement | null = null
+ emitter: Emitter
+
+ // options that can be directly assigned by caller
+ selector: string = '' // will cause subjectEl in all emitted events to be this element
+ handleSelector: string = ''
+ shouldIgnoreMove: boolean = false
+ shouldWatchScroll: boolean = true // for simulating pointermove on scroll
+
+ // internal states
+ isDragging: boolean = false
+ isTouchDragging: boolean = false
+ wasTouchScroll: boolean = false
+ origPageX: number
+ origPageY: number
+ prevPageX: number
+ prevPageY: number
+ prevScrollX: number // at time of last pointer pageX/pageY capture
+ prevScrollY: number // "
+
+ constructor(containerEl: EventTarget) {
+ this.containerEl = containerEl
+ this.emitter = new Emitter()
+ containerEl.addEventListener('mousedown', this.handleMouseDown as EventListener)
+ containerEl.addEventListener('touchstart', this.handleTouchStart as EventListener, { passive: true })
+ listenerCreated()
+ }
+
+ destroy() {
+ this.containerEl.removeEventListener('mousedown', this.handleMouseDown as EventListener)
+ this.containerEl.removeEventListener('touchstart', this.handleTouchStart as EventListener, { passive: true } as AddEventListenerOptions)
+ listenerDestroyed()
+ }
+
+ tryStart(ev: UIEvent): boolean {
+ let subjectEl = this.querySubjectEl(ev)
+ let downEl = ev.target as HTMLElement
+
+ if (
+ subjectEl &&
+ (!this.handleSelector || elementClosest(downEl, this.handleSelector))
+ ) {
+ this.subjectEl = subjectEl
+ this.isDragging = true // do this first so cancelTouchScroll will work
+ this.wasTouchScroll = false
+
+ return true
+ }
+
+ return false
+ }
+
+ cleanup() {
+ isWindowTouchMoveCancelled = false
+ this.isDragging = false
+ this.subjectEl = null
+ // keep wasTouchScroll around for later access
+ this.destroyScrollWatch()
+ }
+
+ querySubjectEl(ev: UIEvent): HTMLElement {
+ if (this.selector) {
+ return elementClosest(ev.target as HTMLElement, this.selector)
+ }
+ return this.containerEl as HTMLElement
+ }
+
+ // Mouse
+ // ----------------------------------------------------------------------------------------------------
+
+ handleMouseDown = (ev: MouseEvent) => {
+ if (
+ !this.shouldIgnoreMouse() &&
+ isPrimaryMouseButton(ev) &&
+ this.tryStart(ev)
+ ) {
+ let pev = this.createEventFromMouse(ev, true)
+ this.emitter.trigger('pointerdown', pev)
+ this.initScrollWatch(pev)
+
+ if (!this.shouldIgnoreMove) {
+ document.addEventListener('mousemove', this.handleMouseMove)
+ }
+
+ document.addEventListener('mouseup', this.handleMouseUp)
+ }
+ }
+
+ handleMouseMove = (ev: MouseEvent) => {
+ let pev = this.createEventFromMouse(ev)
+ this.recordCoords(pev)
+ this.emitter.trigger('pointermove', pev)
+ }
+
+ handleMouseUp = (ev: MouseEvent) => {
+ document.removeEventListener('mousemove', this.handleMouseMove)
+ document.removeEventListener('mouseup', this.handleMouseUp)
+
+ this.emitter.trigger('pointerup', this.createEventFromMouse(ev))
+
+ this.cleanup() // call last so that pointerup has access to props
+ }
+
+ shouldIgnoreMouse() {
+ return ignoreMouseDepth || this.isTouchDragging
+ }
+
+ // Touch
+ // ----------------------------------------------------------------------------------------------------
+
+ handleTouchStart = (ev: TouchEvent) => {
+ if (this.tryStart(ev)) {
+ this.isTouchDragging = true
+
+ let pev = this.createEventFromTouch(ev, true)
+ this.emitter.trigger('pointerdown', pev)
+ this.initScrollWatch(pev)
+
+ // unlike mouse, need to attach to target, not document
+ // https://stackoverflow.com/a/45760014
+ let targetEl = ev.target as HTMLElement
+
+ if (!this.shouldIgnoreMove) {
+ targetEl.addEventListener('touchmove', this.handleTouchMove)
+ }
+
+ targetEl.addEventListener('touchend', this.handleTouchEnd)
+ targetEl.addEventListener('touchcancel', this.handleTouchEnd) // treat it as a touch end
+
+ // attach a handler to get called when ANY scroll action happens on the page.
+ // this was impossible to do with normal on/off because 'scroll' doesn't bubble.
+ // http://stackoverflow.com/a/32954565/96342
+ window.addEventListener(
+ 'scroll',
+ this.handleTouchScroll,
+ true, // useCapture
+ )
+ }
+ }
+
+ handleTouchMove = (ev: TouchEvent) => {
+ let pev = this.createEventFromTouch(ev)
+ this.recordCoords(pev)
+ this.emitter.trigger('pointermove', pev)
+ }
+
+ handleTouchEnd = (ev: TouchEvent) => {
+ if (this.isDragging) { // done to guard against touchend followed by touchcancel
+ let targetEl = ev.target as HTMLElement
+
+ targetEl.removeEventListener('touchmove', this.handleTouchMove)
+ targetEl.removeEventListener('touchend', this.handleTouchEnd)
+ targetEl.removeEventListener('touchcancel', this.handleTouchEnd)
+ window.removeEventListener('scroll', this.handleTouchScroll, true) // useCaptured=true
+
+ this.emitter.trigger('pointerup', this.createEventFromTouch(ev))
+
+ this.cleanup() // call last so that pointerup has access to props
+ this.isTouchDragging = false
+ startIgnoringMouse()
+ }
+ }
+
+ handleTouchScroll = () => {
+ this.wasTouchScroll = true
+ }
+
+ // can be called by user of this class, to cancel touch-based scrolling for the current drag
+ cancelTouchScroll() {
+ if (this.isDragging) {
+ isWindowTouchMoveCancelled = true
+ }
+ }
+
+ // Scrolling that simulates pointermoves
+ // ----------------------------------------------------------------------------------------------------
+
+ initScrollWatch(ev: PointerDragEvent) {
+ if (this.shouldWatchScroll) {
+ this.recordCoords(ev)
+ window.addEventListener('scroll', this.handleScroll, true) // useCapture=true
+ }
+ }
+
+ recordCoords(ev: PointerDragEvent) {
+ if (this.shouldWatchScroll) {
+ this.prevPageX = (ev as any).pageX
+ this.prevPageY = (ev as any).pageY
+ this.prevScrollX = window.pageXOffset
+ this.prevScrollY = window.pageYOffset
+ }
+ }
+
+ handleScroll = (ev: UIEvent) => {
+ if (!this.shouldIgnoreMove) {
+ let pageX = (window.pageXOffset - this.prevScrollX) + this.prevPageX
+ let pageY = (window.pageYOffset - this.prevScrollY) + this.prevPageY
+
+ this.emitter.trigger('pointermove', {
+ origEvent: ev,
+ isTouch: this.isTouchDragging,
+ subjectEl: this.subjectEl,
+ pageX,
+ pageY,
+ deltaX: pageX - this.origPageX,
+ deltaY: pageY - this.origPageY,
+ } as PointerDragEvent)
+ }
+ }
+
+ destroyScrollWatch() {
+ if (this.shouldWatchScroll) {
+ window.removeEventListener('scroll', this.handleScroll, true) // useCaptured=true
+ }
+ }
+
+ // Event Normalization
+ // ----------------------------------------------------------------------------------------------------
+
+ createEventFromMouse(ev: MouseEvent, isFirst?: boolean): PointerDragEvent {
+ let deltaX = 0
+ let deltaY = 0
+
+ // TODO: repeat code
+ if (isFirst) {
+ this.origPageX = ev.pageX
+ this.origPageY = ev.pageY
+ } else {
+ deltaX = ev.pageX - this.origPageX
+ deltaY = ev.pageY - this.origPageY
+ }
+
+ return {
+ origEvent: ev,
+ isTouch: false,
+ subjectEl: this.subjectEl,
+ pageX: ev.pageX,
+ pageY: ev.pageY,
+ deltaX,
+ deltaY,
+ }
+ }
+
+ createEventFromTouch(ev: TouchEvent, isFirst?: boolean): PointerDragEvent {
+ let touches = ev.touches
+ let pageX
+ let pageY
+ let deltaX = 0
+ let deltaY = 0
+
+ // if touch coords available, prefer,
+ // because FF would give bad ev.pageX ev.pageY
+ if (touches && touches.length) {
+ pageX = touches[0].pageX
+ pageY = touches[0].pageY
+ } else {
+ pageX = (ev as any).pageX
+ pageY = (ev as any).pageY
+ }
+
+ // TODO: repeat code
+ if (isFirst) {
+ this.origPageX = pageX
+ this.origPageY = pageY
+ } else {
+ deltaX = pageX - this.origPageX
+ deltaY = pageY - this.origPageY
+ }
+
+ return {
+ origEvent: ev,
+ isTouch: true,
+ subjectEl: this.subjectEl,
+ pageX,
+ pageY,
+ deltaX,
+ deltaY,
+ }
+ }
+}
+
+// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
+function isPrimaryMouseButton(ev: MouseEvent) {
+ return ev.button === 0 && !ev.ctrlKey
+}
+
+// Ignoring fake mouse events generated by touch
+// ----------------------------------------------------------------------------------------------------
+
+function startIgnoringMouse() { // can be made non-class function
+ ignoreMouseDepth += 1
+
+ setTimeout(() => {
+ ignoreMouseDepth -= 1
+ }, config.touchMouseIgnoreWait)
+}
+
+// We want to attach touchmove as early as possible for Safari
+// ----------------------------------------------------------------------------------------------------
+
+function listenerCreated() {
+ listenerCnt += 1
+
+ if (listenerCnt === 1) {
+ window.addEventListener('touchmove', onWindowTouchMove, { passive: false })
+ }
+}
+
+function listenerDestroyed() {
+ listenerCnt -= 1
+
+ if (!listenerCnt) {
+ window.removeEventListener('touchmove', onWindowTouchMove, { passive: false } as AddEventListenerOptions)
+ }
+}
+
+function onWindowTouchMove(ev: UIEvent) {
+ if (isWindowTouchMoveCancelled) {
+ ev.preventDefault()
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalDraggable.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalDraggable.ts
new file mode 100644
index 000000000..22aba108d
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalDraggable.ts
@@ -0,0 +1,70 @@
+import { BASE_OPTION_DEFAULTS, PointerDragEvent } from '@fullcalendar/common'
+import { FeaturefulElementDragging } from '../dnd/FeaturefulElementDragging'
+import { ExternalElementDragging, DragMetaGenerator } from './ExternalElementDragging'
+
+export interface ExternalDraggableSettings {
+ eventData?: DragMetaGenerator
+ itemSelector?: string
+ minDistance?: number
+ longPressDelay?: number
+ appendTo?: HTMLElement
+}
+
+/*
+Makes an element (that is *external* to any calendar) draggable.
+Can pass in data that determines how an event will be created when dropped onto a calendar.
+Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system.
+*/
+export class ExternalDraggable {
+ dragging: FeaturefulElementDragging
+ settings: ExternalDraggableSettings
+
+ constructor(el: HTMLElement, settings: ExternalDraggableSettings = {}) {
+ this.settings = settings
+
+ let dragging = this.dragging = new FeaturefulElementDragging(el)
+ dragging.touchScrollAllowed = false
+
+ if (settings.itemSelector != null) {
+ dragging.pointer.selector = settings.itemSelector
+ }
+
+ if (settings.appendTo != null) {
+ dragging.mirror.parentNode = settings.appendTo // TODO: write tests
+ }
+
+ dragging.emitter.on('pointerdown', this.handlePointerDown)
+ dragging.emitter.on('dragstart', this.handleDragStart)
+
+ new ExternalElementDragging(dragging, settings.eventData) // eslint-disable-line no-new
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ let { dragging } = this
+ let { minDistance, longPressDelay } = this.settings
+
+ dragging.minDistance =
+ minDistance != null ?
+ minDistance :
+ (ev.isTouch ? 0 : BASE_OPTION_DEFAULTS.eventDragMinDistance)
+
+ dragging.delay =
+ ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
+ (longPressDelay != null ? longPressDelay : BASE_OPTION_DEFAULTS.longPressDelay) :
+ 0
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ if (
+ ev.isTouch &&
+ this.dragging.delay &&
+ (ev.subjectEl as HTMLElement).classList.contains('fc-event')
+ ) {
+ this.dragging.mirror.getMirrorEl().classList.add('fc-event-selected')
+ }
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalElementDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalElementDragging.ts
new file mode 100644
index 000000000..95ac7e0c2
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ExternalElementDragging.ts
@@ -0,0 +1,268 @@
+import {
+ Hit,
+ interactionSettingsStore,
+ PointerDragEvent,
+ parseEventDef, createEventInstance, EventTuple,
+ createEmptyEventStore, eventTupleToStore,
+ config,
+ DateSpan, DatePointApi,
+ EventInteractionState,
+ DragMetaInput, DragMeta, parseDragMeta,
+ EventApi,
+ elementMatches,
+ enableCursor, disableCursor,
+ isInteractionValid,
+ ElementDragging,
+ ViewApi,
+ CalendarContext,
+ getDefaultEventEnd,
+ refineEventDef,
+} from '@fullcalendar/common'
+import { __assign } from 'tslib'
+import { HitDragging } from '../interactions/HitDragging'
+import { buildDatePointApiWithContext } from '../utils'
+
+export type DragMetaGenerator = DragMetaInput | ((el: HTMLElement) => DragMetaInput)
+
+export interface ExternalDropApi extends DatePointApi {
+ draggedEl: HTMLElement
+ jsEvent: UIEvent
+ view: ViewApi
+}
+
+/*
+Given an already instantiated draggable object for one-or-more elements,
+Interprets any dragging as an attempt to drag an events that lives outside
+of a calendar onto a calendar.
+*/
+export class ExternalElementDragging {
+ hitDragging: HitDragging
+ receivingContext: CalendarContext | null = null
+ droppableEvent: EventTuple | null = null // will exist for all drags, even if create:false
+ suppliedDragMeta: DragMetaGenerator | null = null
+ dragMeta: DragMeta | null = null
+
+ constructor(dragging: ElementDragging, suppliedDragMeta?: DragMetaGenerator) {
+ let hitDragging = this.hitDragging = new HitDragging(dragging, interactionSettingsStore)
+ hitDragging.requireInitial = false // will start outside of a component
+ hitDragging.emitter.on('dragstart', this.handleDragStart)
+ hitDragging.emitter.on('hitupdate', this.handleHitUpdate)
+ hitDragging.emitter.on('dragend', this.handleDragEnd)
+
+ this.suppliedDragMeta = suppliedDragMeta
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ this.dragMeta = this.buildDragMeta(ev.subjectEl as HTMLElement)
+ }
+
+ buildDragMeta(subjectEl: HTMLElement) {
+ if (typeof this.suppliedDragMeta === 'object') {
+ return parseDragMeta(this.suppliedDragMeta)
+ }
+ if (typeof this.suppliedDragMeta === 'function') {
+ return parseDragMeta(this.suppliedDragMeta(subjectEl))
+ }
+ return getDragMetaFromEl(subjectEl)
+ }
+
+ handleHitUpdate = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
+ let { dragging } = this.hitDragging
+ let receivingContext: CalendarContext | null = null
+ let droppableEvent: EventTuple | null = null
+ let isInvalid = false
+ let interaction: EventInteractionState = {
+ affectedEvents: createEmptyEventStore(),
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: this.dragMeta!.create,
+ }
+
+ if (hit) {
+ receivingContext = hit.context
+
+ if (this.canDropElOnCalendar(ev.subjectEl as HTMLElement, receivingContext)) {
+ droppableEvent = computeEventForDateSpan(
+ hit.dateSpan,
+ this.dragMeta!,
+ receivingContext,
+ )
+
+ interaction.mutatedEvents = eventTupleToStore(droppableEvent)
+ isInvalid = !isInteractionValid(interaction, hit.dateProfile, receivingContext)
+
+ if (isInvalid) {
+ interaction.mutatedEvents = createEmptyEventStore()
+ droppableEvent = null
+ }
+ }
+ }
+
+ this.displayDrag(receivingContext, interaction)
+
+ // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?)
+ // TODO: wish we could somehow wait for dispatch to guarantee render
+ dragging.setMirrorIsVisible(
+ isFinal || !droppableEvent || !document.querySelector('.fc-event-mirror'), // TODO: turn className into constant
+ // TODO: somehow query FullCalendars WITHIN shadow-roots for existing event-mirror els
+ )
+
+ if (!isInvalid) {
+ enableCursor()
+ } else {
+ disableCursor()
+ }
+
+ if (!isFinal) {
+ dragging.setMirrorNeedsRevert(!droppableEvent)
+
+ this.receivingContext = receivingContext
+ this.droppableEvent = droppableEvent
+ }
+ }
+
+ handleDragEnd = (pev: PointerDragEvent) => {
+ let { receivingContext, droppableEvent } = this
+
+ this.clearDrag()
+
+ if (receivingContext && droppableEvent) {
+ let finalHit = this.hitDragging.finalHit!
+ let finalView = finalHit.context.viewApi
+ let dragMeta = this.dragMeta!
+
+ receivingContext.emitter.trigger('drop', {
+ ...buildDatePointApiWithContext(finalHit.dateSpan, receivingContext),
+ draggedEl: pev.subjectEl as HTMLElement,
+ jsEvent: pev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: finalView,
+ })
+
+ if (dragMeta.create) {
+ let addingEvents = eventTupleToStore(droppableEvent)
+
+ receivingContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: addingEvents,
+ })
+
+ if (pev.isTouch) {
+ receivingContext.dispatch({
+ type: 'SELECT_EVENT',
+ eventInstanceId: droppableEvent.instance.instanceId,
+ })
+ }
+
+ // signal that an external event landed
+ receivingContext.emitter.trigger('eventReceive', {
+ event: new EventApi(
+ receivingContext,
+ droppableEvent.def,
+ droppableEvent.instance,
+ ),
+ relatedEvents: [],
+ revert() {
+ receivingContext.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: addingEvents,
+ })
+ },
+ draggedEl: pev.subjectEl as HTMLElement,
+ view: finalView,
+ })
+ }
+ }
+
+ this.receivingContext = null
+ this.droppableEvent = null
+ }
+
+ displayDrag(nextContext: CalendarContext | null, state: EventInteractionState) {
+ let prevContext = this.receivingContext
+
+ if (prevContext && prevContext !== nextContext) {
+ prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' })
+ }
+
+ if (nextContext) {
+ nextContext.dispatch({ type: 'SET_EVENT_DRAG', state })
+ }
+ }
+
+ clearDrag() {
+ if (this.receivingContext) {
+ this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' })
+ }
+ }
+
+ canDropElOnCalendar(el: HTMLElement, receivingContext: CalendarContext): boolean {
+ let dropAccept = receivingContext.options.dropAccept
+
+ if (typeof dropAccept === 'function') {
+ return dropAccept.call(receivingContext.calendarApi, el)
+ }
+
+ if (typeof dropAccept === 'string' && dropAccept) {
+ return Boolean(elementMatches(el, dropAccept))
+ }
+
+ return true
+ }
+}
+
+// Utils for computing event store from the DragMeta
+// ----------------------------------------------------------------------------------------------------
+
+function computeEventForDateSpan(dateSpan: DateSpan, dragMeta: DragMeta, context: CalendarContext): EventTuple {
+ let defProps = { ...dragMeta.leftoverProps }
+
+ for (let transform of context.pluginHooks.externalDefTransforms) {
+ __assign(defProps, transform(dateSpan, dragMeta))
+ }
+
+ let { refined, extra } = refineEventDef(defProps, context)
+ let def = parseEventDef(
+ refined,
+ extra,
+ dragMeta.sourceId,
+ dateSpan.allDay,
+ context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd
+ context,
+ )
+
+ let start = dateSpan.range.start
+
+ // only rely on time info if drop zone is all-day,
+ // otherwise, we already know the time
+ if (dateSpan.allDay && dragMeta.startTime) {
+ start = context.dateEnv.add(start, dragMeta.startTime)
+ }
+
+ let end = dragMeta.duration ?
+ context.dateEnv.add(start, dragMeta.duration) :
+ getDefaultEventEnd(dateSpan.allDay, start, context)
+
+ let instance = createEventInstance(def.defId, { start, end })
+
+ return { def, instance }
+}
+
+// Utils for extracting data from element
+// ----------------------------------------------------------------------------------------------------
+
+function getDragMetaFromEl(el: HTMLElement): DragMeta {
+ let str = getEmbeddedElData(el, 'event')
+ let obj = str ?
+ JSON.parse(str) :
+ { create: false } // if no embedded data, assume no event creation
+
+ return parseDragMeta(obj)
+}
+
+config.dataAttrPrefix = ''
+
+function getEmbeddedElData(el: HTMLElement, name: string): string {
+ let prefix = config.dataAttrPrefix
+ let prefixedName = (prefix ? prefix + '-' : '') + name
+
+ return el.getAttribute('data-' + prefixedName) || ''
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/InferredElementDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/InferredElementDragging.ts
new file mode 100644
index 000000000..ab03cec00
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/InferredElementDragging.ts
@@ -0,0 +1,77 @@
+import { PointerDragEvent, ElementDragging } from '@fullcalendar/common'
+import { PointerDragging } from '../dnd/PointerDragging'
+
+/*
+Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements.
+The third-party system is responsible for drawing the visuals effects of the drag.
+This class simply monitors for pointer movements and fires events.
+It also has the ability to hide the moving element (the "mirror") during the drag.
+*/
+export class InferredElementDragging extends ElementDragging {
+ pointer: PointerDragging
+ shouldIgnoreMove: boolean = false
+ mirrorSelector: string = ''
+ currentMirrorEl: HTMLElement | null = null
+
+ constructor(containerEl: HTMLElement) {
+ super(containerEl)
+
+ let pointer = this.pointer = new PointerDragging(containerEl)
+ pointer.emitter.on('pointerdown', this.handlePointerDown)
+ pointer.emitter.on('pointermove', this.handlePointerMove)
+ pointer.emitter.on('pointerup', this.handlePointerUp)
+ }
+
+ destroy() {
+ this.pointer.destroy()
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ this.emitter.trigger('pointerdown', ev)
+
+ if (!this.shouldIgnoreMove) {
+ // fire dragstart right away. does not support delay or min-distance
+ this.emitter.trigger('dragstart', ev)
+ }
+ }
+
+ handlePointerMove = (ev: PointerDragEvent) => {
+ if (!this.shouldIgnoreMove) {
+ this.emitter.trigger('dragmove', ev)
+ }
+ }
+
+ handlePointerUp = (ev: PointerDragEvent) => {
+ this.emitter.trigger('pointerup', ev)
+
+ if (!this.shouldIgnoreMove) {
+ // fire dragend right away. does not support a revert animation
+ this.emitter.trigger('dragend', ev)
+ }
+ }
+
+ setIgnoreMove(bool: boolean) {
+ this.shouldIgnoreMove = bool
+ }
+
+ setMirrorIsVisible(bool: boolean) {
+ if (bool) {
+ // restore a previously hidden element.
+ // use the reference in case the selector class has already been removed.
+ if (this.currentMirrorEl) {
+ this.currentMirrorEl.style.visibility = ''
+ this.currentMirrorEl = null
+ }
+ } else {
+ let mirrorEl = this.mirrorSelector
+ // TODO: somehow query FullCalendars WITHIN shadow-roots
+ ? document.querySelector(this.mirrorSelector) as HTMLElement
+ : null
+
+ if (mirrorEl) {
+ this.currentMirrorEl = mirrorEl
+ mirrorEl.style.visibility = 'hidden'
+ }
+ }
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ThirdPartyDraggable.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ThirdPartyDraggable.ts
new file mode 100644
index 000000000..324b22255
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions-external/ThirdPartyDraggable.ts
@@ -0,0 +1,52 @@
+import { ExternalElementDragging, DragMetaGenerator } from './ExternalElementDragging'
+import { InferredElementDragging } from './InferredElementDragging'
+
+export interface ThirdPartyDraggableSettings {
+ eventData?: DragMetaGenerator
+ itemSelector?: string
+ mirrorSelector?: string
+}
+
+/*
+Bridges third-party drag-n-drop systems with FullCalendar.
+Must be instantiated and destroyed by caller.
+*/
+export class ThirdPartyDraggable {
+ dragging: InferredElementDragging
+
+ constructor(
+ containerOrSettings?: EventTarget | ThirdPartyDraggableSettings,
+ settings?: ThirdPartyDraggableSettings,
+ ) {
+ let containerEl: EventTarget = document
+
+ if (
+ // wish we could just test instanceof EventTarget, but doesn't work in IE11
+ containerOrSettings === document ||
+ containerOrSettings instanceof Element
+ ) {
+ containerEl = containerOrSettings as EventTarget
+ settings = settings || {}
+ } else {
+ settings = (containerOrSettings || {}) as ThirdPartyDraggableSettings
+ }
+
+ let dragging = this.dragging = new InferredElementDragging(containerEl as HTMLElement)
+
+ if (typeof settings.itemSelector === 'string') {
+ dragging.pointer.selector = settings.itemSelector
+ } else if (containerEl === document) {
+ dragging.pointer.selector = '[data-event]'
+ }
+
+ if (typeof settings.mirrorSelector === 'string') {
+ dragging.mirrorSelector = settings.mirrorSelector
+ }
+
+ new ExternalElementDragging(dragging, settings.eventData) // eslint-disable-line no-new
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateClicking.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateClicking.ts
new file mode 100644
index 000000000..06b352de9
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateClicking.ts
@@ -0,0 +1,71 @@
+import {
+ PointerDragEvent, Interaction, InteractionSettings, interactionSettingsToStore,
+ DatePointApi,
+ ViewApi,
+} from '@fullcalendar/common'
+import { FeaturefulElementDragging } from '../dnd/FeaturefulElementDragging'
+import { HitDragging, isHitsEqual } from './HitDragging'
+import { buildDatePointApiWithContext } from '../utils'
+
+export interface DateClickArg extends DatePointApi {
+ dayEl: HTMLElement
+ jsEvent: MouseEvent
+ view: ViewApi
+}
+
+/*
+Monitors when the user clicks on a specific date/time of a component.
+A pointerdown+pointerup on the same "hit" constitutes a click.
+*/
+export class DateClicking extends Interaction {
+ dragging: FeaturefulElementDragging
+ hitDragging: HitDragging
+
+ constructor(settings: InteractionSettings) {
+ super(settings)
+
+ // we DO want to watch pointer moves because otherwise finalHit won't get populated
+ this.dragging = new FeaturefulElementDragging(settings.el)
+ this.dragging.autoScroller.isEnabled = false
+
+ let hitDragging = this.hitDragging = new HitDragging(this.dragging, interactionSettingsToStore(settings))
+ hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+ hitDragging.emitter.on('dragend', this.handleDragEnd)
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+
+ handlePointerDown = (pev: PointerDragEvent) => {
+ let { dragging } = this
+ let downEl = pev.origEvent.target as HTMLElement
+
+ // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
+ dragging.setIgnoreMove(
+ !this.component.isValidDateDownEl(downEl),
+ )
+ }
+
+ // won't even fire if moving was ignored
+ handleDragEnd = (ev: PointerDragEvent) => {
+ let { component } = this
+ let { pointer } = this.dragging
+
+ if (!pointer.wasTouchScroll) {
+ let { initialHit, finalHit } = this.hitDragging
+
+ if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
+ let { context } = component
+ let arg: DateClickArg = {
+ ...buildDatePointApiWithContext(initialHit.dateSpan, context),
+ dayEl: initialHit.dayEl,
+ jsEvent: ev.origEvent as MouseEvent,
+ view: context.viewApi || context.calendarApi.view,
+ }
+
+ context.emitter.trigger('dateClick', arg)
+ }
+ }
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateSelecting.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateSelecting.ts
new file mode 100644
index 000000000..fa6b3ff09
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/DateSelecting.ts
@@ -0,0 +1,152 @@
+import {
+ compareNumbers, enableCursor, disableCursor, DateComponent, Hit,
+ DateSpan, PointerDragEvent, dateSelectionJoinTransformer,
+ Interaction, InteractionSettings, interactionSettingsToStore,
+ triggerDateSelect, isDateSelectionValid,
+} from '@fullcalendar/common'
+import { __assign } from 'tslib'
+import { HitDragging } from './HitDragging'
+import { FeaturefulElementDragging } from '../dnd/FeaturefulElementDragging'
+
+/*
+Tracks when the user selects a portion of time of a component,
+constituted by a drag over date cells, with a possible delay at the beginning of the drag.
+*/
+export class DateSelecting extends Interaction {
+ dragging: FeaturefulElementDragging
+ hitDragging: HitDragging
+ dragSelection: DateSpan | null = null
+
+ constructor(settings: InteractionSettings) {
+ super(settings)
+ let { component } = settings
+ let { options } = component.context
+
+ let dragging = this.dragging = new FeaturefulElementDragging(settings.el)
+ dragging.touchScrollAllowed = false
+ dragging.minDistance = options.selectMinDistance || 0
+ dragging.autoScroller.isEnabled = options.dragScroll
+
+ let hitDragging = this.hitDragging = new HitDragging(this.dragging, interactionSettingsToStore(settings))
+ hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+ hitDragging.emitter.on('dragstart', this.handleDragStart)
+ hitDragging.emitter.on('hitupdate', this.handleHitUpdate)
+ hitDragging.emitter.on('pointerup', this.handlePointerUp)
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ let { component, dragging } = this
+ let { options } = component.context
+
+ let canSelect = options.selectable &&
+ component.isValidDateDownEl(ev.origEvent.target as HTMLElement)
+
+ // don't bother to watch expensive moves if component won't do selection
+ dragging.setIgnoreMove(!canSelect)
+
+ // if touch, require user to hold down
+ dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ this.component.context.calendarApi.unselect(ev) // unselect previous selections
+ }
+
+ handleHitUpdate = (hit: Hit | null, isFinal: boolean) => {
+ let { context } = this.component
+ let dragSelection: DateSpan | null = null
+ let isInvalid = false
+
+ if (hit) {
+ let initialHit = this.hitDragging.initialHit!
+ let disallowed = hit.componentId === initialHit.componentId
+ && this.isHitComboAllowed
+ && !this.isHitComboAllowed(initialHit, hit)
+
+ if (!disallowed) {
+ dragSelection = joinHitsIntoSelection(
+ initialHit,
+ hit,
+ context.pluginHooks.dateSelectionTransformers,
+ )
+ }
+
+ if (!dragSelection || !isDateSelectionValid(dragSelection, hit.dateProfile, context)) {
+ isInvalid = true
+ dragSelection = null
+ }
+ }
+
+ if (dragSelection) {
+ context.dispatch({ type: 'SELECT_DATES', selection: dragSelection })
+ } else if (!isFinal) { // only unselect if moved away while dragging
+ context.dispatch({ type: 'UNSELECT_DATES' })
+ }
+
+ if (!isInvalid) {
+ enableCursor()
+ } else {
+ disableCursor()
+ }
+
+ if (!isFinal) {
+ this.dragSelection = dragSelection // only clear if moved away from all hits while dragging
+ }
+ }
+
+ handlePointerUp = (pev: PointerDragEvent) => {
+ if (this.dragSelection) {
+ // selection is already rendered, so just need to report selection
+ triggerDateSelect(this.dragSelection, pev, this.component.context)
+
+ this.dragSelection = null
+ }
+ }
+}
+
+function getComponentTouchDelay(component: DateComponent): number {
+ let { options } = component.context
+ let delay = options.selectLongPressDelay
+
+ if (delay == null) {
+ delay = options.longPressDelay
+ }
+
+ return delay
+}
+
+function joinHitsIntoSelection(hit0: Hit, hit1: Hit, dateSelectionTransformers: dateSelectionJoinTransformer[]): DateSpan {
+ let dateSpan0 = hit0.dateSpan
+ let dateSpan1 = hit1.dateSpan
+ let ms = [
+ dateSpan0.range.start,
+ dateSpan0.range.end,
+ dateSpan1.range.start,
+ dateSpan1.range.end,
+ ]
+
+ ms.sort(compareNumbers)
+
+ let props = {} as DateSpan
+
+ for (let transformer of dateSelectionTransformers) {
+ let res = transformer(hit0, hit1)
+
+ if (res === false) {
+ return null
+ }
+
+ if (res) {
+ __assign(props, res)
+ }
+ }
+
+ props.range = { start: ms[0], end: ms[3] }
+ props.allDay = dateSpan0.allDay
+
+ return props
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventDragging.ts
new file mode 100644
index 000000000..9a9ecfc49
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventDragging.ts
@@ -0,0 +1,482 @@
+import {
+ DateComponent, Seg,
+ PointerDragEvent, Hit,
+ EventMutation, applyMutationToEventStore,
+ startOfDay,
+ elementClosest,
+ EventStore, getRelevantEvents, createEmptyEventStore,
+ EventInteractionState,
+ diffDates, enableCursor, disableCursor,
+ EventRenderRange, getElSeg,
+ EventApi,
+ eventDragMutationMassager,
+ Interaction, InteractionSettings, interactionSettingsStore,
+ EventDropTransformers,
+ CalendarContext,
+ ViewApi,
+ EventChangeArg,
+ buildEventApis,
+ EventAddArg,
+ EventRemoveArg,
+ isInteractionValid,
+ getElRoot,
+} from '@fullcalendar/common'
+import { __assign } from 'tslib'
+import { HitDragging, isHitsEqual } from './HitDragging'
+import { FeaturefulElementDragging } from '../dnd/FeaturefulElementDragging'
+import { buildDatePointApiWithContext } from '../utils'
+
+export type EventDragStopArg = EventDragArg
+export type EventDragStartArg = EventDragArg
+
+export interface EventDragArg {
+ el: HTMLElement
+ event: EventApi
+ jsEvent: MouseEvent
+ view: ViewApi
+}
+
+export class EventDragging extends Interaction { // TODO: rename to EventSelectingAndDragging
+ // TODO: test this in IE11
+ // QUESTION: why do we need it on the resizable???
+ static SELECTOR = '.fc-event-draggable, .fc-event-resizable'
+
+ dragging: FeaturefulElementDragging
+ hitDragging: HitDragging
+
+ // internal state
+ subjectEl: HTMLElement | null = null
+ subjectSeg: Seg | null = null // the seg being selected/dragged
+ isDragging: boolean = false
+ eventRange: EventRenderRange | null = null
+ relevantEvents: EventStore | null = null // the events being dragged
+ receivingContext: CalendarContext | null = null
+ validMutation: EventMutation | null = null
+ mutatedRelevantEvents: EventStore | null = null
+
+ constructor(settings: InteractionSettings) {
+ super(settings)
+ let { component } = this
+ let { options } = component.context
+
+ let dragging = this.dragging = new FeaturefulElementDragging(settings.el)
+ dragging.pointer.selector = EventDragging.SELECTOR
+ dragging.touchScrollAllowed = false
+ dragging.autoScroller.isEnabled = options.dragScroll
+
+ let hitDragging = this.hitDragging = new HitDragging(this.dragging, interactionSettingsStore)
+ hitDragging.useSubjectCenter = settings.useEventCenter
+ hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+ hitDragging.emitter.on('dragstart', this.handleDragStart)
+ hitDragging.emitter.on('hitupdate', this.handleHitUpdate)
+ hitDragging.emitter.on('pointerup', this.handlePointerUp)
+ hitDragging.emitter.on('dragend', this.handleDragEnd)
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ let origTarget = ev.origEvent.target as HTMLElement
+ let { component, dragging } = this
+ let { mirror } = dragging
+ let { options } = component.context
+ let initialContext = component.context
+ this.subjectEl = ev.subjectEl as HTMLElement
+ let subjectSeg = this.subjectSeg = getElSeg(ev.subjectEl as HTMLElement)!
+ let eventRange = this.eventRange = subjectSeg.eventRange!
+ let eventInstanceId = eventRange.instance!.instanceId
+
+ this.relevantEvents = getRelevantEvents(
+ initialContext.getCurrentData().eventStore,
+ eventInstanceId,
+ )
+
+ dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance
+ dragging.delay =
+ // only do a touch delay if touch and this event hasn't been selected yet
+ (ev.isTouch && eventInstanceId !== component.props.eventSelection) ?
+ getComponentTouchDelay(component) :
+ null
+
+ if (options.fixedMirrorParent) {
+ mirror.parentNode = options.fixedMirrorParent
+ } else {
+ mirror.parentNode = elementClosest(origTarget, '.fc')
+ }
+
+ mirror.revertDuration = options.dragRevertDuration
+
+ let isValid =
+ component.isValidSegDownEl(origTarget) &&
+ !elementClosest(origTarget, '.fc-event-resizer') // NOT on a resizer
+
+ dragging.setIgnoreMove(!isValid)
+
+ // disable dragging for elements that are resizable (ie, selectable)
+ // but are not draggable
+ this.isDragging = isValid &&
+ (ev.subjectEl as HTMLElement).classList.contains('fc-event-draggable')
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ let initialContext = this.component.context
+ let eventRange = this.eventRange!
+ let eventInstanceId = eventRange.instance.instanceId
+
+ if (ev.isTouch) {
+ // need to select a different event?
+ if (eventInstanceId !== this.component.props.eventSelection) {
+ initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId })
+ }
+ } else {
+ // if now using mouse, but was previous touch interaction, clear selected event
+ initialContext.dispatch({ type: 'UNSELECT_EVENT' })
+ }
+
+ if (this.isDragging) {
+ initialContext.calendarApi.unselect(ev) // unselect *date* selection
+ initialContext.emitter.trigger('eventDragStart', {
+ el: this.subjectEl,
+ event: new EventApi(initialContext, eventRange.def, eventRange.instance),
+ jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: initialContext.viewApi,
+ } as EventDragStartArg)
+ }
+ }
+
+ handleHitUpdate = (hit: Hit | null, isFinal: boolean) => {
+ if (!this.isDragging) {
+ return
+ }
+
+ let relevantEvents = this.relevantEvents!
+ let initialHit = this.hitDragging.initialHit!
+ let initialContext = this.component.context
+
+ // states based on new hit
+ let receivingContext: CalendarContext | null = null
+ let mutation: EventMutation | null = null
+ let mutatedRelevantEvents: EventStore | null = null
+ let isInvalid = false
+ let interaction: EventInteractionState = {
+ affectedEvents: relevantEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ }
+
+ if (hit) {
+ receivingContext = hit.context
+ let receivingOptions = receivingContext.options
+
+ if (
+ initialContext === receivingContext ||
+ (receivingOptions.editable && receivingOptions.droppable)
+ ) {
+ mutation = computeEventMutation(initialHit, hit, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers)
+
+ if (mutation) {
+ mutatedRelevantEvents = applyMutationToEventStore(
+ relevantEvents,
+ receivingContext.getCurrentData().eventUiBases,
+ mutation,
+ receivingContext,
+ )
+ interaction.mutatedEvents = mutatedRelevantEvents
+
+ if (!isInteractionValid(interaction, hit.dateProfile, receivingContext)) {
+ isInvalid = true
+ mutation = null
+ mutatedRelevantEvents = null
+ interaction.mutatedEvents = createEmptyEventStore()
+ }
+ }
+ } else {
+ receivingContext = null
+ }
+ }
+
+ this.displayDrag(receivingContext, interaction)
+
+ if (!isInvalid) {
+ enableCursor()
+ } else {
+ disableCursor()
+ }
+
+ if (!isFinal) {
+ if (
+ initialContext === receivingContext && // TODO: write test for this
+ isHitsEqual(initialHit, hit)
+ ) {
+ mutation = null
+ }
+
+ this.dragging.setMirrorNeedsRevert(!mutation)
+
+ // render the mirror if no already-rendered mirror
+ // TODO: wish we could somehow wait for dispatch to guarantee render
+ this.dragging.setMirrorIsVisible(
+ !hit || !getElRoot(this.subjectEl).querySelector('.fc-event-mirror'), // TODO: turn className into constant
+ )
+
+ // assign states based on new hit
+ this.receivingContext = receivingContext
+ this.validMutation = mutation
+ this.mutatedRelevantEvents = mutatedRelevantEvents
+ }
+ }
+
+ handlePointerUp = () => {
+ if (!this.isDragging) {
+ this.cleanup() // because handleDragEnd won't fire
+ }
+ }
+
+ handleDragEnd = (ev: PointerDragEvent) => {
+ if (this.isDragging) {
+ let initialContext = this.component.context
+ let initialView = initialContext.viewApi
+ let { receivingContext, validMutation } = this
+ let eventDef = this.eventRange!.def
+ let eventInstance = this.eventRange!.instance
+ let eventApi = new EventApi(initialContext, eventDef, eventInstance)
+ let relevantEvents = this.relevantEvents!
+ let mutatedRelevantEvents = this.mutatedRelevantEvents!
+ let { finalHit } = this.hitDragging
+
+ this.clearDrag() // must happen after revert animation
+
+ initialContext.emitter.trigger('eventDragStop', {
+ el: this.subjectEl,
+ event: eventApi,
+ jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: initialView,
+ } as EventDragStopArg)
+
+ if (validMutation) {
+ // dropped within same calendar
+ if (receivingContext === initialContext) {
+ let updatedEventApi = new EventApi(
+ initialContext,
+ mutatedRelevantEvents.defs[eventDef.defId],
+ eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null,
+ )
+
+ initialContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents,
+ })
+
+ let eventChangeArg: EventChangeArg = {
+ oldEvent: eventApi,
+ event: updatedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents, initialContext, eventInstance),
+ revert() {
+ initialContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents, // the pre-change data
+ })
+ },
+ }
+
+ let transformed: ReturnType = {}
+ for (let transformer of initialContext.getCurrentData().pluginHooks.eventDropTransformers) {
+ __assign(transformed, transformer(validMutation, initialContext))
+ }
+
+ initialContext.emitter.trigger('eventDrop', {
+ ...eventChangeArg,
+ ...transformed,
+ el: ev.subjectEl as HTMLElement,
+ delta: validMutation.datesDelta!,
+ jsEvent: ev.origEvent as MouseEvent, // bad
+ view: initialView,
+ })
+
+ initialContext.emitter.trigger('eventChange', eventChangeArg)
+
+ // dropped in different calendar
+ } else if (receivingContext) {
+ let eventRemoveArg: EventRemoveArg = {
+ event: eventApi,
+ relatedEvents: buildEventApis(relevantEvents, initialContext, eventInstance),
+ revert() {
+ initialContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents,
+ })
+ },
+ }
+
+ initialContext.emitter.trigger('eventLeave', {
+ ...eventRemoveArg,
+ draggedEl: ev.subjectEl as HTMLElement,
+ view: initialView,
+ })
+
+ initialContext.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: relevantEvents,
+ })
+
+ initialContext.emitter.trigger('eventRemove', eventRemoveArg)
+
+ let addedEventDef = mutatedRelevantEvents.defs[eventDef.defId]
+ let addedEventInstance = mutatedRelevantEvents.instances[eventInstance.instanceId]
+ let addedEventApi = new EventApi(receivingContext, addedEventDef, addedEventInstance)
+
+ receivingContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents,
+ })
+
+ let eventAddArg: EventAddArg = {
+ event: addedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents, receivingContext, addedEventInstance),
+ revert() {
+ receivingContext.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: mutatedRelevantEvents,
+ })
+ },
+ }
+
+ receivingContext.emitter.trigger('eventAdd', eventAddArg)
+
+ if (ev.isTouch) {
+ receivingContext.dispatch({
+ type: 'SELECT_EVENT',
+ eventInstanceId: eventInstance.instanceId,
+ })
+ }
+
+ receivingContext.emitter.trigger('drop', {
+ ...buildDatePointApiWithContext(finalHit.dateSpan, receivingContext),
+ draggedEl: ev.subjectEl as HTMLElement,
+ jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: finalHit.context.viewApi,
+ })
+
+ receivingContext.emitter.trigger('eventReceive', {
+ ...eventAddArg,
+ draggedEl: ev.subjectEl as HTMLElement,
+ view: finalHit.context.viewApi,
+ })
+ }
+ } else {
+ initialContext.emitter.trigger('_noEventDrop')
+ }
+ }
+
+ this.cleanup()
+ }
+
+ // render a drag state on the next receivingCalendar
+ displayDrag(nextContext: CalendarContext | null, state: EventInteractionState) {
+ let initialContext = this.component.context
+ let prevContext = this.receivingContext
+
+ // does the previous calendar need to be cleared?
+ if (prevContext && prevContext !== nextContext) {
+ // does the initial calendar need to be cleared?
+ // if so, don't clear all the way. we still need to to hide the affectedEvents
+ if (prevContext === initialContext) {
+ prevContext.dispatch({
+ type: 'SET_EVENT_DRAG',
+ state: {
+ affectedEvents: state.affectedEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ },
+ })
+
+ // completely clear the old calendar if it wasn't the initial
+ } else {
+ prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' })
+ }
+ }
+
+ if (nextContext) {
+ nextContext.dispatch({ type: 'SET_EVENT_DRAG', state })
+ }
+ }
+
+ clearDrag() {
+ let initialCalendar = this.component.context
+ let { receivingContext } = this
+
+ if (receivingContext) {
+ receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' })
+ }
+
+ // the initial calendar might have an dummy drag state from displayDrag
+ if (initialCalendar !== receivingContext) {
+ initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' })
+ }
+ }
+
+ cleanup() { // reset all internal state
+ this.subjectSeg = null
+ this.isDragging = false
+ this.eventRange = null
+ this.relevantEvents = null
+ this.receivingContext = null
+ this.validMutation = null
+ this.mutatedRelevantEvents = null
+ }
+}
+
+function computeEventMutation(hit0: Hit, hit1: Hit, massagers: eventDragMutationMassager[]): EventMutation {
+ let dateSpan0 = hit0.dateSpan
+ let dateSpan1 = hit1.dateSpan
+ let date0 = dateSpan0.range.start
+ let date1 = dateSpan1.range.start
+ let standardProps = {} as any
+
+ if (dateSpan0.allDay !== dateSpan1.allDay) {
+ standardProps.allDay = dateSpan1.allDay
+ standardProps.hasEnd = hit1.context.options.allDayMaintainDuration
+
+ if (dateSpan1.allDay) {
+ // means date1 is already start-of-day,
+ // but date0 needs to be converted
+ date0 = startOfDay(date0)
+ }
+ }
+
+ let delta = diffDates(
+ date0, date1,
+ hit0.context.dateEnv,
+ hit0.componentId === hit1.componentId ?
+ hit0.largeUnit :
+ null,
+ )
+
+ if (delta.milliseconds) { // has hours/minutes/seconds
+ standardProps.allDay = false
+ }
+
+ let mutation: EventMutation = {
+ datesDelta: delta,
+ standardProps,
+ }
+
+ for (let massager of massagers) {
+ massager(mutation, hit0, hit1)
+ }
+
+ return mutation
+}
+
+function getComponentTouchDelay(component: DateComponent): number | null {
+ let { options } = component.context
+ let delay = options.eventLongPressDelay
+
+ if (delay == null) {
+ delay = options.longPressDelay
+ }
+
+ return delay
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventResizing.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventResizing.ts
new file mode 100644
index 000000000..013b399ae
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/EventResizing.ts
@@ -0,0 +1,263 @@
+import {
+ Seg, Hit,
+ EventMutation, applyMutationToEventStore,
+ elementClosest,
+ PointerDragEvent,
+ EventStore, getRelevantEvents, createEmptyEventStore,
+ diffDates, enableCursor, disableCursor,
+ DateRange,
+ EventApi,
+ EventRenderRange, getElSeg,
+ createDuration,
+ EventInteractionState,
+ Interaction, InteractionSettings, interactionSettingsToStore, ViewApi, Duration, EventChangeArg, buildEventApis, isInteractionValid,
+} from '@fullcalendar/common'
+import { __assign } from 'tslib'
+import { HitDragging, isHitsEqual } from './HitDragging'
+import { FeaturefulElementDragging } from '../dnd/FeaturefulElementDragging'
+
+export type EventResizeStartArg = EventResizeStartStopArg
+export type EventResizeStopArg = EventResizeStartStopArg
+
+export interface EventResizeStartStopArg {
+ el: HTMLElement
+ event: EventApi
+ jsEvent: MouseEvent
+ view: ViewApi
+}
+
+export interface EventResizeDoneArg extends EventChangeArg {
+ el: HTMLElement
+ startDelta: Duration
+ endDelta: Duration
+ jsEvent: MouseEvent
+ view: ViewApi
+}
+
+export class EventResizing extends Interaction {
+ dragging: FeaturefulElementDragging
+ hitDragging: HitDragging
+
+ // internal state
+ draggingSegEl: HTMLElement | null = null
+ draggingSeg: Seg | null = null // TODO: rename to resizingSeg? subjectSeg?
+ eventRange: EventRenderRange | null = null
+ relevantEvents: EventStore | null = null
+ validMutation: EventMutation | null = null
+ mutatedRelevantEvents: EventStore | null = null
+
+ constructor(settings: InteractionSettings) {
+ super(settings)
+ let { component } = settings
+
+ let dragging = this.dragging = new FeaturefulElementDragging(settings.el)
+ dragging.pointer.selector = '.fc-event-resizer'
+ dragging.touchScrollAllowed = false
+ dragging.autoScroller.isEnabled = component.context.options.dragScroll
+
+ let hitDragging = this.hitDragging = new HitDragging(this.dragging, interactionSettingsToStore(settings))
+ hitDragging.emitter.on('pointerdown', this.handlePointerDown)
+ hitDragging.emitter.on('dragstart', this.handleDragStart)
+ hitDragging.emitter.on('hitupdate', this.handleHitUpdate)
+ hitDragging.emitter.on('dragend', this.handleDragEnd)
+ }
+
+ destroy() {
+ this.dragging.destroy()
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ let { component } = this
+ let segEl = this.querySegEl(ev)
+ let seg = getElSeg(segEl)
+ let eventRange = this.eventRange = seg.eventRange!
+
+ this.dragging.minDistance = component.context.options.eventDragMinDistance
+
+ // if touch, need to be working with a selected event
+ this.dragging.setIgnoreMove(
+ !this.component.isValidSegDownEl(ev.origEvent.target as HTMLElement) ||
+ (ev.isTouch && this.component.props.eventSelection !== eventRange.instance!.instanceId),
+ )
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ let { context } = this.component
+ let eventRange = this.eventRange!
+
+ this.relevantEvents = getRelevantEvents(
+ context.getCurrentData().eventStore,
+ this.eventRange.instance!.instanceId,
+ )
+
+ let segEl = this.querySegEl(ev)
+ this.draggingSegEl = segEl
+ this.draggingSeg = getElSeg(segEl)
+
+ context.calendarApi.unselect()
+ context.emitter.trigger('eventResizeStart', {
+ el: segEl,
+ event: new EventApi(context, eventRange.def, eventRange.instance),
+ jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: context.viewApi,
+ } as EventResizeStartArg)
+ }
+
+ handleHitUpdate = (hit: Hit | null, isFinal: boolean, ev: PointerDragEvent) => {
+ let { context } = this.component
+ let relevantEvents = this.relevantEvents!
+ let initialHit = this.hitDragging.initialHit!
+ let eventInstance = this.eventRange.instance!
+ let mutation: EventMutation | null = null
+ let mutatedRelevantEvents: EventStore | null = null
+ let isInvalid = false
+ let interaction: EventInteractionState = {
+ affectedEvents: relevantEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ }
+
+ if (hit) {
+ let disallowed = hit.componentId === initialHit.componentId
+ && this.isHitComboAllowed
+ && !this.isHitComboAllowed(initialHit, hit)
+
+ if (!disallowed) {
+ mutation = computeMutation(
+ initialHit,
+ hit,
+ (ev.subjectEl as HTMLElement).classList.contains('fc-event-resizer-start'),
+ eventInstance.range,
+ )
+ }
+ }
+
+ if (mutation) {
+ mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context)
+ interaction.mutatedEvents = mutatedRelevantEvents
+
+ if (!isInteractionValid(interaction, hit.dateProfile, context)) {
+ isInvalid = true
+ mutation = null
+ mutatedRelevantEvents = null
+ interaction.mutatedEvents = null
+ }
+ }
+
+ if (mutatedRelevantEvents) {
+ context.dispatch({
+ type: 'SET_EVENT_RESIZE',
+ state: interaction,
+ })
+ } else {
+ context.dispatch({ type: 'UNSET_EVENT_RESIZE' })
+ }
+
+ if (!isInvalid) {
+ enableCursor()
+ } else {
+ disableCursor()
+ }
+
+ if (!isFinal) {
+ if (mutation && isHitsEqual(initialHit, hit)) {
+ mutation = null
+ }
+
+ this.validMutation = mutation
+ this.mutatedRelevantEvents = mutatedRelevantEvents
+ }
+ }
+
+ handleDragEnd = (ev: PointerDragEvent) => {
+ let { context } = this.component
+ let eventDef = this.eventRange!.def
+ let eventInstance = this.eventRange!.instance
+ let eventApi = new EventApi(context, eventDef, eventInstance)
+ let relevantEvents = this.relevantEvents!
+ let mutatedRelevantEvents = this.mutatedRelevantEvents!
+
+ context.emitter.trigger('eventResizeStop', {
+ el: this.draggingSegEl,
+ event: eventApi,
+ jsEvent: ev.origEvent as MouseEvent, // Is this always a mouse event? See #4655
+ view: context.viewApi,
+ } as EventResizeStopArg)
+
+ if (this.validMutation) {
+ let updatedEventApi = new EventApi(
+ context,
+ mutatedRelevantEvents.defs[eventDef.defId],
+ eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null,
+ )
+
+ context.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents,
+ })
+
+ let eventChangeArg: EventChangeArg = {
+ oldEvent: eventApi,
+ event: updatedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents, context, eventInstance),
+ revert() {
+ context.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents, // the pre-change events
+ })
+ },
+ }
+
+ context.emitter.trigger('eventResize', {
+ ...eventChangeArg,
+ el: this.draggingSegEl,
+ startDelta: this.validMutation.startDelta || createDuration(0),
+ endDelta: this.validMutation.endDelta || createDuration(0),
+ jsEvent: ev.origEvent as MouseEvent,
+ view: context.viewApi,
+ })
+
+ context.emitter.trigger('eventChange', eventChangeArg)
+ } else {
+ context.emitter.trigger('_noEventResize')
+ }
+
+ // reset all internal state
+ this.draggingSeg = null
+ this.relevantEvents = null
+ this.validMutation = null
+
+ // okay to keep eventInstance around. useful to set it in handlePointerDown
+ }
+
+ querySegEl(ev: PointerDragEvent) {
+ return elementClosest(ev.subjectEl as HTMLElement, '.fc-event')
+ }
+}
+
+function computeMutation(
+ hit0: Hit,
+ hit1: Hit,
+ isFromStart: boolean,
+ instanceRange: DateRange,
+): EventMutation | null {
+ let dateEnv = hit0.context.dateEnv
+ let date0 = hit0.dateSpan.range.start
+ let date1 = hit1.dateSpan.range.start
+
+ let delta = diffDates(
+ date0, date1,
+ dateEnv,
+ hit0.largeUnit,
+ )
+
+ if (isFromStart) {
+ if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
+ return { startDelta: delta }
+ }
+ } else if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
+ return { endDelta: delta }
+ }
+
+ return null
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/HitDragging.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/HitDragging.ts
new file mode 100644
index 000000000..d0a329e9c
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/HitDragging.ts
@@ -0,0 +1,220 @@
+import {
+ Emitter, PointerDragEvent,
+ isDateSpansEqual,
+ computeRect,
+ constrainPoint, intersectRects, getRectCenter, diffPoints, Point,
+ rangeContainsRange,
+ Hit,
+ InteractionSettingsStore,
+ mapHash,
+ ElementDragging,
+} from '@fullcalendar/common'
+import { OffsetTracker } from '../OffsetTracker'
+
+/*
+Tracks movement over multiple droppable areas (aka "hits")
+that exist in one or more DateComponents.
+Relies on an existing draggable.
+
+emits:
+- pointerdown
+- dragstart
+- hitchange - fires initially, even if not over a hit
+- pointerup
+- (hitchange - again, to null, if ended over a hit)
+- dragend
+*/
+export class HitDragging {
+ droppableStore: InteractionSettingsStore
+ dragging: ElementDragging
+ emitter: Emitter
+
+ // options that can be set by caller
+ useSubjectCenter: boolean = false
+ requireInitial: boolean = true // if doesn't start out on a hit, won't emit any events
+
+ // internal state
+ offsetTrackers: { [componentUid: string]: OffsetTracker }
+ initialHit: Hit | null = null
+ movingHit: Hit | null = null
+ finalHit: Hit | null = null // won't ever be populated if shouldIgnoreMove
+ coordAdjust?: Point
+
+ constructor(dragging: ElementDragging, droppableStore: InteractionSettingsStore) {
+ this.droppableStore = droppableStore
+
+ dragging.emitter.on('pointerdown', this.handlePointerDown)
+ dragging.emitter.on('dragstart', this.handleDragStart)
+ dragging.emitter.on('dragmove', this.handleDragMove)
+ dragging.emitter.on('pointerup', this.handlePointerUp)
+ dragging.emitter.on('dragend', this.handleDragEnd)
+
+ this.dragging = dragging
+ this.emitter = new Emitter()
+ }
+
+ handlePointerDown = (ev: PointerDragEvent) => {
+ let { dragging } = this
+
+ this.initialHit = null
+ this.movingHit = null
+ this.finalHit = null
+
+ this.prepareHits()
+ this.processFirstCoord(ev)
+
+ if (this.initialHit || !this.requireInitial) {
+ dragging.setIgnoreMove(false)
+
+ // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
+ this.emitter.trigger('pointerdown', ev)
+ } else {
+ dragging.setIgnoreMove(true)
+ }
+ }
+
+ // sets initialHit
+ // sets coordAdjust
+ processFirstCoord(ev: PointerDragEvent) {
+ let origPoint = { left: ev.pageX, top: ev.pageY }
+ let adjustedPoint = origPoint
+ let subjectEl = ev.subjectEl
+ let subjectRect
+
+ if (subjectEl instanceof HTMLElement) { // i.e. not a Document/ShadowRoot
+ subjectRect = computeRect(subjectEl)
+ adjustedPoint = constrainPoint(adjustedPoint, subjectRect)
+ }
+
+ let initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top)
+ if (initialHit) {
+ if (this.useSubjectCenter && subjectRect) {
+ let slicedSubjectRect = intersectRects(subjectRect, initialHit.rect)
+ if (slicedSubjectRect) {
+ adjustedPoint = getRectCenter(slicedSubjectRect)
+ }
+ }
+
+ this.coordAdjust = diffPoints(adjustedPoint, origPoint)
+ } else {
+ this.coordAdjust = { left: 0, top: 0 }
+ }
+ }
+
+ handleDragStart = (ev: PointerDragEvent) => {
+ this.emitter.trigger('dragstart', ev)
+ this.handleMove(ev, true) // force = fire even if initially null
+ }
+
+ handleDragMove = (ev: PointerDragEvent) => {
+ this.emitter.trigger('dragmove', ev)
+ this.handleMove(ev)
+ }
+
+ handlePointerUp = (ev: PointerDragEvent) => {
+ this.releaseHits()
+ this.emitter.trigger('pointerup', ev)
+ }
+
+ handleDragEnd = (ev: PointerDragEvent) => {
+ if (this.movingHit) {
+ this.emitter.trigger('hitupdate', null, true, ev)
+ }
+
+ this.finalHit = this.movingHit
+ this.movingHit = null
+ this.emitter.trigger('dragend', ev)
+ }
+
+ handleMove(ev: PointerDragEvent, forceHandle?: boolean) {
+ let hit = this.queryHitForOffset(
+ ev.pageX + this.coordAdjust!.left,
+ ev.pageY + this.coordAdjust!.top,
+ )
+
+ if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
+ this.movingHit = hit
+ this.emitter.trigger('hitupdate', hit, false, ev)
+ }
+ }
+
+ prepareHits() {
+ this.offsetTrackers = mapHash(this.droppableStore, (interactionSettings) => {
+ interactionSettings.component.prepareHits()
+ return new OffsetTracker(interactionSettings.el)
+ })
+ }
+
+ releaseHits() {
+ let { offsetTrackers } = this
+
+ for (let id in offsetTrackers) {
+ offsetTrackers[id].destroy()
+ }
+
+ this.offsetTrackers = {}
+ }
+
+ queryHitForOffset(offsetLeft: number, offsetTop: number): Hit | null {
+ let { droppableStore, offsetTrackers } = this
+ let bestHit: Hit | null = null
+
+ for (let id in droppableStore) {
+ let component = droppableStore[id].component
+ let offsetTracker = offsetTrackers[id]
+
+ if (
+ offsetTracker && // wasn't destroyed mid-drag
+ offsetTracker.isWithinClipping(offsetLeft, offsetTop)
+ ) {
+ let originLeft = offsetTracker.computeLeft()
+ let originTop = offsetTracker.computeTop()
+ let positionLeft = offsetLeft - originLeft
+ let positionTop = offsetTop - originTop
+ let { origRect } = offsetTracker
+ let width = origRect.right - origRect.left
+ let height = origRect.bottom - origRect.top
+
+ if (
+ // must be within the element's bounds
+ positionLeft >= 0 && positionLeft < width &&
+ positionTop >= 0 && positionTop < height
+ ) {
+ let hit = component.queryHit(positionLeft, positionTop, width, height)
+ if (
+ hit && (
+ // make sure the hit is within activeRange, meaning it's not a dead cell
+ rangeContainsRange(hit.dateProfile.activeRange, hit.dateSpan.range)
+ ) &&
+ (!bestHit || hit.layer > bestHit.layer)
+ ) {
+ hit.componentId = id
+ hit.context = component.context
+
+ // TODO: better way to re-orient rectangle
+ hit.rect.left += originLeft
+ hit.rect.right += originLeft
+ hit.rect.top += originTop
+ hit.rect.bottom += originTop
+
+ bestHit = hit
+ }
+ }
+ }
+ }
+
+ return bestHit
+ }
+}
+
+export function isHitsEqual(hit0: Hit | null, hit1: Hit | null): boolean {
+ if (!hit0 && !hit1) {
+ return true
+ }
+
+ if (Boolean(hit0) !== Boolean(hit1)) {
+ return false
+ }
+
+ return isDateSpansEqual(hit0!.dateSpan, hit1!.dateSpan)
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/interactions/UnselectAuto.ts b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/UnselectAuto.ts
new file mode 100644
index 000000000..23e5b47cb
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/interactions/UnselectAuto.ts
@@ -0,0 +1,77 @@
+import {
+ DateSelectionApi,
+ PointerDragEvent,
+ elementClosest,
+ CalendarContext,
+ getEventTargetViaRoot,
+} from '@fullcalendar/common'
+import { PointerDragging } from '../dnd/PointerDragging'
+import { EventDragging } from './EventDragging'
+
+export class UnselectAuto {
+ documentPointer: PointerDragging // for unfocusing
+ isRecentPointerDateSelect = false // wish we could use a selector to detect date selection, but uses hit system
+ matchesCancel = false
+ matchesEvent = false
+
+ constructor(private context: CalendarContext) {
+ let documentPointer = this.documentPointer = new PointerDragging(document)
+ documentPointer.shouldIgnoreMove = true
+ documentPointer.shouldWatchScroll = false
+ documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown)
+ documentPointer.emitter.on('pointerup', this.onDocumentPointerUp)
+
+ /*
+ TODO: better way to know about whether there was a selection with the pointer
+ */
+ context.emitter.on('select', this.onSelect)
+ }
+
+ destroy() {
+ this.context.emitter.off('select', this.onSelect)
+ this.documentPointer.destroy()
+ }
+
+ onSelect = (selectInfo: DateSelectionApi) => {
+ if (selectInfo.jsEvent) {
+ this.isRecentPointerDateSelect = true
+ }
+ }
+
+ onDocumentPointerDown = (pev: PointerDragEvent) => {
+ let unselectCancel = this.context.options.unselectCancel
+ let downEl = getEventTargetViaRoot(pev.origEvent) as HTMLElement
+
+ this.matchesCancel = !!elementClosest(downEl, unselectCancel)
+ this.matchesEvent = !!elementClosest(downEl, EventDragging.SELECTOR) // interaction started on an event?
+ }
+
+ onDocumentPointerUp = (pev: PointerDragEvent) => {
+ let { context } = this
+ let { documentPointer } = this
+ let calendarState = context.getCurrentData()
+
+ // touch-scrolling should never unfocus any type of selection
+ if (!documentPointer.wasTouchScroll) {
+ if (
+ calendarState.dateSelection && // an existing date selection?
+ !this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
+ ) {
+ let unselectAuto = context.options.unselectAuto
+
+ if (unselectAuto && (!unselectAuto || !this.matchesCancel)) {
+ context.calendarApi.unselect(pev)
+ }
+ }
+
+ if (
+ calendarState.eventSelection && // an existing event selected?
+ !this.matchesEvent // interaction DIDN'T start on an event
+ ) {
+ context.dispatch({ type: 'UNSELECT_EVENT' })
+ }
+ }
+
+ this.isRecentPointerDateSelect = false
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/main.global.ts b/apps/schoolCalendar/fullcalendar/interaction/src/main.global.ts
new file mode 100644
index 000000000..0af579774
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/main.global.ts
@@ -0,0 +1,7 @@
+import { globalPlugins } from '@fullcalendar/common'
+import plugin from './main'
+
+globalPlugins.push(plugin)
+
+export default plugin
+export * from './main'
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/main.ts b/apps/schoolCalendar/fullcalendar/interaction/src/main.ts
new file mode 100644
index 000000000..f57049ca3
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/main.ts
@@ -0,0 +1,23 @@
+import { createPlugin } from '@fullcalendar/common'
+import { DateClicking } from './interactions/DateClicking'
+import { DateSelecting } from './interactions/DateSelecting'
+import { EventDragging } from './interactions/EventDragging'
+import { EventResizing } from './interactions/EventResizing'
+import { UnselectAuto } from './interactions/UnselectAuto'
+import { FeaturefulElementDragging } from './dnd/FeaturefulElementDragging'
+import { OPTION_REFINERS, LISTENER_REFINERS } from './options'
+import './options-declare'
+
+export default createPlugin({
+ componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing],
+ calendarInteractions: [UnselectAuto],
+ elementDraggingImpl: FeaturefulElementDragging,
+ optionRefiners: OPTION_REFINERS,
+ listenerRefiners: LISTENER_REFINERS,
+})
+
+export * from './api-type-deps'
+export { FeaturefulElementDragging }
+export { PointerDragging } from './dnd/PointerDragging'
+export { ExternalDraggable as Draggable } from './interactions-external/ExternalDraggable'
+export { ThirdPartyDraggable } from './interactions-external/ThirdPartyDraggable'
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/options-declare.ts b/apps/schoolCalendar/fullcalendar/interaction/src/options-declare.ts
new file mode 100644
index 000000000..9cbc5a4a8
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/options-declare.ts
@@ -0,0 +1,9 @@
+import { OPTION_REFINERS, LISTENER_REFINERS } from './options'
+
+type ExtraOptionRefiners = typeof OPTION_REFINERS
+type ExtraListenerRefiners = typeof LISTENER_REFINERS
+
+declare module '@fullcalendar/common' {
+ interface BaseOptionRefiners extends ExtraOptionRefiners {}
+ interface CalendarListenerRefiners extends ExtraListenerRefiners {}
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/options.ts b/apps/schoolCalendar/fullcalendar/interaction/src/options.ts
new file mode 100644
index 000000000..a11ce5399
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/options.ts
@@ -0,0 +1,26 @@
+import { identity, Identity, EventDropArg } from '@fullcalendar/common'
+
+// public
+import {
+ DateClickArg,
+ EventDragStartArg, EventDragStopArg,
+ EventResizeStartArg, EventResizeStopArg, EventResizeDoneArg,
+ DropArg, EventReceiveArg, EventLeaveArg,
+} from './api-type-deps'
+
+export const OPTION_REFINERS = {
+ fixedMirrorParent: identity as Identity,
+}
+
+export const LISTENER_REFINERS = {
+ dateClick: identity as Identity<(arg: DateClickArg) => void>,
+ eventDragStart: identity as Identity<(arg: EventDragStartArg) => void>,
+ eventDragStop: identity as Identity<(arg: EventDragStopArg) => void>,
+ eventDrop: identity as Identity<(arg: EventDropArg) => void>,
+ eventResizeStart: identity as Identity<(arg: EventResizeStartArg) => void>,
+ eventResizeStop: identity as Identity<(arg: EventResizeStopArg) => void>,
+ eventResize: identity as Identity<(arg: EventResizeDoneArg) => void>,
+ drop: identity as Identity<(arg: DropArg) => void>,
+ eventReceive: identity as Identity<(arg: EventReceiveArg) => void>,
+ eventLeave: identity as Identity<(arg: EventLeaveArg) => void>,
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/src/utils.ts b/apps/schoolCalendar/fullcalendar/interaction/src/utils.ts
new file mode 100644
index 000000000..056a0040d
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/src/utils.ts
@@ -0,0 +1,38 @@
+import { DateSpan, CalendarContext, DatePointApi, DateEnv, ViewApi, EventApi } from '@fullcalendar/common'
+import { __assign } from 'tslib'
+
+export interface DropArg extends DatePointApi {
+ draggedEl: HTMLElement
+ jsEvent: MouseEvent
+ view: ViewApi
+}
+
+export type EventReceiveArg = EventReceiveLeaveArg
+export type EventLeaveArg = EventReceiveLeaveArg
+export interface EventReceiveLeaveArg { // will this become public?
+ draggedEl: HTMLElement
+ event: EventApi
+ relatedEvents: EventApi[]
+ revert: () => void
+ view: ViewApi
+}
+
+export function buildDatePointApiWithContext(dateSpan: DateSpan, context: CalendarContext) {
+ let props = {} as DatePointApi
+
+ for (let transform of context.pluginHooks.datePointTransforms) {
+ __assign(props, transform(dateSpan, context))
+ }
+
+ __assign(props, buildDatePointApi(dateSpan, context.dateEnv))
+
+ return props
+}
+
+export function buildDatePointApi(span: DateSpan, dateEnv: DateEnv): DatePointApi {
+ return {
+ date: dateEnv.toDate(span.range.start),
+ dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+ allDay: span.allDay,
+ }
+}
diff --git a/apps/schoolCalendar/fullcalendar/interaction/tsconfig.json b/apps/schoolCalendar/fullcalendar/interaction/tsconfig.json
new file mode 100644
index 000000000..6172b1a45
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/interaction/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.base",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "tsc"
+ },
+ "include": [
+ "src/**/*"
+ ],
+ "references": [
+ { "path": "../common" }
+ ]
+}
diff --git a/apps/schoolCalendar/fullcalendar/locales-all.js b/apps/schoolCalendar/fullcalendar/locales-all.js
new file mode 100644
index 000000000..f8be47ef2
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/locales-all.js
@@ -0,0 +1,1622 @@
+[].push.apply(FullCalendar.globalLocales, function () {
+ 'use strict';
+
+ var l0 = {
+ code: 'af',
+ week: {
+ dow: 1, // Maandag is die eerste dag van die week.
+ doy: 4, // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
+ },
+ buttonText: {
+ prev: 'Vorige',
+ next: 'Volgende',
+ today: 'Vandag',
+ year: 'Jaar',
+ month: 'Maand',
+ week: 'Week',
+ day: 'Dag',
+ list: 'Agenda',
+ },
+ allDayText: 'Heeldag',
+ moreLinkText: 'Addisionele',
+ noEventsText: 'Daar is geen gebeurtenisse nie',
+ };
+
+ var l1 = {
+ code: 'ar-dz',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 4, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l2 = {
+ code: 'ar-kw',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l3 = {
+ code: 'ar-ly',
+ week: {
+ dow: 6, // Saturday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l4 = {
+ code: 'ar-ma',
+ week: {
+ dow: 6, // Saturday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l5 = {
+ code: 'ar-sa',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 6, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l6 = {
+ code: 'ar-tn',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l7 = {
+ code: 'ar',
+ week: {
+ dow: 6, // Saturday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'السابق',
+ next: 'التالي',
+ today: 'اليوم',
+ month: 'شهر',
+ week: 'أسبوع',
+ day: 'يوم',
+ list: 'أجندة',
+ },
+ weekText: 'أسبوع',
+ allDayText: 'اليوم كله',
+ moreLinkText: 'أخرى',
+ noEventsText: 'أي أحداث لعرض',
+ };
+
+ var l8 = {
+ code: 'az',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Əvvəl',
+ next: 'Sonra',
+ today: 'Bu Gün',
+ month: 'Ay',
+ week: 'Həftə',
+ day: 'Gün',
+ list: 'Gündəm',
+ },
+ weekText: 'Həftə',
+ allDayText: 'Bütün Gün',
+ moreLinkText: function(n) {
+ return '+ daha çox ' + n
+ },
+ noEventsText: 'Göstərmək üçün hadisə yoxdur',
+ };
+
+ var l9 = {
+ code: 'bg',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'назад',
+ next: 'напред',
+ today: 'днес',
+ month: 'Месец',
+ week: 'Седмица',
+ day: 'Ден',
+ list: 'График',
+ },
+ allDayText: 'Цял ден',
+ moreLinkText: function(n) {
+ return '+още ' + n
+ },
+ noEventsText: 'Няма събития за показване',
+ };
+
+ var l10 = {
+ code: 'bn',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 6, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'পেছনে',
+ next: 'সামনে',
+ today: 'আজ',
+ month: 'মাস',
+ week: 'সপ্তাহ',
+ day: 'দিন',
+ list: 'তালিকা',
+ },
+ weekText: 'সপ্তাহ',
+ allDayText: 'সারাদিন',
+ moreLinkText: function(n) {
+ return '+অন্যান্য ' + n
+ },
+ noEventsText: 'কোনো ইভেন্ট নেই',
+ };
+
+ var l11 = {
+ code: 'bs',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Prošli',
+ next: 'Sljedeći',
+ today: 'Danas',
+ month: 'Mjesec',
+ week: 'Sedmica',
+ day: 'Dan',
+ list: 'Raspored',
+ },
+ weekText: 'Sed',
+ allDayText: 'Cijeli dan',
+ moreLinkText: function(n) {
+ return '+ još ' + n
+ },
+ noEventsText: 'Nema događaja za prikazivanje',
+ };
+
+ var l12 = {
+ code: 'ca',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Anterior',
+ next: 'Següent',
+ today: 'Avui',
+ month: 'Mes',
+ week: 'Setmana',
+ day: 'Dia',
+ list: 'Agenda',
+ },
+ weekText: 'Set',
+ allDayText: 'Tot el dia',
+ moreLinkText: 'més',
+ noEventsText: 'No hi ha esdeveniments per mostrar',
+ };
+
+ var l13 = {
+ code: 'cs',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Dříve',
+ next: 'Později',
+ today: 'Nyní',
+ month: 'Měsíc',
+ week: 'Týden',
+ day: 'Den',
+ list: 'Agenda',
+ },
+ weekText: 'Týd',
+ allDayText: 'Celý den',
+ moreLinkText: function(n) {
+ return '+další: ' + n
+ },
+ noEventsText: 'Žádné akce k zobrazení',
+ };
+
+ var l14 = {
+ code: 'cy',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Blaenorol',
+ next: 'Nesaf',
+ today: 'Heddiw',
+ year: 'Blwyddyn',
+ month: 'Mis',
+ week: 'Wythnos',
+ day: 'Dydd',
+ list: 'Rhestr',
+ },
+ weekText: 'Wythnos',
+ allDayText: 'Trwy\'r dydd',
+ moreLinkText: 'Mwy',
+ noEventsText: 'Dim digwyddiadau',
+ };
+
+ var l15 = {
+ code: 'da',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Forrige',
+ next: 'Næste',
+ today: 'I dag',
+ month: 'Måned',
+ week: 'Uge',
+ day: 'Dag',
+ list: 'Agenda',
+ },
+ weekText: 'Uge',
+ allDayText: 'Hele dagen',
+ moreLinkText: 'flere',
+ noEventsText: 'Ingen arrangementer at vise',
+ };
+
+ var l16 = {
+ code: 'de-at',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Zurück',
+ next: 'Vor',
+ today: 'Heute',
+ year: 'Jahr',
+ month: 'Monat',
+ week: 'Woche',
+ day: 'Tag',
+ list: 'Terminübersicht',
+ },
+ weekText: 'KW',
+ allDayText: 'Ganztägig',
+ moreLinkText: function(n) {
+ return '+ weitere ' + n
+ },
+ noEventsText: 'Keine Ereignisse anzuzeigen',
+ };
+
+ var l17 = {
+ code: 'de',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Zurück',
+ next: 'Vor',
+ today: 'Heute',
+ year: 'Jahr',
+ month: 'Monat',
+ week: 'Woche',
+ day: 'Tag',
+ list: 'Terminübersicht',
+ },
+ weekText: 'KW',
+ allDayText: 'Ganztägig',
+ moreLinkText: function(n) {
+ return '+ weitere ' + n
+ },
+ noEventsText: 'Keine Ereignisse anzuzeigen',
+ };
+
+ var l18 = {
+ code: 'el',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Προηγούμενος',
+ next: 'Επόμενος',
+ today: 'Σήμερα',
+ month: 'Μήνας',
+ week: 'Εβδομάδα',
+ day: 'Ημέρα',
+ list: 'Ατζέντα',
+ },
+ weekText: 'Εβδ',
+ allDayText: 'Ολοήμερο',
+ moreLinkText: 'περισσότερα',
+ noEventsText: 'Δεν υπάρχουν γεγονότα προς εμφάνιση',
+ };
+
+ var l19 = {
+ code: 'en-au',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ };
+
+ var l20 = {
+ code: 'en-gb',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ };
+
+ var l21 = {
+ code: 'en-nz',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ };
+
+ var l22 = {
+ code: 'eo',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Antaŭa',
+ next: 'Sekva',
+ today: 'Hodiaŭ',
+ month: 'Monato',
+ week: 'Semajno',
+ day: 'Tago',
+ list: 'Tagordo',
+ },
+ weekText: 'Sm',
+ allDayText: 'Tuta tago',
+ moreLinkText: 'pli',
+ noEventsText: 'Neniuj eventoj por montri',
+ };
+
+ var l23 = {
+ code: 'es',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 6, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Ant',
+ next: 'Sig',
+ today: 'Hoy',
+ month: 'Mes',
+ week: 'Semana',
+ day: 'Día',
+ list: 'Agenda',
+ },
+ weekText: 'Sm',
+ allDayText: 'Todo el día',
+ moreLinkText: 'más',
+ noEventsText: 'No hay eventos para mostrar',
+ };
+
+ var l24 = {
+ code: 'es',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Ant',
+ next: 'Sig',
+ today: 'Hoy',
+ month: 'Mes',
+ week: 'Semana',
+ day: 'Día',
+ list: 'Agenda',
+ },
+ weekText: 'Sm',
+ allDayText: 'Todo el día',
+ moreLinkText: 'más',
+ noEventsText: 'No hay eventos para mostrar',
+ };
+
+ var l25 = {
+ code: 'et',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Eelnev',
+ next: 'Järgnev',
+ today: 'Täna',
+ month: 'Kuu',
+ week: 'Nädal',
+ day: 'Päev',
+ list: 'Päevakord',
+ },
+ weekText: 'näd',
+ allDayText: 'Kogu päev',
+ moreLinkText: function(n) {
+ return '+ veel ' + n
+ },
+ noEventsText: 'Kuvamiseks puuduvad sündmused',
+ };
+
+ var l26 = {
+ code: 'eu',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Aur',
+ next: 'Hur',
+ today: 'Gaur',
+ month: 'Hilabetea',
+ week: 'Astea',
+ day: 'Eguna',
+ list: 'Agenda',
+ },
+ weekText: 'As',
+ allDayText: 'Egun osoa',
+ moreLinkText: 'gehiago',
+ noEventsText: 'Ez dago ekitaldirik erakusteko',
+ };
+
+ var l27 = {
+ code: 'fa',
+ week: {
+ dow: 6, // Saturday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'قبلی',
+ next: 'بعدی',
+ today: 'امروز',
+ month: 'ماه',
+ week: 'هفته',
+ day: 'روز',
+ list: 'برنامه',
+ },
+ weekText: 'هف',
+ allDayText: 'تمام روز',
+ moreLinkText: function(n) {
+ return 'بیش از ' + n
+ },
+ noEventsText: 'هیچ رویدادی به نمایش',
+ };
+
+ var l28 = {
+ code: 'fi',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Edellinen',
+ next: 'Seuraava',
+ today: 'Tänään',
+ month: 'Kuukausi',
+ week: 'Viikko',
+ day: 'Päivä',
+ list: 'Tapahtumat',
+ },
+ weekText: 'Vk',
+ allDayText: 'Koko päivä',
+ moreLinkText: 'lisää',
+ noEventsText: 'Ei näytettäviä tapahtumia',
+ };
+
+ var l29 = {
+ code: 'fr',
+ buttonText: {
+ prev: 'Précédent',
+ next: 'Suivant',
+ today: "Aujourd'hui",
+ year: 'Année',
+ month: 'Mois',
+ week: 'Semaine',
+ day: 'Jour',
+ list: 'Mon planning',
+ },
+ weekText: 'Sem.',
+ allDayText: 'Toute la journée',
+ moreLinkText: 'en plus',
+ noEventsText: 'Aucun événement à afficher',
+ };
+
+ var l30 = {
+ code: 'fr-ch',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Précédent',
+ next: 'Suivant',
+ today: 'Courant',
+ year: 'Année',
+ month: 'Mois',
+ week: 'Semaine',
+ day: 'Jour',
+ list: 'Mon planning',
+ },
+ weekText: 'Sm',
+ allDayText: 'Toute la journée',
+ moreLinkText: 'en plus',
+ noEventsText: 'Aucun événement à afficher',
+ };
+
+ var l31 = {
+ code: 'fr',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Précédent',
+ next: 'Suivant',
+ today: "Aujourd'hui",
+ year: 'Année',
+ month: 'Mois',
+ week: 'Semaine',
+ day: 'Jour',
+ list: 'Planning',
+ },
+ weekText: 'Sem.',
+ allDayText: 'Toute la journée',
+ moreLinkText: 'en plus',
+ noEventsText: 'Aucun événement à afficher',
+ };
+
+ var l32 = {
+ code: 'gl',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Ant',
+ next: 'Seg',
+ today: 'Hoxe',
+ month: 'Mes',
+ week: 'Semana',
+ day: 'Día',
+ list: 'Axenda',
+ },
+ weekText: 'Sm',
+ allDayText: 'Todo o día',
+ moreLinkText: 'máis',
+ noEventsText: 'Non hai eventos para amosar',
+ };
+
+ var l33 = {
+ code: 'he',
+ direction: 'rtl',
+ buttonText: {
+ prev: 'הקודם',
+ next: 'הבא',
+ today: 'היום',
+ month: 'חודש',
+ week: 'שבוע',
+ day: 'יום',
+ list: 'סדר יום',
+ },
+ allDayText: 'כל היום',
+ moreLinkText: 'אחר',
+ noEventsText: 'אין אירועים להצגה',
+ weekText: 'שבוע',
+ };
+
+ var l34 = {
+ code: 'hi',
+ week: {
+ dow: 0, // Sunday is the first day of the week.
+ doy: 6, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'पिछला',
+ next: 'अगला',
+ today: 'आज',
+ month: 'महीना',
+ week: 'सप्ताह',
+ day: 'दिन',
+ list: 'कार्यसूची',
+ },
+ weekText: 'हफ्ता',
+ allDayText: 'सभी दिन',
+ moreLinkText: function(n) {
+ return '+अधिक ' + n
+ },
+ noEventsText: 'कोई घटनाओं को प्रदर्शित करने के लिए',
+ };
+
+ var l35 = {
+ code: 'hr',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Prijašnji',
+ next: 'Sljedeći',
+ today: 'Danas',
+ month: 'Mjesec',
+ week: 'Tjedan',
+ day: 'Dan',
+ list: 'Raspored',
+ },
+ weekText: 'Tje',
+ allDayText: 'Cijeli dan',
+ moreLinkText: function(n) {
+ return '+ još ' + n
+ },
+ noEventsText: 'Nema događaja za prikaz',
+ };
+
+ var l36 = {
+ code: 'hu',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'vissza',
+ next: 'előre',
+ today: 'ma',
+ month: 'Hónap',
+ week: 'Hét',
+ day: 'Nap',
+ list: 'Lista',
+ },
+ weekText: 'Hét',
+ allDayText: 'Egész nap',
+ moreLinkText: 'további',
+ noEventsText: 'Nincs megjeleníthető esemény',
+ };
+
+ var l37 = {
+ code: 'hy-am',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Նախորդ',
+ next: 'Հաջորդ',
+ today: 'Այսօր',
+ month: 'Ամիս',
+ week: 'Շաբաթ',
+ day: 'Օր',
+ list: 'Օրվա ցուցակ',
+ },
+ weekText: 'Շաբ',
+ allDayText: 'Ամբողջ օր',
+ moreLinkText: function(n) {
+ return '+ ևս ' + n
+ },
+ noEventsText: 'Բացակայում է իրադարձությունը ցուցադրելու',
+ };
+
+ var l38 = {
+ code: 'id',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'mundur',
+ next: 'maju',
+ today: 'hari ini',
+ month: 'Bulan',
+ week: 'Minggu',
+ day: 'Hari',
+ list: 'Agenda',
+ },
+ weekText: 'Mg',
+ allDayText: 'Sehari penuh',
+ moreLinkText: 'lebih',
+ noEventsText: 'Tidak ada acara untuk ditampilkan',
+ };
+
+ var l39 = {
+ code: 'is',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Fyrri',
+ next: 'Næsti',
+ today: 'Í dag',
+ month: 'Mánuður',
+ week: 'Vika',
+ day: 'Dagur',
+ list: 'Dagskrá',
+ },
+ weekText: 'Vika',
+ allDayText: 'Allan daginn',
+ moreLinkText: 'meira',
+ noEventsText: 'Engir viðburðir til að sýna',
+ };
+
+ var l40 = {
+ code: 'it',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Prec',
+ next: 'Succ',
+ today: 'Oggi',
+ month: 'Mese',
+ week: 'Settimana',
+ day: 'Giorno',
+ list: 'Agenda',
+ },
+ weekText: 'Sm',
+ allDayText: 'Tutto il giorno',
+ moreLinkText: function(n) {
+ return '+altri ' + n
+ },
+ noEventsText: 'Non ci sono eventi da visualizzare',
+ };
+
+ var l41 = {
+ code: 'ja',
+ buttonText: {
+ prev: '前',
+ next: '次',
+ today: '今日',
+ month: '月',
+ week: '週',
+ day: '日',
+ list: '予定リスト',
+ },
+ weekText: '週',
+ allDayText: '終日',
+ moreLinkText: function(n) {
+ return '他 ' + n + ' 件'
+ },
+ noEventsText: '表示する予定はありません',
+ };
+
+ var l42 = {
+ code: 'ka',
+ week: {
+ dow: 1,
+ doy: 7,
+ },
+ buttonText: {
+ prev: 'წინა',
+ next: 'შემდეგი',
+ today: 'დღეს',
+ month: 'თვე',
+ week: 'კვირა',
+ day: 'დღე',
+ list: 'დღის წესრიგი',
+ },
+ weekText: 'კვ',
+ allDayText: 'მთელი დღე',
+ moreLinkText: function(n) {
+ return '+ კიდევ ' + n
+ },
+ noEventsText: 'ღონისძიებები არ არის',
+ };
+
+ var l43 = {
+ code: 'kk',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Алдыңғы',
+ next: 'Келесі',
+ today: 'Бүгін',
+ month: 'Ай',
+ week: 'Апта',
+ day: 'Күн',
+ list: 'Күн тәртібі',
+ },
+ weekText: 'Не',
+ allDayText: 'Күні бойы',
+ moreLinkText: function(n) {
+ return '+ тағы ' + n
+ },
+ noEventsText: 'Көрсету үшін оқиғалар жоқ',
+ };
+
+ var l44 = {
+ code: 'km',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'មុន',
+ next: 'បន្ទាប់',
+ today: 'ថ្ងៃនេះ',
+ year: 'ឆ្នាំ',
+ month: 'ខែ',
+ week: 'សប្តាហ៍',
+ day: 'ថ្ងៃ',
+ list: 'បញ្ជី',
+ },
+ weekText: 'សប្តាហ៍',
+ allDayText: 'ពេញមួយថ្ងៃ',
+ moreLinkText: 'ច្រើនទៀត',
+ noEventsText: 'គ្មានព្រឹត្តិការណ៍ត្រូវបង្ហាញ',
+ };
+
+ var l45 = {
+ code: 'ko',
+ buttonText: {
+ prev: '이전달',
+ next: '다음달',
+ today: '오늘',
+ month: '월',
+ week: '주',
+ day: '일',
+ list: '일정목록',
+ },
+ weekText: '주',
+ allDayText: '종일',
+ moreLinkText: '개',
+ noEventsText: '일정이 없습니다',
+ };
+
+ var l46 = {
+ code: 'ku',
+ week: {
+ dow: 6, // Saturday is the first day of the week.
+ doy: 12, // The week that contains Jan 1st is the first week of the year.
+ },
+ direction: 'rtl',
+ buttonText: {
+ prev: 'پێشتر',
+ next: 'دواتر',
+ today: 'ئەمڕو',
+ month: 'مانگ',
+ week: 'هەفتە',
+ day: 'ڕۆژ',
+ list: 'بەرنامە',
+ },
+ weekText: 'هەفتە',
+ allDayText: 'هەموو ڕۆژەکە',
+ moreLinkText: 'زیاتر',
+ noEventsText: 'هیچ ڕووداوێك نیە',
+ };
+
+ var l47 = {
+ code: 'lb',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Zréck',
+ next: 'Weider',
+ today: 'Haut',
+ month: 'Mount',
+ week: 'Woch',
+ day: 'Dag',
+ list: 'Terminiwwersiicht',
+ },
+ weekText: 'W',
+ allDayText: 'Ganzen Dag',
+ moreLinkText: 'méi',
+ noEventsText: 'Nee Evenementer ze affichéieren',
+ };
+
+ var l48 = {
+ code: 'lt',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Atgal',
+ next: 'Pirmyn',
+ today: 'Šiandien',
+ month: 'Mėnuo',
+ week: 'Savaitė',
+ day: 'Diena',
+ list: 'Darbotvarkė',
+ },
+ weekText: 'SAV',
+ allDayText: 'Visą dieną',
+ moreLinkText: 'daugiau',
+ noEventsText: 'Nėra įvykių rodyti',
+ };
+
+ var l49 = {
+ code: 'lv',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Iepr.',
+ next: 'Nāk.',
+ today: 'Šodien',
+ month: 'Mēnesis',
+ week: 'Nedēļa',
+ day: 'Diena',
+ list: 'Dienas kārtība',
+ },
+ weekText: 'Ned.',
+ allDayText: 'Visu dienu',
+ moreLinkText: function(n) {
+ return '+vēl ' + n
+ },
+ noEventsText: 'Nav notikumu',
+ };
+
+ var l50 = {
+ code: 'mk',
+ buttonText: {
+ prev: 'претходно',
+ next: 'следно',
+ today: 'Денес',
+ month: 'Месец',
+ week: 'Недела',
+ day: 'Ден',
+ list: 'График',
+ },
+ weekText: 'Сед',
+ allDayText: 'Цел ден',
+ moreLinkText: function(n) {
+ return '+повеќе ' + n
+ },
+ noEventsText: 'Нема настани за прикажување',
+ };
+
+ var l51 = {
+ code: 'ms',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Sebelum',
+ next: 'Selepas',
+ today: 'hari ini',
+ month: 'Bulan',
+ week: 'Minggu',
+ day: 'Hari',
+ list: 'Agenda',
+ },
+ weekText: 'Mg',
+ allDayText: 'Sepanjang hari',
+ moreLinkText: function(n) {
+ return 'masih ada ' + n + ' acara'
+ },
+ noEventsText: 'Tiada peristiwa untuk dipaparkan',
+ };
+
+ var l52 = {
+ code: 'nb',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Forrige',
+ next: 'Neste',
+ today: 'I dag',
+ month: 'Måned',
+ week: 'Uke',
+ day: 'Dag',
+ list: 'Agenda',
+ },
+ weekText: 'Uke',
+ allDayText: 'Hele dagen',
+ moreLinkText: 'til',
+ noEventsText: 'Ingen hendelser å vise',
+ };
+
+ var l53 = {
+ code: 'ne', // code for nepal
+ week: {
+ dow: 7, // Sunday is the first day of the week.
+ doy: 1, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'अघिल्लो',
+ next: 'अर्को',
+ today: 'आज',
+ month: 'महिना',
+ week: 'हप्ता',
+ day: 'दिन',
+ list: 'सूची',
+ },
+ weekText: 'हप्ता',
+ allDayText: 'दिनभरि',
+ moreLinkText: 'थप लिंक',
+ noEventsText: 'देखाउनको लागि कुनै घटनाहरू छैनन्',
+ };
+
+ var l54 = {
+ code: 'nl',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Vorige',
+ next: 'Volgende',
+ today: 'Vandaag',
+ year: 'Jaar',
+ month: 'Maand',
+ week: 'Week',
+ day: 'Dag',
+ list: 'Agenda',
+ },
+ allDayText: 'Hele dag',
+ moreLinkText: 'extra',
+ noEventsText: 'Geen evenementen om te laten zien',
+ };
+
+ var l55 = {
+ code: 'nn',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Førre',
+ next: 'Neste',
+ today: 'I dag',
+ month: 'Månad',
+ week: 'Veke',
+ day: 'Dag',
+ list: 'Agenda',
+ },
+ weekText: 'Veke',
+ allDayText: 'Heile dagen',
+ moreLinkText: 'til',
+ noEventsText: 'Ingen hendelser å vise',
+ };
+
+ var l56 = {
+ code: 'pl',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Poprzedni',
+ next: 'Następny',
+ today: 'Dziś',
+ month: 'Miesiąc',
+ week: 'Tydzień',
+ day: 'Dzień',
+ list: 'Plan dnia',
+ },
+ weekText: 'Tydz',
+ allDayText: 'Cały dzień',
+ moreLinkText: 'więcej',
+ noEventsText: 'Brak wydarzeń do wyświetlenia',
+ };
+
+ var l57 = {
+ code: 'pt-br',
+ buttonText: {
+ prev: 'Anterior',
+ next: 'Próximo',
+ today: 'Hoje',
+ month: 'Mês',
+ week: 'Semana',
+ day: 'Dia',
+ list: 'Lista',
+ },
+ weekText: 'Sm',
+ allDayText: 'dia inteiro',
+ moreLinkText: function(n) {
+ return 'mais +' + n
+ },
+ noEventsText: 'Não há eventos para mostrar',
+ };
+
+ var l58 = {
+ code: 'pt',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Anterior',
+ next: 'Seguinte',
+ today: 'Hoje',
+ month: 'Mês',
+ week: 'Semana',
+ day: 'Dia',
+ list: 'Agenda',
+ },
+ weekText: 'Sem',
+ allDayText: 'Todo o dia',
+ moreLinkText: 'mais',
+ noEventsText: 'Não há eventos para mostrar',
+ };
+
+ var l59 = {
+ code: 'ro',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'precedentă',
+ next: 'următoare',
+ today: 'Azi',
+ month: 'Lună',
+ week: 'Săptămână',
+ day: 'Zi',
+ list: 'Agendă',
+ },
+ weekText: 'Săpt',
+ allDayText: 'Toată ziua',
+ moreLinkText: function(n) {
+ return '+alte ' + n
+ },
+ noEventsText: 'Nu există evenimente de afișat',
+ };
+
+ var l60 = {
+ code: 'ru',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Пред',
+ next: 'След',
+ today: 'Сегодня',
+ month: 'Месяц',
+ week: 'Неделя',
+ day: 'День',
+ list: 'Повестка дня',
+ },
+ weekText: 'Нед',
+ allDayText: 'Весь день',
+ moreLinkText: function(n) {
+ return '+ ещё ' + n
+ },
+ noEventsText: 'Нет событий для отображения',
+ };
+
+ var l61 = {
+ code: 'sk',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Predchádzajúci',
+ next: 'Nasledujúci',
+ today: 'Dnes',
+ month: 'Mesiac',
+ week: 'Týždeň',
+ day: 'Deň',
+ list: 'Rozvrh',
+ },
+ weekText: 'Ty',
+ allDayText: 'Celý deň',
+ moreLinkText: function(n) {
+ return '+ďalšie: ' + n
+ },
+ noEventsText: 'Žiadne akcie na zobrazenie',
+ };
+
+ var l62 = {
+ code: 'sl',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Prejšnji',
+ next: 'Naslednji',
+ today: 'Trenutni',
+ month: 'Mesec',
+ week: 'Teden',
+ day: 'Dan',
+ list: 'Dnevni red',
+ },
+ weekText: 'Teden',
+ allDayText: 'Ves dan',
+ moreLinkText: 'več',
+ noEventsText: 'Ni dogodkov za prikaz',
+ };
+
+ var l63 = {
+ code: 'sm',
+ buttonText: {
+ prev: 'Talu ai',
+ next: 'Mulimuli atu',
+ today: 'Aso nei',
+ month: 'Masina',
+ week: 'Vaiaso',
+ day: 'Aso',
+ list: 'Faasologa',
+ },
+ weekText: 'Vaiaso',
+ allDayText: 'Aso atoa',
+ moreLinkText: 'sili atu',
+ noEventsText: 'Leai ni mea na tutupu',
+ };
+
+ var l64 = {
+ code: 'sq',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'mbrapa',
+ next: 'Përpara',
+ today: 'sot',
+ month: 'Muaj',
+ week: 'Javë',
+ day: 'Ditë',
+ list: 'Listë',
+ },
+ weekText: 'Ja',
+ allDayText: 'Gjithë ditën',
+ moreLinkText: function(n) {
+ return '+më tepër ' + n
+ },
+ noEventsText: 'Nuk ka evente për të shfaqur',
+ };
+
+ var l65 = {
+ code: 'sr-cyrl',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Претходна',
+ next: 'следећи',
+ today: 'Данас',
+ month: 'Месец',
+ week: 'Недеља',
+ day: 'Дан',
+ list: 'Планер',
+ },
+ weekText: 'Сед',
+ allDayText: 'Цео дан',
+ moreLinkText: function(n) {
+ return '+ још ' + n
+ },
+ noEventsText: 'Нема догађаја за приказ',
+ };
+
+ var l66 = {
+ code: 'sr',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Prethodna',
+ next: 'Sledeći',
+ today: 'Danas',
+ month: 'Mеsеc',
+ week: 'Nеdеlja',
+ day: 'Dan',
+ list: 'Planеr',
+ },
+ weekText: 'Sed',
+ allDayText: 'Cеo dan',
+ moreLinkText: function(n) {
+ return '+ još ' + n
+ },
+ noEventsText: 'Nеma događaja za prikaz',
+ };
+
+ var l67 = {
+ code: 'sv',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Förra',
+ next: 'Nästa',
+ today: 'Idag',
+ month: 'Månad',
+ week: 'Vecka',
+ day: 'Dag',
+ list: 'Program',
+ },
+ weekText: 'v.',
+ allDayText: 'Heldag',
+ moreLinkText: 'till',
+ noEventsText: 'Inga händelser att visa',
+ };
+
+ var l68 = {
+ code: 'ta-in',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'முந்தைய',
+ next: 'அடுத்தது',
+ today: 'இன்று',
+ month: 'மாதம்',
+ week: 'வாரம்',
+ day: 'நாள்',
+ list: 'தினசரி அட்டவணை',
+ },
+ weekText: 'வாரம்',
+ allDayText: 'நாள் முழுவதும்',
+ moreLinkText: function(n) {
+ return '+ மேலும் ' + n
+ },
+ noEventsText: 'காண்பிக்க நிகழ்வுகள் இல்லை',
+ };
+
+ var l69 = {
+ code: 'th',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'ก่อนหน้า',
+ next: 'ถัดไป',
+ prevYear: 'ปีก่อนหน้า',
+ nextYear: 'ปีถัดไป',
+ year: 'ปี',
+ today: 'วันนี้',
+ month: 'เดือน',
+ week: 'สัปดาห์',
+ day: 'วัน',
+ list: 'กำหนดการ',
+ },
+ weekText: 'สัปดาห์',
+ allDayText: 'ตลอดวัน',
+ moreLinkText: 'เพิ่มเติม',
+ noEventsText: 'ไม่มีกิจกรรมที่จะแสดง',
+ };
+
+ var l70 = {
+ code: 'tr',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'geri',
+ next: 'ileri',
+ today: 'bugün',
+ month: 'Ay',
+ week: 'Hafta',
+ day: 'Gün',
+ list: 'Ajanda',
+ },
+ weekText: 'Hf',
+ allDayText: 'Tüm gün',
+ moreLinkText: 'daha fazla',
+ noEventsText: 'Gösterilecek etkinlik yok',
+ };
+
+ var l71 = {
+ code: 'ug',
+ buttonText: {
+ month: 'ئاي',
+ week: 'ھەپتە',
+ day: 'كۈن',
+ list: 'كۈنتەرتىپ',
+ },
+ allDayText: 'پۈتۈن كۈن',
+ };
+
+ var l72 = {
+ code: 'uk',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 7, // The week that contains Jan 1st is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Попередній',
+ next: 'далі',
+ today: 'Сьогодні',
+ month: 'Місяць',
+ week: 'Тиждень',
+ day: 'День',
+ list: 'Порядок денний',
+ },
+ weekText: 'Тиж',
+ allDayText: 'Увесь день',
+ moreLinkText: function(n) {
+ return '+ще ' + n + '...'
+ },
+ noEventsText: 'Немає подій для відображення',
+ };
+
+ var l73 = {
+ code: 'uz',
+ buttonText: {
+ month: 'Oy',
+ week: 'Xafta',
+ day: 'Kun',
+ list: 'Kun tartibi',
+ },
+ allDayText: "Kun bo'yi",
+ moreLinkText: function(n) {
+ return '+ yana ' + n
+ },
+ noEventsText: "Ko'rsatish uchun voqealar yo'q",
+ };
+
+ var l74 = {
+ code: 'vi',
+ week: {
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: 'Trước',
+ next: 'Tiếp',
+ today: 'Hôm nay',
+ month: 'Tháng',
+ week: 'Tuần',
+ day: 'Ngày',
+ list: 'Lịch biểu',
+ },
+ weekText: 'Tu',
+ allDayText: 'Cả ngày',
+ moreLinkText: function(n) {
+ return '+ thêm ' + n
+ },
+ noEventsText: 'Không có sự kiện để hiển thị',
+ };
+
+ var l75 = {
+ code: 'zh-cn',
+ week: {
+ // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
+ dow: 1, // Monday is the first day of the week.
+ doy: 4, // The week that contains Jan 4th is the first week of the year.
+ },
+ buttonText: {
+ prev: '上月',
+ next: '下月',
+ today: '今天',
+ month: '月',
+ week: '周',
+ day: '日',
+ list: '日程',
+ },
+ weekText: '周',
+ allDayText: '全天',
+ moreLinkText: function(n) {
+ return '另外 ' + n + ' 个'
+ },
+ noEventsText: '没有事件显示',
+ };
+
+ var l76 = {
+ code: 'zh-tw',
+ buttonText: {
+ prev: '上月',
+ next: '下月',
+ today: '今天',
+ month: '月',
+ week: '週',
+ day: '天',
+ list: '活動列表',
+ },
+ weekText: '周',
+ allDayText: '整天',
+ moreLinkText: '顯示更多',
+ noEventsText: '没有任何活動',
+ };
+
+ /* eslint max-len: off */
+
+ var localesAll = [
+ l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, l33, l34, l35, l36, l37, l38, l39, l40, l41, l42, l43, l44, l45, l46, l47, l48, l49, l50, l51, l52, l53, l54, l55, l56, l57, l58, l59, l60, l61, l62, l63, l64, l65, l66, l67, l68, l69, l70, l71, l72, l73, l74, l75, l76,
+ ];
+
+ return localesAll;
+
+}());
diff --git a/apps/schoolCalendar/fullcalendar/main.css b/apps/schoolCalendar/fullcalendar/main.css
new file mode 100644
index 000000000..957887b56
--- /dev/null
+++ b/apps/schoolCalendar/fullcalendar/main.css
@@ -0,0 +1,1446 @@
+
+/* classes attached to */
+/* TODO: make fc-event selector work when calender in shadow DOM */
+.fc-not-allowed,
+.fc-not-allowed .fc-event { /* override events' custom cursors */
+ cursor: not-allowed;
+}
+
+/* TODO: not attached to body. attached to specific els. move */
+.fc-unselectable {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-touch-callout: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+.fc {
+ /* layout of immediate children */
+ display: flex;
+ flex-direction: column;
+
+ font-size: 1em
+}
+.fc,
+ .fc *,
+ .fc *:before,
+ .fc *:after {
+ box-sizing: border-box;
+ }
+.fc table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ font-size: 1em; /* normalize cross-browser */
+ }
+.fc th {
+ text-align: center;
+ }
+.fc th,
+ .fc td {
+ vertical-align: top;
+ padding: 0;
+ }
+.fc a[data-navlink] {
+ cursor: pointer;
+ }
+.fc a[data-navlink]:hover {
+ text-decoration: underline;
+ }
+.fc-direction-ltr {
+ direction: ltr;
+ text-align: left;
+}
+.fc-direction-rtl {
+ direction: rtl;
+ text-align: right;
+}
+.fc-theme-standard td,
+ .fc-theme-standard th {
+ border: 1px solid #ddd;
+ border: 1px solid var(--fc-border-color, #ddd);
+ }
+/* for FF, which doesn't expand a 100% div within a table cell. use absolute positioning */
+/* inner-wrappers are responsible for being absolute */
+/* TODO: best place for this? */
+.fc-liquid-hack td,
+ .fc-liquid-hack th {
+ position: relative;
+ }
+
+@font-face {
+ font-family: 'fcicons';
+ src: url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.fc-icon {
+ /* added for fc */
+ display: inline-block;
+ width: 1em;
+ height: 1em;
+ text-align: center;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+
+ /* use !important to prevent issues with browser extensions that change fonts */
+ font-family: 'fcicons' !important;
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+
+ /* Better Font Rendering =========== */
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.fc-icon-chevron-left:before {
+ content: "\e900";
+}
+
+.fc-icon-chevron-right:before {
+ content: "\e901";
+}
+
+.fc-icon-chevrons-left:before {
+ content: "\e902";
+}
+
+.fc-icon-chevrons-right:before {
+ content: "\e903";
+}
+
+.fc-icon-minus-square:before {
+ content: "\e904";
+}
+
+.fc-icon-plus-square:before {
+ content: "\e905";
+}
+
+.fc-icon-x:before {
+ content: "\e906";
+}
+/*
+Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css
+
+These styles only apply when the standard-theme is activated.
+When it's NOT activated, the fc-button classes won't even be in the DOM.
+*/
+.fc {
+
+ /* reset */
+
+}
+.fc .fc-button {
+ border-radius: 0;
+ overflow: visible;
+ text-transform: none;
+ margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+ }
+.fc .fc-button:focus {
+ outline: 1px dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ }
+.fc .fc-button {
+ -webkit-appearance: button;
+ }
+.fc .fc-button:not(:disabled) {
+ cursor: pointer;
+ }
+.fc .fc-button::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+ }
+.fc {
+
+ /* theme */
+
+}
+.fc .fc-button {
+ display: inline-block;
+ font-weight: 400;
+ text-align: center;
+ vertical-align: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-color: transparent;
+ border: 1px solid transparent;
+ padding: 0.4em 0.65em;
+ font-size: 1em;
+ line-height: 1.5;
+ border-radius: 0.25em;
+ }
+.fc .fc-button:hover {
+ text-decoration: none;
+ }
+.fc .fc-button:focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25);
+ }
+.fc .fc-button:disabled {
+ opacity: 0.65;
+ }
+.fc {
+
+ /* "primary" coloring */
+
+}
+.fc .fc-button-primary {
+ color: #fff;
+ color: var(--fc-button-text-color, #fff);
+ background-color: #2C3E50;
+ background-color: var(--fc-button-bg-color, #2C3E50);
+ border-color: #2C3E50;
+ border-color: var(--fc-button-border-color, #2C3E50);
+ }
+.fc .fc-button-primary:hover {
+ color: #fff;
+ color: var(--fc-button-text-color, #fff);
+ background-color: #1e2b37;
+ background-color: var(--fc-button-hover-bg-color, #1e2b37);
+ border-color: #1a252f;
+ border-color: var(--fc-button-hover-border-color, #1a252f);
+ }
+.fc .fc-button-primary:disabled { /* not DRY */
+ color: #fff;
+ color: var(--fc-button-text-color, #fff);
+ background-color: #2C3E50;
+ background-color: var(--fc-button-bg-color, #2C3E50);
+ border-color: #2C3E50;
+ border-color: var(--fc-button-border-color, #2C3E50); /* overrides :hover */
+ }
+.fc .fc-button-primary:focus {
+ box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+ }
+.fc .fc-button-primary:not(:disabled):active,
+ .fc .fc-button-primary:not(:disabled).fc-button-active {
+ color: #fff;
+ color: var(--fc-button-text-color, #fff);
+ background-color: #1a252f;
+ background-color: var(--fc-button-active-bg-color, #1a252f);
+ border-color: #151e27;
+ border-color: var(--fc-button-active-border-color, #151e27);
+ }
+.fc .fc-button-primary:not(:disabled):active:focus,
+ .fc .fc-button-primary:not(:disabled).fc-button-active:focus {
+ box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+ }
+.fc {
+
+ /* icons within buttons */
+
+}
+.fc .fc-button .fc-icon {
+ vertical-align: middle;
+ font-size: 1.5em; /* bump up the size (but don't make it bigger than line-height of button, which is 1.5em also) */
+ }
+.fc .fc-button-group {
+ position: relative;
+ display: inline-flex;
+ vertical-align: middle;
+ }
+.fc .fc-button-group > .fc-button {
+ position: relative;
+ flex: 1 1 auto;
+ }
+.fc .fc-button-group > .fc-button:hover {
+ z-index: 1;
+ }
+.fc .fc-button-group > .fc-button:focus,
+ .fc .fc-button-group > .fc-button:active,
+ .fc .fc-button-group > .fc-button.fc-button-active {
+ z-index: 1;
+ }
+.fc-direction-ltr .fc-button-group > .fc-button:not(:first-child) {
+ margin-left: -1px;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+.fc-direction-ltr .fc-button-group > .fc-button:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+.fc-direction-rtl .fc-button-group > .fc-button:not(:first-child) {
+ margin-right: -1px;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+.fc-direction-rtl .fc-button-group > .fc-button:not(:last-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+.fc .fc-toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+.fc .fc-toolbar.fc-header-toolbar {
+ margin-bottom: 1.5em;
+ }
+.fc .fc-toolbar.fc-footer-toolbar {
+ margin-top: 1.5em;
+ }
+.fc .fc-toolbar-title {
+ font-size: 1.75em;
+ margin: 0;
+ }
+.fc-direction-ltr .fc-toolbar > * > :not(:first-child) {
+ margin-left: .75em; /* space between */
+ }
+.fc-direction-rtl .fc-toolbar > * > :not(:first-child) {
+ margin-right: .75em; /* space between */
+ }
+.fc-direction-rtl .fc-toolbar-ltr { /* when the toolbar-chunk positioning system is explicitly left-to-right */
+ flex-direction: row-reverse;
+ }
+.fc .fc-scroller {
+ -webkit-overflow-scrolling: touch;
+ position: relative; /* for abs-positioned elements within */
+ }
+.fc .fc-scroller-liquid {
+ height: 100%;
+ }
+.fc .fc-scroller-liquid-absolute {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ }
+.fc .fc-scroller-harness {
+ position: relative;
+ overflow: hidden;
+ direction: ltr;
+ /* hack for chrome computing the scroller's right/left wrong for rtl. undone below... */
+ /* TODO: demonstrate in codepen */
+ }
+.fc .fc-scroller-harness-liquid {
+ height: 100%;
+ }
+.fc-direction-rtl .fc-scroller-harness > .fc-scroller { /* undo above hack */
+ direction: rtl;
+ }
+.fc-theme-standard .fc-scrollgrid {
+ border: 1px solid #ddd;
+ border: 1px solid var(--fc-border-color, #ddd); /* bootstrap does this. match */
+ }
+.fc .fc-scrollgrid,
+ .fc .fc-scrollgrid table { /* all tables (self included) */
+ width: 100%; /* because tables don't normally do this */
+ table-layout: fixed;
+ }
+.fc .fc-scrollgrid table { /* inner tables */
+ border-top-style: hidden;
+ border-left-style: hidden;
+ border-right-style: hidden;
+ }
+.fc .fc-scrollgrid {
+
+ border-collapse: separate;
+ border-right-width: 0;
+ border-bottom-width: 0;
+
+ }
+.fc .fc-scrollgrid-liquid {
+ height: 100%;
+ }
+.fc .fc-scrollgrid-section { /* a
elements work best with integers. round up to ensure contents fits
+ }
+ function getSectionHasLiquidHeight(props, sectionConfig) {
+ return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well)
+ }
+ function getAllowYScrolling(props, sectionConfig) {
+ return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars
+ getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars
+ }
+ // TODO: ONLY use `arg`. force out internal function to use same API
+ function renderChunkContent(sectionConfig, chunkConfig, arg) {
+ var expandRows = arg.expandRows;
+ var content = typeof chunkConfig.content === 'function' ?
+ chunkConfig.content(arg) :
+ createElement('table', {
+ className: [
+ chunkConfig.tableClassName,
+ sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '',
+ ].join(' '),
+ style: {
+ minWidth: arg.tableMinWidth,
+ width: arg.clientWidth,
+ height: expandRows ? arg.clientHeight : '', // css `height` on a
serves as a min-height
+ },
+ }, arg.tableColGroupNode, createElement('tbody', {}, typeof chunkConfig.rowContent === 'function' ? chunkConfig.rowContent(arg) : chunkConfig.rowContent));
+ return content;
+ }
+ function isColPropsEqual(cols0, cols1) {
+ return isArraysEqual(cols0, cols1, isPropsEqual);
+ }
+ function renderMicroColGroup(cols, shrinkWidth) {
+ var colNodes = [];
+ /*
+ for ColProps with spans, it would have been great to make a single
+ HOWEVER, Chrome was getting messing up distributing the width to
/
elements with colspans.
+ SOLUTION: making individual
elements makes Chrome behave.
+ */
+ for (var _i = 0, cols_1 = cols; _i < cols_1.length; _i++) {
+ var colProps = cols_1[_i];
+ var span = colProps.span || 1;
+ for (var i = 0; i < span; i += 1) {
+ colNodes.push(createElement("col", { style: {
+ width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''),
+ minWidth: colProps.minWidth || '',
+ } }));
+ }
+ }
+ return createElement.apply(void 0, __spreadArray(['colgroup', {}], colNodes));
+ }
+ function sanitizeShrinkWidth(shrinkWidth) {
+ /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
+ 4 accounts for 2 2-pixel borders. TODO: better solution? */
+ return shrinkWidth == null ? 4 : shrinkWidth;
+ }
+ function hasShrinkWidth(cols) {
+ for (var _i = 0, cols_2 = cols; _i < cols_2.length; _i++) {
+ var col = cols_2[_i];
+ if (col.width === 'shrink') {
+ return true;
+ }
+ }
+ return false;
+ }
+ function getScrollGridClassNames(liquid, context) {
+ var classNames = [
+ 'fc-scrollgrid',
+ context.theme.getClass('table'),
+ ];
+ if (liquid) {
+ classNames.push('fc-scrollgrid-liquid');
+ }
+ return classNames;
+ }
+ function getSectionClassNames(sectionConfig, wholeTableVGrow) {
+ var classNames = [
+ 'fc-scrollgrid-section',
+ "fc-scrollgrid-section-" + sectionConfig.type,
+ sectionConfig.className, // used?
+ ];
+ if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) {
+ classNames.push('fc-scrollgrid-section-liquid');
+ }
+ if (sectionConfig.isSticky) {
+ classNames.push('fc-scrollgrid-section-sticky');
+ }
+ return classNames;
+ }
+ function renderScrollShim(arg) {
+ return (createElement("div", { className: "fc-scrollgrid-sticky-shim", style: {
+ width: arg.clientWidth,
+ minWidth: arg.tableMinWidth,
+ } }));
+ }
+ function getStickyHeaderDates(options) {
+ var stickyHeaderDates = options.stickyHeaderDates;
+ if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
+ stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto';
+ }
+ return stickyHeaderDates;
+ }
+ function getStickyFooterScrollbar(options) {
+ var stickyFooterScrollbar = options.stickyFooterScrollbar;
+ if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
+ stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto';
+ }
+ return stickyFooterScrollbar;
+ }
+
+ var SimpleScrollGrid = /** @class */ (function (_super) {
+ __extends(SimpleScrollGrid, _super);
+ function SimpleScrollGrid() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.processCols = memoize(function (a) { return a; }, isColPropsEqual); // so we get same `cols` props every time
+ // yucky to memoize VNodes, but much more efficient for consumers
+ _this.renderMicroColGroup = memoize(renderMicroColGroup);
+ _this.scrollerRefs = new RefMap();
+ _this.scrollerElRefs = new RefMap(_this._handleScrollerEl.bind(_this));
+ _this.state = {
+ shrinkWidth: null,
+ forceYScrollbars: false,
+ scrollerClientWidths: {},
+ scrollerClientHeights: {},
+ };
+ // TODO: can do a really simple print-view. dont need to join rows
+ _this.handleSizing = function () {
+ _this.setState(__assign({ shrinkWidth: _this.computeShrinkWidth() }, _this.computeScrollerDims()));
+ };
+ return _this;
+ }
+ SimpleScrollGrid.prototype.render = function () {
+ var _a = this, props = _a.props, state = _a.state, context = _a.context;
+ var sectionConfigs = props.sections || [];
+ var cols = this.processCols(props.cols);
+ var microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth);
+ var classNames = getScrollGridClassNames(props.liquid, context);
+ if (props.collapsibleWidth) {
+ classNames.push('fc-scrollgrid-collapsible');
+ }
+ // TODO: make DRY
+ var configCnt = sectionConfigs.length;
+ var configI = 0;
+ var currentConfig;
+ var headSectionNodes = [];
+ var bodySectionNodes = [];
+ var footSectionNodes = [];
+ while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') {
+ headSectionNodes.push(this.renderSection(currentConfig, microColGroupNode));
+ configI += 1;
+ }
+ while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') {
+ bodySectionNodes.push(this.renderSection(currentConfig, microColGroupNode));
+ configI += 1;
+ }
+ while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') {
+ footSectionNodes.push(this.renderSection(currentConfig, microColGroupNode));
+ configI += 1;
+ }
+ // firefox bug: when setting height on table and there is a thead or tfoot,
+ // the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524)
+ // use getCanVGrowWithinCell as a way to detect table-stupid firefox.
+ // if so, use a simpler dom structure, jam everything into a lone tbody.
+ var isBuggy = !getCanVGrowWithinCell();
+ return createElement('table', {
+ className: classNames.join(' '),
+ style: { height: props.height },
+ }, Boolean(!isBuggy && headSectionNodes.length) && createElement.apply(void 0, __spreadArray(['thead', {}], headSectionNodes)), Boolean(!isBuggy && bodySectionNodes.length) && createElement.apply(void 0, __spreadArray(['tbody', {}], bodySectionNodes)), Boolean(!isBuggy && footSectionNodes.length) && createElement.apply(void 0, __spreadArray(['tfoot', {}], footSectionNodes)), isBuggy && createElement.apply(void 0, __spreadArray(__spreadArray(__spreadArray(['tbody', {}], headSectionNodes), bodySectionNodes), footSectionNodes)));
+ };
+ SimpleScrollGrid.prototype.renderSection = function (sectionConfig, microColGroupNode) {
+ if ('outerContent' in sectionConfig) {
+ return (createElement(Fragment, { key: sectionConfig.key }, sectionConfig.outerContent));
+ }
+ return (createElement("tr", { key: sectionConfig.key, className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, microColGroupNode, sectionConfig.chunk)));
+ };
+ SimpleScrollGrid.prototype.renderChunkTd = function (sectionConfig, microColGroupNode, chunkConfig) {
+ if ('outerContent' in chunkConfig) {
+ return chunkConfig.outerContent;
+ }
+ var props = this.props;
+ var _a = this.state, forceYScrollbars = _a.forceYScrollbars, scrollerClientWidths = _a.scrollerClientWidths, scrollerClientHeights = _a.scrollerClientHeights;
+ var needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config?
+ var isLiquid = getSectionHasLiquidHeight(props, sectionConfig);
+ // for `!props.liquid` - is WHOLE scrollgrid natural height?
+ // TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars
+ var overflowY = !props.liquid ? 'visible' :
+ forceYScrollbars ? 'scroll' :
+ !needsYScrolling ? 'hidden' :
+ 'auto';
+ var sectionKey = sectionConfig.key;
+ var content = renderChunkContent(sectionConfig, chunkConfig, {
+ tableColGroupNode: microColGroupNode,
+ tableMinWidth: '',
+ clientWidth: (!props.collapsibleWidth && scrollerClientWidths[sectionKey] !== undefined) ? scrollerClientWidths[sectionKey] : null,
+ clientHeight: scrollerClientHeights[sectionKey] !== undefined ? scrollerClientHeights[sectionKey] : null,
+ expandRows: sectionConfig.expandRows,
+ syncRowHeights: false,
+ rowSyncHeights: [],
+ reportRowHeightChange: function () { },
+ });
+ return (createElement("td", { ref: chunkConfig.elRef },
+ createElement("div", { className: "fc-scroller-harness" + (isLiquid ? ' fc-scroller-harness-liquid' : '') },
+ createElement(Scroller, { ref: this.scrollerRefs.createRef(sectionKey), elRef: this.scrollerElRefs.createRef(sectionKey), overflowY: overflowY, overflowX: !props.liquid ? 'visible' : 'hidden' /* natural height? */, maxHeight: sectionConfig.maxHeight, liquid: isLiquid, liquidIsAbsolute // because its within a harness
+ : true }, content))));
+ };
+ SimpleScrollGrid.prototype._handleScrollerEl = function (scrollerEl, key) {
+ var section = getSectionByKey(this.props.sections, key);
+ if (section) {
+ setRef(section.chunk.scrollerElRef, scrollerEl);
+ }
+ };
+ SimpleScrollGrid.prototype.componentDidMount = function () {
+ this.handleSizing();
+ this.context.addResizeHandler(this.handleSizing);
+ };
+ SimpleScrollGrid.prototype.componentDidUpdate = function () {
+ // TODO: need better solution when state contains non-sizing things
+ this.handleSizing();
+ };
+ SimpleScrollGrid.prototype.componentWillUnmount = function () {
+ this.context.removeResizeHandler(this.handleSizing);
+ };
+ SimpleScrollGrid.prototype.computeShrinkWidth = function () {
+ return hasShrinkWidth(this.props.cols)
+ ? computeShrinkWidth(this.scrollerElRefs.getAll())
+ : 0;
+ };
+ SimpleScrollGrid.prototype.computeScrollerDims = function () {
+ var scrollbarWidth = getScrollbarWidths();
+ var _a = this, scrollerRefs = _a.scrollerRefs, scrollerElRefs = _a.scrollerElRefs;
+ var forceYScrollbars = false;
+ var scrollerClientWidths = {};
+ var scrollerClientHeights = {};
+ for (var sectionKey in scrollerRefs.currentMap) {
+ var scroller = scrollerRefs.currentMap[sectionKey];
+ if (scroller && scroller.needsYScrolling()) {
+ forceYScrollbars = true;
+ break;
+ }
+ }
+ for (var _i = 0, _b = this.props.sections; _i < _b.length; _i++) {
+ var section = _b[_i];
+ var sectionKey = section.key;
+ var scrollerEl = scrollerElRefs.currentMap[sectionKey];
+ if (scrollerEl) {
+ var harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders
+ scrollerClientWidths[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars
+ ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future
+ : 0));
+ scrollerClientHeights[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().height);
+ }
+ }
+ return { forceYScrollbars: forceYScrollbars, scrollerClientWidths: scrollerClientWidths, scrollerClientHeights: scrollerClientHeights };
+ };
+ return SimpleScrollGrid;
+ }(BaseComponent));
+ SimpleScrollGrid.addStateEquality({
+ scrollerClientWidths: isPropsEqual,
+ scrollerClientHeights: isPropsEqual,
+ });
+ function getSectionByKey(sections, key) {
+ for (var _i = 0, sections_1 = sections; _i < sections_1.length; _i++) {
+ var section = sections_1[_i];
+ if (section.key === key) {
+ return section;
+ }
+ }
+ return null;
+ }
+
+ var EventRoot = /** @class */ (function (_super) {
+ __extends(EventRoot, _super);
+ function EventRoot() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.elRef = createRef();
+ return _this;
+ }
+ EventRoot.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ var options = context.options;
+ var seg = props.seg;
+ var eventRange = seg.eventRange;
+ var ui = eventRange.ui;
+ var hookProps = {
+ event: new EventApi(context, eventRange.def, eventRange.instance),
+ view: context.viewApi,
+ timeText: props.timeText,
+ textColor: ui.textColor,
+ backgroundColor: ui.backgroundColor,
+ borderColor: ui.borderColor,
+ isDraggable: !props.disableDragging && computeSegDraggable(seg, context),
+ isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context),
+ isEndResizable: !props.disableResizing && computeSegEndResizable(seg),
+ isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting),
+ isStart: Boolean(seg.isStart),
+ isEnd: Boolean(seg.isEnd),
+ isPast: Boolean(props.isPast),
+ isFuture: Boolean(props.isFuture),
+ isToday: Boolean(props.isToday),
+ isSelected: Boolean(props.isSelected),
+ isDragging: Boolean(props.isDragging),
+ isResizing: Boolean(props.isResizing),
+ };
+ var standardClassNames = getEventClassNames(hookProps).concat(ui.classNames);
+ return (createElement(RenderHook, { hookProps: hookProps, classNames: options.eventClassNames, content: options.eventContent, defaultContent: props.defaultContent, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount, elRef: this.elRef }, function (rootElRef, customClassNames, innerElRef, innerContent) { return props.children(rootElRef, standardClassNames.concat(customClassNames), innerElRef, innerContent, hookProps); }));
+ };
+ EventRoot.prototype.componentDidMount = function () {
+ setElSeg(this.elRef.current, this.props.seg);
+ };
+ /*
+ need to re-assign seg to the element if seg changes, even if the element is the same
+ */
+ EventRoot.prototype.componentDidUpdate = function (prevProps) {
+ var seg = this.props.seg;
+ if (seg !== prevProps.seg) {
+ setElSeg(this.elRef.current, seg);
+ }
+ };
+ return EventRoot;
+ }(BaseComponent));
+
+ // should not be a purecomponent
+ var StandardEvent = /** @class */ (function (_super) {
+ __extends(StandardEvent, _super);
+ function StandardEvent() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ StandardEvent.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ var seg = props.seg;
+ var timeFormat = context.options.eventTimeFormat || props.defaultTimeFormat;
+ var timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd);
+ return (createElement(EventRoot, { seg: seg, timeText: timeText, disableDragging: props.disableDragging, disableResizing: props.disableResizing, defaultContent: props.defaultContent || renderInnerContent$4, isDragging: props.isDragging, isResizing: props.isResizing, isDateSelecting: props.isDateSelecting, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("a", __assign({ className: props.extraClassNames.concat(classNames).join(' '), style: {
+ borderColor: hookProps.borderColor,
+ backgroundColor: hookProps.backgroundColor,
+ }, ref: rootElRef }, getSegAnchorAttrs$1(seg)),
+ createElement("div", { className: "fc-event-main", ref: innerElRef, style: { color: hookProps.textColor } }, innerContent),
+ hookProps.isStartResizable &&
+ createElement("div", { className: "fc-event-resizer fc-event-resizer-start" }),
+ hookProps.isEndResizable &&
+ createElement("div", { className: "fc-event-resizer fc-event-resizer-end" }))); }));
+ };
+ return StandardEvent;
+ }(BaseComponent));
+ function renderInnerContent$4(innerProps) {
+ return (createElement("div", { className: "fc-event-main-frame" },
+ innerProps.timeText && (createElement("div", { className: "fc-event-time" }, innerProps.timeText)),
+ createElement("div", { className: "fc-event-title-container" },
+ createElement("div", { className: "fc-event-title fc-sticky" }, innerProps.event.title || createElement(Fragment, null, "\u00A0")))));
+ }
+ function getSegAnchorAttrs$1(seg) {
+ var url = seg.eventRange.def.url;
+ return url ? { href: url } : {};
+ }
+
+ var NowIndicatorRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) {
+ var options = context.options;
+ var hookProps = {
+ isAxis: props.isAxis,
+ date: context.dateEnv.toDate(props.date),
+ view: context.viewApi,
+ };
+ return (createElement(RenderHook, { hookProps: hookProps, classNames: options.nowIndicatorClassNames, content: options.nowIndicatorContent, didMount: options.nowIndicatorDidMount, willUnmount: options.nowIndicatorWillUnmount }, props.children));
+ })); };
+
+ var DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
+ var DayCellContent = /** @class */ (function (_super) {
+ __extends(DayCellContent, _super);
+ function DayCellContent() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ DayCellContent.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ var options = context.options;
+ var hookProps = refineDayCellHookProps({
+ date: props.date,
+ dateProfile: props.dateProfile,
+ todayRange: props.todayRange,
+ showDayNumber: props.showDayNumber,
+ extraProps: props.extraHookProps,
+ viewApi: context.viewApi,
+ dateEnv: context.dateEnv,
+ });
+ return (createElement(ContentHook, { hookProps: hookProps, content: options.dayCellContent, defaultContent: props.defaultContent }, props.children));
+ };
+ return DayCellContent;
+ }(BaseComponent));
+ function refineDayCellHookProps(raw) {
+ var date = raw.date, dateEnv = raw.dateEnv;
+ var dayMeta = getDateMeta(date, raw.todayRange, null, raw.dateProfile);
+ return __assign(__assign(__assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { dayNumberText: raw.showDayNumber ? dateEnv.format(date, DAY_NUM_FORMAT) : '' }), raw.extraProps);
+ }
+
+ var DayCellRoot = /** @class */ (function (_super) {
+ __extends(DayCellRoot, _super);
+ function DayCellRoot() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.refineHookProps = memoizeObjArg(refineDayCellHookProps);
+ _this.normalizeClassNames = buildClassNameNormalizer();
+ return _this;
+ }
+ DayCellRoot.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ var options = context.options;
+ var hookProps = this.refineHookProps({
+ date: props.date,
+ dateProfile: props.dateProfile,
+ todayRange: props.todayRange,
+ showDayNumber: props.showDayNumber,
+ extraProps: props.extraHookProps,
+ viewApi: context.viewApi,
+ dateEnv: context.dateEnv,
+ });
+ var classNames = getDayClassNames(hookProps, context.theme).concat(hookProps.isDisabled
+ ? [] // don't use custom classNames if disabled
+ : this.normalizeClassNames(options.dayCellClassNames, hookProps));
+ var dataAttrs = hookProps.isDisabled ? {} : {
+ 'data-date': formatDayString(props.date),
+ };
+ return (createElement(MountHook, { hookProps: hookProps, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount, elRef: props.elRef }, function (rootElRef) { return props.children(rootElRef, classNames, dataAttrs, hookProps.isDisabled); }));
+ };
+ return DayCellRoot;
+ }(BaseComponent));
+
+ function renderFill(fillType) {
+ return (createElement("div", { className: "fc-" + fillType }));
+ }
+ var BgEvent = function (props) { return (createElement(EventRoot, { defaultContent: renderInnerContent$3, seg: props.seg /* uselesss i think */, timeText: "", disableDragging: true, disableResizing: true, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent, hookProps) { return (createElement("div", { ref: rootElRef, className: ['fc-bg-event'].concat(classNames).join(' '), style: {
+ backgroundColor: hookProps.backgroundColor,
+ } }, innerContent)); })); };
+ function renderInnerContent$3(props) {
+ var title = props.event.title;
+ return title && (createElement("div", { className: "fc-event-title" }, props.event.title));
+ }
+
+ var WeekNumberRoot = function (props) { return (createElement(ViewContextType.Consumer, null, function (context) {
+ var dateEnv = context.dateEnv, options = context.options;
+ var date = props.date;
+ var format = options.weekNumberFormat || props.defaultFormat;
+ var num = dateEnv.computeWeekNumber(date); // TODO: somehow use for formatting as well?
+ var text = dateEnv.format(date, format);
+ var hookProps = { num: num, text: text, date: date };
+ return (createElement(RenderHook, { hookProps: hookProps, classNames: options.weekNumberClassNames, content: options.weekNumberContent, defaultContent: renderInner, didMount: options.weekNumberDidMount, willUnmount: options.weekNumberWillUnmount }, props.children));
+ })); };
+ function renderInner(innerProps) {
+ return innerProps.text;
+ }
+
+ var PADDING_FROM_VIEWPORT = 10;
+ var Popover = /** @class */ (function (_super) {
+ __extends(Popover, _super);
+ function Popover() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.handleRootEl = function (el) {
+ _this.rootEl = el;
+ if (_this.props.elRef) {
+ setRef(_this.props.elRef, el);
+ }
+ };
+ // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
+ _this.handleDocumentMousedown = function (ev) {
+ // only hide the popover if the click happened outside the popover
+ var target = getEventTargetViaRoot(ev);
+ if (!_this.rootEl.contains(target)) {
+ _this.handleCloseClick();
+ }
+ };
+ _this.handleCloseClick = function () {
+ var onClose = _this.props.onClose;
+ if (onClose) {
+ onClose();
+ }
+ };
+ return _this;
+ }
+ Popover.prototype.render = function () {
+ var theme = this.context.theme;
+ var props = this.props;
+ var classNames = [
+ 'fc-popover',
+ theme.getClass('popover'),
+ ].concat(props.extraClassNames || []);
+ return createPortal(createElement("div", __assign({ className: classNames.join(' ') }, props.extraAttrs, { ref: this.handleRootEl }),
+ createElement("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
+ createElement("span", { className: "fc-popover-title" }, props.title),
+ createElement("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), onClick: this.handleCloseClick })),
+ createElement("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
+ };
+ Popover.prototype.componentDidMount = function () {
+ document.addEventListener('mousedown', this.handleDocumentMousedown);
+ this.updateSize();
+ };
+ Popover.prototype.componentWillUnmount = function () {
+ document.removeEventListener('mousedown', this.handleDocumentMousedown);
+ };
+ Popover.prototype.updateSize = function () {
+ var isRtl = this.context.isRtl;
+ var _a = this.props, alignmentEl = _a.alignmentEl, alignGridTop = _a.alignGridTop;
+ var rootEl = this.rootEl;
+ var alignmentRect = computeClippedClientRect(alignmentEl);
+ if (alignmentRect) {
+ var popoverDims = rootEl.getBoundingClientRect();
+ // position relative to viewport
+ var popoverTop = alignGridTop
+ ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top
+ : alignmentRect.top;
+ var popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
+ // constrain
+ popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
+ popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
+ popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
+ var origin_1 = rootEl.offsetParent.getBoundingClientRect();
+ applyStyle(rootEl, {
+ top: popoverTop - origin_1.top,
+ left: popoverLeft - origin_1.left,
+ });
+ }
+ };
+ return Popover;
+ }(BaseComponent));
+
+ var MorePopover = /** @class */ (function (_super) {
+ __extends(MorePopover, _super);
+ function MorePopover() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.handleRootEl = function (rootEl) {
+ _this.rootEl = rootEl;
+ if (rootEl) {
+ _this.context.registerInteractiveComponent(_this, {
+ el: rootEl,
+ useEventCenter: false,
+ });
+ }
+ else {
+ _this.context.unregisterInteractiveComponent(_this);
+ }
+ };
+ return _this;
+ }
+ MorePopover.prototype.render = function () {
+ var _a = this.context, options = _a.options, dateEnv = _a.dateEnv;
+ var props = this.props;
+ var startDate = props.startDate, todayRange = props.todayRange, dateProfile = props.dateProfile;
+ var title = dateEnv.format(startDate, options.dayPopoverFormat);
+ return (createElement(DayCellRoot, { date: startDate, dateProfile: dateProfile, todayRange: todayRange, elRef: this.handleRootEl }, function (rootElRef, dayClassNames, dataAttrs) { return (createElement(Popover, { elRef: rootElRef, title: title, extraClassNames: ['fc-more-popover'].concat(dayClassNames), extraAttrs: dataAttrs /* TODO: make these time-based when not whole-day? */, parentEl: props.parentEl, alignmentEl: props.alignmentEl, alignGridTop: props.alignGridTop, onClose: props.onClose },
+ createElement(DayCellContent, { date: startDate, dateProfile: dateProfile, todayRange: todayRange }, function (innerElRef, innerContent) { return (innerContent &&
+ createElement("div", { className: "fc-more-popover-misc", ref: innerElRef }, innerContent)); }),
+ props.children)); }));
+ };
+ MorePopover.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+ var _a = this, rootEl = _a.rootEl, props = _a.props;
+ if (positionLeft >= 0 && positionLeft < elWidth &&
+ positionTop >= 0 && positionTop < elHeight) {
+ return {
+ dateProfile: props.dateProfile,
+ dateSpan: __assign({ allDay: true, range: {
+ start: props.startDate,
+ end: props.endDate,
+ } }, props.extraDateSpan),
+ dayEl: rootEl,
+ rect: {
+ left: 0,
+ top: 0,
+ right: elWidth,
+ bottom: elHeight,
+ },
+ layer: 1, // important when comparing with hits from other components
+ };
+ }
+ return null;
+ };
+ return MorePopover;
+ }(DateComponent));
+
+ var MoreLinkRoot = /** @class */ (function (_super) {
+ __extends(MoreLinkRoot, _super);
+ function MoreLinkRoot() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.linkElRef = createRef();
+ _this.state = {
+ isPopoverOpen: false,
+ };
+ _this.handleClick = function (ev) {
+ var _a = _this, props = _a.props, context = _a.context;
+ var moreLinkClick = context.options.moreLinkClick;
+ var date = computeRange(props).start;
+ function buildPublicSeg(seg) {
+ var _a = seg.eventRange, def = _a.def, instance = _a.instance, range = _a.range;
+ return {
+ event: new EventApi(context, def, instance),
+ start: context.dateEnv.toDate(range.start),
+ end: context.dateEnv.toDate(range.end),
+ isStart: seg.isStart,
+ isEnd: seg.isEnd,
+ };
+ }
+ if (typeof moreLinkClick === 'function') {
+ moreLinkClick = moreLinkClick({
+ date: date,
+ allDay: Boolean(props.allDayDate),
+ allSegs: props.allSegs.map(buildPublicSeg),
+ hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
+ jsEvent: ev,
+ view: context.viewApi,
+ });
+ }
+ if (!moreLinkClick || moreLinkClick === 'popover') {
+ _this.setState({ isPopoverOpen: true });
+ }
+ else if (typeof moreLinkClick === 'string') { // a view name
+ context.calendarApi.zoomTo(date, moreLinkClick);
+ }
+ };
+ _this.handlePopoverClose = function () {
+ _this.setState({ isPopoverOpen: false });
+ };
+ return _this;
+ }
+ MoreLinkRoot.prototype.render = function () {
+ var _this = this;
+ var props = this.props;
+ return (createElement(ViewContextType.Consumer, null, function (context) {
+ var viewApi = context.viewApi, options = context.options, calendarApi = context.calendarApi;
+ var moreLinkText = options.moreLinkText;
+ var moreCnt = props.moreCnt;
+ var range = computeRange(props);
+ var hookProps = {
+ num: moreCnt,
+ shortText: "+" + moreCnt,
+ text: typeof moreLinkText === 'function'
+ ? moreLinkText.call(calendarApi, moreCnt)
+ : "+" + moreCnt + " " + moreLinkText,
+ view: viewApi,
+ };
+ return (createElement(Fragment, null,
+ Boolean(props.moreCnt) && (createElement(RenderHook, { elRef: _this.linkElRef, hookProps: hookProps, classNames: options.moreLinkClassNames, content: options.moreLinkContent, defaultContent: props.defaultContent || renderMoreLinkInner$1, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return props.children(rootElRef, ['fc-more-link'].concat(customClassNames), innerElRef, innerContent, _this.handleClick); })),
+ _this.state.isPopoverOpen && (createElement(MorePopover, { startDate: range.start, endDate: range.end, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: props.extraDateSpan, parentEl: _this.parentEl, alignmentEl: props.alignmentElRef.current, alignGridTop: props.alignGridTop, onClose: _this.handlePopoverClose }, props.popoverContent()))));
+ }));
+ };
+ MoreLinkRoot.prototype.componentDidMount = function () {
+ this.updateParentEl();
+ };
+ MoreLinkRoot.prototype.componentDidUpdate = function () {
+ this.updateParentEl();
+ };
+ MoreLinkRoot.prototype.updateParentEl = function () {
+ if (this.linkElRef.current) {
+ this.parentEl = elementClosest(this.linkElRef.current, '.fc-view-harness');
+ }
+ };
+ return MoreLinkRoot;
+ }(BaseComponent));
+ function renderMoreLinkInner$1(props) {
+ return props.text;
+ }
+ function computeRange(props) {
+ if (props.allDayDate) {
+ return {
+ start: props.allDayDate,
+ end: addDays(props.allDayDate, 1),
+ };
+ }
+ var hiddenSegs = props.hiddenSegs;
+ return {
+ start: computeEarliestSegStart(hiddenSegs),
+ end: computeLatestSegEnd(hiddenSegs),
+ };
+ }
+ function computeEarliestSegStart(segs) {
+ return segs.reduce(pickEarliestStart).eventRange.range.start;
+ }
+ function pickEarliestStart(seg0, seg1) {
+ return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
+ }
+ function computeLatestSegEnd(segs) {
+ return segs.reduce(pickLatestEnd).eventRange.range.end;
+ }
+ function pickLatestEnd(seg0, seg1) {
+ return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
+ }
+
+ // exports
+ // --------------------------------------------------------------------------------------------------
+ var version = '5.9.0'; // important to type it, so .d.ts has generic string
+
+ var Calendar = /** @class */ (function (_super) {
+ __extends(Calendar, _super);
+ function Calendar(el, optionOverrides) {
+ if (optionOverrides === void 0) { optionOverrides = {}; }
+ var _this = _super.call(this) || this;
+ _this.isRendering = false;
+ _this.isRendered = false;
+ _this.currentClassNames = [];
+ _this.customContentRenderId = 0; // will affect custom generated classNames?
+ _this.handleAction = function (action) {
+ // actions we know we want to render immediately
+ switch (action.type) {
+ case 'SET_EVENT_DRAG':
+ case 'SET_EVENT_RESIZE':
+ _this.renderRunner.tryDrain();
+ }
+ };
+ _this.handleData = function (data) {
+ _this.currentData = data;
+ _this.renderRunner.request(data.calendarOptions.rerenderDelay);
+ };
+ _this.handleRenderRequest = function () {
+ if (_this.isRendering) {
+ _this.isRendered = true;
+ var currentData_1 = _this.currentData;
+ render(createElement(CalendarRoot, { options: currentData_1.calendarOptions, theme: currentData_1.theme, emitter: currentData_1.emitter }, function (classNames, height, isHeightAuto, forPrint) {
+ _this.setClassNames(classNames);
+ _this.setHeight(height);
+ return (createElement(CustomContentRenderContext.Provider, { value: _this.customContentRenderId },
+ createElement(CalendarContent, __assign({ isHeightAuto: isHeightAuto, forPrint: forPrint }, currentData_1))));
+ }), _this.el);
+ }
+ else if (_this.isRendered) {
+ _this.isRendered = false;
+ unmountComponentAtNode(_this.el);
+ _this.setClassNames([]);
+ _this.setHeight('');
+ }
+ flushToDom();
+ };
+ _this.el = el;
+ _this.renderRunner = new DelayedRunner(_this.handleRenderRequest);
+ new CalendarDataManager({
+ optionOverrides: optionOverrides,
+ calendarApi: _this,
+ onAction: _this.handleAction,
+ onData: _this.handleData,
+ });
+ return _this;
+ }
+ Object.defineProperty(Calendar.prototype, "view", {
+ get: function () { return this.currentData.viewApi; } // for public API
+ ,
+ enumerable: false,
+ configurable: true
+ });
+ Calendar.prototype.render = function () {
+ var wasRendering = this.isRendering;
+ if (!wasRendering) {
+ this.isRendering = true;
+ }
+ else {
+ this.customContentRenderId += 1;
+ }
+ this.renderRunner.request();
+ if (wasRendering) {
+ this.updateSize();
+ }
+ };
+ Calendar.prototype.destroy = function () {
+ if (this.isRendering) {
+ this.isRendering = false;
+ this.renderRunner.request();
+ }
+ };
+ Calendar.prototype.updateSize = function () {
+ _super.prototype.updateSize.call(this);
+ flushToDom();
+ };
+ Calendar.prototype.batchRendering = function (func) {
+ this.renderRunner.pause('batchRendering');
+ func();
+ this.renderRunner.resume('batchRendering');
+ };
+ Calendar.prototype.pauseRendering = function () {
+ this.renderRunner.pause('pauseRendering');
+ };
+ Calendar.prototype.resumeRendering = function () {
+ this.renderRunner.resume('pauseRendering', true);
+ };
+ Calendar.prototype.resetOptions = function (optionOverrides, append) {
+ this.currentDataManager.resetOptions(optionOverrides, append);
+ };
+ Calendar.prototype.setClassNames = function (classNames) {
+ if (!isArraysEqual(classNames, this.currentClassNames)) {
+ var classList = this.el.classList;
+ for (var _i = 0, _a = this.currentClassNames; _i < _a.length; _i++) {
+ var className = _a[_i];
+ classList.remove(className);
+ }
+ for (var _b = 0, classNames_1 = classNames; _b < classNames_1.length; _b++) {
+ var className = classNames_1[_b];
+ classList.add(className);
+ }
+ this.currentClassNames = classNames;
+ }
+ };
+ Calendar.prototype.setHeight = function (height) {
+ applyStyleProp(this.el, 'height', height);
+ };
+ return Calendar;
+ }(CalendarApi));
+
+ config.touchMouseIgnoreWait = 500;
+ var ignoreMouseDepth = 0;
+ var listenerCnt = 0;
+ var isWindowTouchMoveCancelled = false;
+ /*
+ Uses a "pointer" abstraction, which monitors UI events for both mouse and touch.
+ Tracks when the pointer "drags" on a certain element, meaning down+move+up.
+
+ Also, tracks if there was touch-scrolling.
+ Also, can prevent touch-scrolling from happening.
+ Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement.
+
+ emits:
+ - pointerdown
+ - pointermove
+ - pointerup
+ */
+ var PointerDragging = /** @class */ (function () {
+ function PointerDragging(containerEl) {
+ var _this = this;
+ this.subjectEl = null;
+ // options that can be directly assigned by caller
+ this.selector = ''; // will cause subjectEl in all emitted events to be this element
+ this.handleSelector = '';
+ this.shouldIgnoreMove = false;
+ this.shouldWatchScroll = true; // for simulating pointermove on scroll
+ // internal states
+ this.isDragging = false;
+ this.isTouchDragging = false;
+ this.wasTouchScroll = false;
+ // Mouse
+ // ----------------------------------------------------------------------------------------------------
+ this.handleMouseDown = function (ev) {
+ if (!_this.shouldIgnoreMouse() &&
+ isPrimaryMouseButton(ev) &&
+ _this.tryStart(ev)) {
+ var pev = _this.createEventFromMouse(ev, true);
+ _this.emitter.trigger('pointerdown', pev);
+ _this.initScrollWatch(pev);
+ if (!_this.shouldIgnoreMove) {
+ document.addEventListener('mousemove', _this.handleMouseMove);
+ }
+ document.addEventListener('mouseup', _this.handleMouseUp);
+ }
+ };
+ this.handleMouseMove = function (ev) {
+ var pev = _this.createEventFromMouse(ev);
+ _this.recordCoords(pev);
+ _this.emitter.trigger('pointermove', pev);
+ };
+ this.handleMouseUp = function (ev) {
+ document.removeEventListener('mousemove', _this.handleMouseMove);
+ document.removeEventListener('mouseup', _this.handleMouseUp);
+ _this.emitter.trigger('pointerup', _this.createEventFromMouse(ev));
+ _this.cleanup(); // call last so that pointerup has access to props
+ };
+ // Touch
+ // ----------------------------------------------------------------------------------------------------
+ this.handleTouchStart = function (ev) {
+ if (_this.tryStart(ev)) {
+ _this.isTouchDragging = true;
+ var pev = _this.createEventFromTouch(ev, true);
+ _this.emitter.trigger('pointerdown', pev);
+ _this.initScrollWatch(pev);
+ // unlike mouse, need to attach to target, not document
+ // https://stackoverflow.com/a/45760014
+ var targetEl = ev.target;
+ if (!_this.shouldIgnoreMove) {
+ targetEl.addEventListener('touchmove', _this.handleTouchMove);
+ }
+ targetEl.addEventListener('touchend', _this.handleTouchEnd);
+ targetEl.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end
+ // attach a handler to get called when ANY scroll action happens on the page.
+ // this was impossible to do with normal on/off because 'scroll' doesn't bubble.
+ // http://stackoverflow.com/a/32954565/96342
+ window.addEventListener('scroll', _this.handleTouchScroll, true);
+ }
+ };
+ this.handleTouchMove = function (ev) {
+ var pev = _this.createEventFromTouch(ev);
+ _this.recordCoords(pev);
+ _this.emitter.trigger('pointermove', pev);
+ };
+ this.handleTouchEnd = function (ev) {
+ if (_this.isDragging) { // done to guard against touchend followed by touchcancel
+ var targetEl = ev.target;
+ targetEl.removeEventListener('touchmove', _this.handleTouchMove);
+ targetEl.removeEventListener('touchend', _this.handleTouchEnd);
+ targetEl.removeEventListener('touchcancel', _this.handleTouchEnd);
+ window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true
+ _this.emitter.trigger('pointerup', _this.createEventFromTouch(ev));
+ _this.cleanup(); // call last so that pointerup has access to props
+ _this.isTouchDragging = false;
+ startIgnoringMouse();
+ }
+ };
+ this.handleTouchScroll = function () {
+ _this.wasTouchScroll = true;
+ };
+ this.handleScroll = function (ev) {
+ if (!_this.shouldIgnoreMove) {
+ var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX;
+ var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY;
+ _this.emitter.trigger('pointermove', {
+ origEvent: ev,
+ isTouch: _this.isTouchDragging,
+ subjectEl: _this.subjectEl,
+ pageX: pageX,
+ pageY: pageY,
+ deltaX: pageX - _this.origPageX,
+ deltaY: pageY - _this.origPageY,
+ });
+ }
+ };
+ this.containerEl = containerEl;
+ this.emitter = new Emitter();
+ containerEl.addEventListener('mousedown', this.handleMouseDown);
+ containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true });
+ listenerCreated();
+ }
+ PointerDragging.prototype.destroy = function () {
+ this.containerEl.removeEventListener('mousedown', this.handleMouseDown);
+ this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
+ listenerDestroyed();
+ };
+ PointerDragging.prototype.tryStart = function (ev) {
+ var subjectEl = this.querySubjectEl(ev);
+ var downEl = ev.target;
+ if (subjectEl &&
+ (!this.handleSelector || elementClosest(downEl, this.handleSelector))) {
+ this.subjectEl = subjectEl;
+ this.isDragging = true; // do this first so cancelTouchScroll will work
+ this.wasTouchScroll = false;
+ return true;
+ }
+ return false;
+ };
+ PointerDragging.prototype.cleanup = function () {
+ isWindowTouchMoveCancelled = false;
+ this.isDragging = false;
+ this.subjectEl = null;
+ // keep wasTouchScroll around for later access
+ this.destroyScrollWatch();
+ };
+ PointerDragging.prototype.querySubjectEl = function (ev) {
+ if (this.selector) {
+ return elementClosest(ev.target, this.selector);
+ }
+ return this.containerEl;
+ };
+ PointerDragging.prototype.shouldIgnoreMouse = function () {
+ return ignoreMouseDepth || this.isTouchDragging;
+ };
+ // can be called by user of this class, to cancel touch-based scrolling for the current drag
+ PointerDragging.prototype.cancelTouchScroll = function () {
+ if (this.isDragging) {
+ isWindowTouchMoveCancelled = true;
+ }
+ };
+ // Scrolling that simulates pointermoves
+ // ----------------------------------------------------------------------------------------------------
+ PointerDragging.prototype.initScrollWatch = function (ev) {
+ if (this.shouldWatchScroll) {
+ this.recordCoords(ev);
+ window.addEventListener('scroll', this.handleScroll, true); // useCapture=true
+ }
+ };
+ PointerDragging.prototype.recordCoords = function (ev) {
+ if (this.shouldWatchScroll) {
+ this.prevPageX = ev.pageX;
+ this.prevPageY = ev.pageY;
+ this.prevScrollX = window.pageXOffset;
+ this.prevScrollY = window.pageYOffset;
+ }
+ };
+ PointerDragging.prototype.destroyScrollWatch = function () {
+ if (this.shouldWatchScroll) {
+ window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true
+ }
+ };
+ // Event Normalization
+ // ----------------------------------------------------------------------------------------------------
+ PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) {
+ var deltaX = 0;
+ var deltaY = 0;
+ // TODO: repeat code
+ if (isFirst) {
+ this.origPageX = ev.pageX;
+ this.origPageY = ev.pageY;
+ }
+ else {
+ deltaX = ev.pageX - this.origPageX;
+ deltaY = ev.pageY - this.origPageY;
+ }
+ return {
+ origEvent: ev,
+ isTouch: false,
+ subjectEl: this.subjectEl,
+ pageX: ev.pageX,
+ pageY: ev.pageY,
+ deltaX: deltaX,
+ deltaY: deltaY,
+ };
+ };
+ PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) {
+ var touches = ev.touches;
+ var pageX;
+ var pageY;
+ var deltaX = 0;
+ var deltaY = 0;
+ // if touch coords available, prefer,
+ // because FF would give bad ev.pageX ev.pageY
+ if (touches && touches.length) {
+ pageX = touches[0].pageX;
+ pageY = touches[0].pageY;
+ }
+ else {
+ pageX = ev.pageX;
+ pageY = ev.pageY;
+ }
+ // TODO: repeat code
+ if (isFirst) {
+ this.origPageX = pageX;
+ this.origPageY = pageY;
+ }
+ else {
+ deltaX = pageX - this.origPageX;
+ deltaY = pageY - this.origPageY;
+ }
+ return {
+ origEvent: ev,
+ isTouch: true,
+ subjectEl: this.subjectEl,
+ pageX: pageX,
+ pageY: pageY,
+ deltaX: deltaX,
+ deltaY: deltaY,
+ };
+ };
+ return PointerDragging;
+ }());
+ // Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
+ function isPrimaryMouseButton(ev) {
+ return ev.button === 0 && !ev.ctrlKey;
+ }
+ // Ignoring fake mouse events generated by touch
+ // ----------------------------------------------------------------------------------------------------
+ function startIgnoringMouse() {
+ ignoreMouseDepth += 1;
+ setTimeout(function () {
+ ignoreMouseDepth -= 1;
+ }, config.touchMouseIgnoreWait);
+ }
+ // We want to attach touchmove as early as possible for Safari
+ // ----------------------------------------------------------------------------------------------------
+ function listenerCreated() {
+ listenerCnt += 1;
+ if (listenerCnt === 1) {
+ window.addEventListener('touchmove', onWindowTouchMove, { passive: false });
+ }
+ }
+ function listenerDestroyed() {
+ listenerCnt -= 1;
+ if (!listenerCnt) {
+ window.removeEventListener('touchmove', onWindowTouchMove, { passive: false });
+ }
+ }
+ function onWindowTouchMove(ev) {
+ if (isWindowTouchMoveCancelled) {
+ ev.preventDefault();
+ }
+ }
+
+ /*
+ An effect in which an element follows the movement of a pointer across the screen.
+ The moving element is a clone of some other element.
+ Must call start + handleMove + stop.
+ */
+ var ElementMirror = /** @class */ (function () {
+ function ElementMirror() {
+ this.isVisible = false; // must be explicitly enabled
+ this.sourceEl = null;
+ this.mirrorEl = null;
+ this.sourceElRect = null; // screen coords relative to viewport
+ // options that can be set directly by caller
+ this.parentNode = document.body; // HIGHLY SUGGESTED to set this to sidestep ShadowDOM issues
+ this.zIndex = 9999;
+ this.revertDuration = 0;
+ }
+ ElementMirror.prototype.start = function (sourceEl, pageX, pageY) {
+ this.sourceEl = sourceEl;
+ this.sourceElRect = this.sourceEl.getBoundingClientRect();
+ this.origScreenX = pageX - window.pageXOffset;
+ this.origScreenY = pageY - window.pageYOffset;
+ this.deltaX = 0;
+ this.deltaY = 0;
+ this.updateElPosition();
+ };
+ ElementMirror.prototype.handleMove = function (pageX, pageY) {
+ this.deltaX = (pageX - window.pageXOffset) - this.origScreenX;
+ this.deltaY = (pageY - window.pageYOffset) - this.origScreenY;
+ this.updateElPosition();
+ };
+ // can be called before start
+ ElementMirror.prototype.setIsVisible = function (bool) {
+ if (bool) {
+ if (!this.isVisible) {
+ if (this.mirrorEl) {
+ this.mirrorEl.style.display = '';
+ }
+ this.isVisible = bool; // needs to happen before updateElPosition
+ this.updateElPosition(); // because was not updating the position while invisible
+ }
+ }
+ else if (this.isVisible) {
+ if (this.mirrorEl) {
+ this.mirrorEl.style.display = 'none';
+ }
+ this.isVisible = bool;
+ }
+ };
+ // always async
+ ElementMirror.prototype.stop = function (needsRevertAnimation, callback) {
+ var _this = this;
+ var done = function () {
+ _this.cleanup();
+ callback();
+ };
+ if (needsRevertAnimation &&
+ this.mirrorEl &&
+ this.isVisible &&
+ this.revertDuration && // if 0, transition won't work
+ (this.deltaX || this.deltaY) // if same coords, transition won't work
+ ) {
+ this.doRevertAnimation(done, this.revertDuration);
+ }
+ else {
+ setTimeout(done, 0);
+ }
+ };
+ ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) {
+ var mirrorEl = this.mirrorEl;
+ var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened
+ mirrorEl.style.transition =
+ 'top ' + revertDuration + 'ms,' +
+ 'left ' + revertDuration + 'ms';
+ applyStyle(mirrorEl, {
+ left: finalSourceElRect.left,
+ top: finalSourceElRect.top,
+ });
+ whenTransitionDone(mirrorEl, function () {
+ mirrorEl.style.transition = '';
+ callback();
+ });
+ };
+ ElementMirror.prototype.cleanup = function () {
+ if (this.mirrorEl) {
+ removeElement(this.mirrorEl);
+ this.mirrorEl = null;
+ }
+ this.sourceEl = null;
+ };
+ ElementMirror.prototype.updateElPosition = function () {
+ if (this.sourceEl && this.isVisible) {
+ applyStyle(this.getMirrorEl(), {
+ left: this.sourceElRect.left + this.deltaX,
+ top: this.sourceElRect.top + this.deltaY,
+ });
+ }
+ };
+ ElementMirror.prototype.getMirrorEl = function () {
+ var sourceElRect = this.sourceElRect;
+ var mirrorEl = this.mirrorEl;
+ if (!mirrorEl) {
+ mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true
+ // we don't want long taps or any mouse interaction causing selection/menus.
+ // would use preventSelection(), but that prevents selectstart, causing problems.
+ mirrorEl.classList.add('fc-unselectable');
+ mirrorEl.classList.add('fc-event-dragging');
+ applyStyle(mirrorEl, {
+ position: 'fixed',
+ zIndex: this.zIndex,
+ visibility: '',
+ boxSizing: 'border-box',
+ width: sourceElRect.right - sourceElRect.left,
+ height: sourceElRect.bottom - sourceElRect.top,
+ right: 'auto',
+ bottom: 'auto',
+ margin: 0,
+ });
+ this.parentNode.appendChild(mirrorEl);
+ }
+ return mirrorEl;
+ };
+ return ElementMirror;
+ }());
+
+ /*
+ Is a cache for a given element's scroll information (all the info that ScrollController stores)
+ in addition the "client rectangle" of the element.. the area within the scrollbars.
+
+ The cache can be in one of two modes:
+ - doesListening:false - ignores when the container is scrolled by someone else
+ - doesListening:true - watch for scrolling and update the cache
+ */
+ var ScrollGeomCache = /** @class */ (function (_super) {
+ __extends(ScrollGeomCache, _super);
+ function ScrollGeomCache(scrollController, doesListening) {
+ var _this = _super.call(this) || this;
+ _this.handleScroll = function () {
+ _this.scrollTop = _this.scrollController.getScrollTop();
+ _this.scrollLeft = _this.scrollController.getScrollLeft();
+ _this.handleScrollChange();
+ };
+ _this.scrollController = scrollController;
+ _this.doesListening = doesListening;
+ _this.scrollTop = _this.origScrollTop = scrollController.getScrollTop();
+ _this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft();
+ _this.scrollWidth = scrollController.getScrollWidth();
+ _this.scrollHeight = scrollController.getScrollHeight();
+ _this.clientWidth = scrollController.getClientWidth();
+ _this.clientHeight = scrollController.getClientHeight();
+ _this.clientRect = _this.computeClientRect(); // do last in case it needs cached values
+ if (_this.doesListening) {
+ _this.getEventTarget().addEventListener('scroll', _this.handleScroll);
+ }
+ return _this;
+ }
+ ScrollGeomCache.prototype.destroy = function () {
+ if (this.doesListening) {
+ this.getEventTarget().removeEventListener('scroll', this.handleScroll);
+ }
+ };
+ ScrollGeomCache.prototype.getScrollTop = function () {
+ return this.scrollTop;
+ };
+ ScrollGeomCache.prototype.getScrollLeft = function () {
+ return this.scrollLeft;
+ };
+ ScrollGeomCache.prototype.setScrollTop = function (top) {
+ this.scrollController.setScrollTop(top);
+ if (!this.doesListening) {
+ // we are not relying on the element to normalize out-of-bounds scroll values
+ // so we need to sanitize ourselves
+ this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0);
+ this.handleScrollChange();
+ }
+ };
+ ScrollGeomCache.prototype.setScrollLeft = function (top) {
+ this.scrollController.setScrollLeft(top);
+ if (!this.doesListening) {
+ // we are not relying on the element to normalize out-of-bounds scroll values
+ // so we need to sanitize ourselves
+ this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0);
+ this.handleScrollChange();
+ }
+ };
+ ScrollGeomCache.prototype.getClientWidth = function () {
+ return this.clientWidth;
+ };
+ ScrollGeomCache.prototype.getClientHeight = function () {
+ return this.clientHeight;
+ };
+ ScrollGeomCache.prototype.getScrollWidth = function () {
+ return this.scrollWidth;
+ };
+ ScrollGeomCache.prototype.getScrollHeight = function () {
+ return this.scrollHeight;
+ };
+ ScrollGeomCache.prototype.handleScrollChange = function () {
+ };
+ return ScrollGeomCache;
+ }(ScrollController));
+
+ var ElementScrollGeomCache = /** @class */ (function (_super) {
+ __extends(ElementScrollGeomCache, _super);
+ function ElementScrollGeomCache(el, doesListening) {
+ return _super.call(this, new ElementScrollController(el), doesListening) || this;
+ }
+ ElementScrollGeomCache.prototype.getEventTarget = function () {
+ return this.scrollController.el;
+ };
+ ElementScrollGeomCache.prototype.computeClientRect = function () {
+ return computeInnerRect(this.scrollController.el);
+ };
+ return ElementScrollGeomCache;
+ }(ScrollGeomCache));
+
+ var WindowScrollGeomCache = /** @class */ (function (_super) {
+ __extends(WindowScrollGeomCache, _super);
+ function WindowScrollGeomCache(doesListening) {
+ return _super.call(this, new WindowScrollController(), doesListening) || this;
+ }
+ WindowScrollGeomCache.prototype.getEventTarget = function () {
+ return window;
+ };
+ WindowScrollGeomCache.prototype.computeClientRect = function () {
+ return {
+ left: this.scrollLeft,
+ right: this.scrollLeft + this.clientWidth,
+ top: this.scrollTop,
+ bottom: this.scrollTop + this.clientHeight,
+ };
+ };
+ // the window is the only scroll object that changes it's rectangle relative
+ // to the document's topleft as it scrolls
+ WindowScrollGeomCache.prototype.handleScrollChange = function () {
+ this.clientRect = this.computeClientRect();
+ };
+ return WindowScrollGeomCache;
+ }(ScrollGeomCache));
+
+ // If available we are using native "performance" API instead of "Date"
+ // Read more about it on MDN:
+ // https://developer.mozilla.org/en-US/docs/Web/API/Performance
+ var getTime = typeof performance === 'function' ? performance.now : Date.now;
+ /*
+ For a pointer interaction, automatically scrolls certain scroll containers when the pointer
+ approaches the edge.
+
+ The caller must call start + handleMove + stop.
+ */
+ var AutoScroller = /** @class */ (function () {
+ function AutoScroller() {
+ var _this = this;
+ // options that can be set by caller
+ this.isEnabled = true;
+ this.scrollQuery = [window, '.fc-scroller'];
+ this.edgeThreshold = 50; // pixels
+ this.maxVelocity = 300; // pixels per second
+ // internal state
+ this.pointerScreenX = null;
+ this.pointerScreenY = null;
+ this.isAnimating = false;
+ this.scrollCaches = null;
+ // protect against the initial pointerdown being too close to an edge and starting the scroll
+ this.everMovedUp = false;
+ this.everMovedDown = false;
+ this.everMovedLeft = false;
+ this.everMovedRight = false;
+ this.animate = function () {
+ if (_this.isAnimating) { // wasn't cancelled between animation calls
+ var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset);
+ if (edge) {
+ var now = getTime();
+ _this.handleSide(edge, (now - _this.msSinceRequest) / 1000);
+ _this.requestAnimation(now);
+ }
+ else {
+ _this.isAnimating = false; // will stop animation
+ }
+ }
+ };
+ }
+ AutoScroller.prototype.start = function (pageX, pageY, scrollStartEl) {
+ if (this.isEnabled) {
+ this.scrollCaches = this.buildCaches(scrollStartEl);
+ this.pointerScreenX = null;
+ this.pointerScreenY = null;
+ this.everMovedUp = false;
+ this.everMovedDown = false;
+ this.everMovedLeft = false;
+ this.everMovedRight = false;
+ this.handleMove(pageX, pageY);
+ }
+ };
+ AutoScroller.prototype.handleMove = function (pageX, pageY) {
+ if (this.isEnabled) {
+ var pointerScreenX = pageX - window.pageXOffset;
+ var pointerScreenY = pageY - window.pageYOffset;
+ var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY;
+ var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX;
+ if (yDelta < 0) {
+ this.everMovedUp = true;
+ }
+ else if (yDelta > 0) {
+ this.everMovedDown = true;
+ }
+ if (xDelta < 0) {
+ this.everMovedLeft = true;
+ }
+ else if (xDelta > 0) {
+ this.everMovedRight = true;
+ }
+ this.pointerScreenX = pointerScreenX;
+ this.pointerScreenY = pointerScreenY;
+ if (!this.isAnimating) {
+ this.isAnimating = true;
+ this.requestAnimation(getTime());
+ }
+ }
+ };
+ AutoScroller.prototype.stop = function () {
+ if (this.isEnabled) {
+ this.isAnimating = false; // will stop animation
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ scrollCache.destroy();
+ }
+ this.scrollCaches = null;
+ }
+ };
+ AutoScroller.prototype.requestAnimation = function (now) {
+ this.msSinceRequest = now;
+ requestAnimationFrame(this.animate);
+ };
+ AutoScroller.prototype.handleSide = function (edge, seconds) {
+ var scrollCache = edge.scrollCache;
+ var edgeThreshold = this.edgeThreshold;
+ var invDistance = edgeThreshold - edge.distance;
+ var velocity = // the closer to the edge, the faster we scroll
+ ((invDistance * invDistance) / (edgeThreshold * edgeThreshold)) * // quadratic
+ this.maxVelocity * seconds;
+ var sign = 1;
+ switch (edge.name) {
+ case 'left':
+ sign = -1;
+ // falls through
+ case 'right':
+ scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign);
+ break;
+ case 'top':
+ sign = -1;
+ // falls through
+ case 'bottom':
+ scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign);
+ break;
+ }
+ };
+ // left/top are relative to document topleft
+ AutoScroller.prototype.computeBestEdge = function (left, top) {
+ var edgeThreshold = this.edgeThreshold;
+ var bestSide = null;
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ var rect = scrollCache.clientRect;
+ var leftDist = left - rect.left;
+ var rightDist = rect.right - left;
+ var topDist = top - rect.top;
+ var bottomDist = rect.bottom - top;
+ // completely within the rect?
+ if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) {
+ if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() &&
+ (!bestSide || bestSide.distance > topDist)) {
+ bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist };
+ }
+ if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() &&
+ (!bestSide || bestSide.distance > bottomDist)) {
+ bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist };
+ }
+ if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() &&
+ (!bestSide || bestSide.distance > leftDist)) {
+ bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist };
+ }
+ if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() &&
+ (!bestSide || bestSide.distance > rightDist)) {
+ bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist };
+ }
+ }
+ }
+ return bestSide;
+ };
+ AutoScroller.prototype.buildCaches = function (scrollStartEl) {
+ return this.queryScrollEls(scrollStartEl).map(function (el) {
+ if (el === window) {
+ return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls
+ }
+ return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls
+ });
+ };
+ AutoScroller.prototype.queryScrollEls = function (scrollStartEl) {
+ var els = [];
+ for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) {
+ var query = _a[_i];
+ if (typeof query === 'object') {
+ els.push(query);
+ }
+ else {
+ els.push.apply(els, Array.prototype.slice.call(getElRoot(scrollStartEl).querySelectorAll(query)));
+ }
+ }
+ return els;
+ };
+ return AutoScroller;
+ }());
+
+ /*
+ Monitors dragging on an element. Has a number of high-level features:
+ - minimum distance required before dragging
+ - minimum wait time ("delay") before dragging
+ - a mirror element that follows the pointer
+ */
+ var FeaturefulElementDragging = /** @class */ (function (_super) {
+ __extends(FeaturefulElementDragging, _super);
+ function FeaturefulElementDragging(containerEl, selector) {
+ var _this = _super.call(this, containerEl) || this;
+ _this.containerEl = containerEl;
+ // options that can be directly set by caller
+ // the caller can also set the PointerDragging's options as well
+ _this.delay = null;
+ _this.minDistance = 0;
+ _this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag
+ _this.mirrorNeedsRevert = false;
+ _this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup
+ _this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation
+ _this.isDelayEnded = false;
+ _this.isDistanceSurpassed = false;
+ _this.delayTimeoutId = null;
+ _this.onPointerDown = function (ev) {
+ if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going
+ _this.isInteracting = true;
+ _this.isDelayEnded = false;
+ _this.isDistanceSurpassed = false;
+ preventSelection(document.body);
+ preventContextMenu(document.body);
+ // prevent links from being visited if there's an eventual drag.
+ // also prevents selection in older browsers (maybe?).
+ // not necessary for touch, besides, browser would complain about passiveness.
+ if (!ev.isTouch) {
+ ev.origEvent.preventDefault();
+ }
+ _this.emitter.trigger('pointerdown', ev);
+ if (_this.isInteracting && // not destroyed via pointerdown handler
+ !_this.pointer.shouldIgnoreMove) {
+ // actions related to initiating dragstart+dragmove+dragend...
+ _this.mirror.setIsVisible(false); // reset. caller must set-visible
+ _this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down
+ _this.startDelay(ev);
+ if (!_this.minDistance) {
+ _this.handleDistanceSurpassed(ev);
+ }
+ }
+ }
+ };
+ _this.onPointerMove = function (ev) {
+ if (_this.isInteracting) {
+ _this.emitter.trigger('pointermove', ev);
+ if (!_this.isDistanceSurpassed) {
+ var minDistance = _this.minDistance;
+ var distanceSq = void 0; // current distance from the origin, squared
+ var deltaX = ev.deltaX, deltaY = ev.deltaY;
+ distanceSq = deltaX * deltaX + deltaY * deltaY;
+ if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
+ _this.handleDistanceSurpassed(ev);
+ }
+ }
+ if (_this.isDragging) {
+ // a real pointer move? (not one simulated by scrolling)
+ if (ev.origEvent.type !== 'scroll') {
+ _this.mirror.handleMove(ev.pageX, ev.pageY);
+ _this.autoScroller.handleMove(ev.pageX, ev.pageY);
+ }
+ _this.emitter.trigger('dragmove', ev);
+ }
+ }
+ };
+ _this.onPointerUp = function (ev) {
+ if (_this.isInteracting) {
+ _this.isInteracting = false;
+ allowSelection(document.body);
+ allowContextMenu(document.body);
+ _this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert
+ if (_this.isDragging) {
+ _this.autoScroller.stop();
+ _this.tryStopDrag(ev); // which will stop the mirror
+ }
+ if (_this.delayTimeoutId) {
+ clearTimeout(_this.delayTimeoutId);
+ _this.delayTimeoutId = null;
+ }
+ }
+ };
+ var pointer = _this.pointer = new PointerDragging(containerEl);
+ pointer.emitter.on('pointerdown', _this.onPointerDown);
+ pointer.emitter.on('pointermove', _this.onPointerMove);
+ pointer.emitter.on('pointerup', _this.onPointerUp);
+ if (selector) {
+ pointer.selector = selector;
+ }
+ _this.mirror = new ElementMirror();
+ _this.autoScroller = new AutoScroller();
+ return _this;
+ }
+ FeaturefulElementDragging.prototype.destroy = function () {
+ this.pointer.destroy();
+ // HACK: simulate a pointer-up to end the current drag
+ // TODO: fire 'dragend' directly and stop interaction. discourage use of pointerup event (b/c might not fire)
+ this.onPointerUp({});
+ };
+ FeaturefulElementDragging.prototype.startDelay = function (ev) {
+ var _this = this;
+ if (typeof this.delay === 'number') {
+ this.delayTimeoutId = setTimeout(function () {
+ _this.delayTimeoutId = null;
+ _this.handleDelayEnd(ev);
+ }, this.delay); // not assignable to number!
+ }
+ else {
+ this.handleDelayEnd(ev);
+ }
+ };
+ FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) {
+ this.isDelayEnded = true;
+ this.tryStartDrag(ev);
+ };
+ FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) {
+ this.isDistanceSurpassed = true;
+ this.tryStartDrag(ev);
+ };
+ FeaturefulElementDragging.prototype.tryStartDrag = function (ev) {
+ if (this.isDelayEnded && this.isDistanceSurpassed) {
+ if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) {
+ this.isDragging = true;
+ this.mirrorNeedsRevert = false;
+ this.autoScroller.start(ev.pageX, ev.pageY, this.containerEl);
+ this.emitter.trigger('dragstart', ev);
+ if (this.touchScrollAllowed === false) {
+ this.pointer.cancelTouchScroll();
+ }
+ }
+ }
+ };
+ FeaturefulElementDragging.prototype.tryStopDrag = function (ev) {
+ // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events
+ // that come from the document to fire beforehand. much more convenient this way.
+ this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev));
+ };
+ FeaturefulElementDragging.prototype.stopDrag = function (ev) {
+ this.isDragging = false;
+ this.emitter.trigger('dragend', ev);
+ };
+ // fill in the implementations...
+ FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) {
+ this.pointer.shouldIgnoreMove = bool;
+ };
+ FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) {
+ this.mirror.setIsVisible(bool);
+ };
+ FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
+ this.mirrorNeedsRevert = bool;
+ };
+ FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) {
+ this.autoScroller.isEnabled = bool;
+ };
+ return FeaturefulElementDragging;
+ }(ElementDragging));
+
+ /*
+ When this class is instantiated, it records the offset of an element (relative to the document topleft),
+ and continues to monitor scrolling, updating the cached coordinates if it needs to.
+ Does not access the DOM after instantiation, so highly performant.
+
+ Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element
+ and an determine if a given point is inside the combined clipping rectangle.
+ */
+ var OffsetTracker = /** @class */ (function () {
+ function OffsetTracker(el) {
+ this.origRect = computeRect(el);
+ // will work fine for divs that have overflow:hidden
+ this.scrollCaches = getClippingParents(el).map(function (scrollEl) { return new ElementScrollGeomCache(scrollEl, true); });
+ }
+ OffsetTracker.prototype.destroy = function () {
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ scrollCache.destroy();
+ }
+ };
+ OffsetTracker.prototype.computeLeft = function () {
+ var left = this.origRect.left;
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ left += scrollCache.origScrollLeft - scrollCache.getScrollLeft();
+ }
+ return left;
+ };
+ OffsetTracker.prototype.computeTop = function () {
+ var top = this.origRect.top;
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ top += scrollCache.origScrollTop - scrollCache.getScrollTop();
+ }
+ return top;
+ };
+ OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) {
+ var point = { left: pageX, top: pageY };
+ for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+ var scrollCache = _a[_i];
+ if (!isIgnoredClipping(scrollCache.getEventTarget()) &&
+ !pointInsideRect(point, scrollCache.clientRect)) {
+ return false;
+ }
+ }
+ return true;
+ };
+ return OffsetTracker;
+ }());
+ // certain clipping containers should never constrain interactions, like and
+ // https://github.com/fullcalendar/fullcalendar/issues/3615
+ function isIgnoredClipping(node) {
+ var tagName = node.tagName;
+ return tagName === 'HTML' || tagName === 'BODY';
+ }
+
+ /*
+ Tracks movement over multiple droppable areas (aka "hits")
+ that exist in one or more DateComponents.
+ Relies on an existing draggable.
+
+ emits:
+ - pointerdown
+ - dragstart
+ - hitchange - fires initially, even if not over a hit
+ - pointerup
+ - (hitchange - again, to null, if ended over a hit)
+ - dragend
+ */
+ var HitDragging = /** @class */ (function () {
+ function HitDragging(dragging, droppableStore) {
+ var _this = this;
+ // options that can be set by caller
+ this.useSubjectCenter = false;
+ this.requireInitial = true; // if doesn't start out on a hit, won't emit any events
+ this.initialHit = null;
+ this.movingHit = null;
+ this.finalHit = null; // won't ever be populated if shouldIgnoreMove
+ this.handlePointerDown = function (ev) {
+ var dragging = _this.dragging;
+ _this.initialHit = null;
+ _this.movingHit = null;
+ _this.finalHit = null;
+ _this.prepareHits();
+ _this.processFirstCoord(ev);
+ if (_this.initialHit || !_this.requireInitial) {
+ dragging.setIgnoreMove(false);
+ // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
+ _this.emitter.trigger('pointerdown', ev);
+ }
+ else {
+ dragging.setIgnoreMove(true);
+ }
+ };
+ this.handleDragStart = function (ev) {
+ _this.emitter.trigger('dragstart', ev);
+ _this.handleMove(ev, true); // force = fire even if initially null
+ };
+ this.handleDragMove = function (ev) {
+ _this.emitter.trigger('dragmove', ev);
+ _this.handleMove(ev);
+ };
+ this.handlePointerUp = function (ev) {
+ _this.releaseHits();
+ _this.emitter.trigger('pointerup', ev);
+ };
+ this.handleDragEnd = function (ev) {
+ if (_this.movingHit) {
+ _this.emitter.trigger('hitupdate', null, true, ev);
+ }
+ _this.finalHit = _this.movingHit;
+ _this.movingHit = null;
+ _this.emitter.trigger('dragend', ev);
+ };
+ this.droppableStore = droppableStore;
+ dragging.emitter.on('pointerdown', this.handlePointerDown);
+ dragging.emitter.on('dragstart', this.handleDragStart);
+ dragging.emitter.on('dragmove', this.handleDragMove);
+ dragging.emitter.on('pointerup', this.handlePointerUp);
+ dragging.emitter.on('dragend', this.handleDragEnd);
+ this.dragging = dragging;
+ this.emitter = new Emitter();
+ }
+ // sets initialHit
+ // sets coordAdjust
+ HitDragging.prototype.processFirstCoord = function (ev) {
+ var origPoint = { left: ev.pageX, top: ev.pageY };
+ var adjustedPoint = origPoint;
+ var subjectEl = ev.subjectEl;
+ var subjectRect;
+ if (subjectEl instanceof HTMLElement) { // i.e. not a Document/ShadowRoot
+ subjectRect = computeRect(subjectEl);
+ adjustedPoint = constrainPoint(adjustedPoint, subjectRect);
+ }
+ var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top);
+ if (initialHit) {
+ if (this.useSubjectCenter && subjectRect) {
+ var slicedSubjectRect = intersectRects(subjectRect, initialHit.rect);
+ if (slicedSubjectRect) {
+ adjustedPoint = getRectCenter(slicedSubjectRect);
+ }
+ }
+ this.coordAdjust = diffPoints(adjustedPoint, origPoint);
+ }
+ else {
+ this.coordAdjust = { left: 0, top: 0 };
+ }
+ };
+ HitDragging.prototype.handleMove = function (ev, forceHandle) {
+ var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top);
+ if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
+ this.movingHit = hit;
+ this.emitter.trigger('hitupdate', hit, false, ev);
+ }
+ };
+ HitDragging.prototype.prepareHits = function () {
+ this.offsetTrackers = mapHash(this.droppableStore, function (interactionSettings) {
+ interactionSettings.component.prepareHits();
+ return new OffsetTracker(interactionSettings.el);
+ });
+ };
+ HitDragging.prototype.releaseHits = function () {
+ var offsetTrackers = this.offsetTrackers;
+ for (var id in offsetTrackers) {
+ offsetTrackers[id].destroy();
+ }
+ this.offsetTrackers = {};
+ };
+ HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) {
+ var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers;
+ var bestHit = null;
+ for (var id in droppableStore) {
+ var component = droppableStore[id].component;
+ var offsetTracker = offsetTrackers[id];
+ if (offsetTracker && // wasn't destroyed mid-drag
+ offsetTracker.isWithinClipping(offsetLeft, offsetTop)) {
+ var originLeft = offsetTracker.computeLeft();
+ var originTop = offsetTracker.computeTop();
+ var positionLeft = offsetLeft - originLeft;
+ var positionTop = offsetTop - originTop;
+ var origRect = offsetTracker.origRect;
+ var width = origRect.right - origRect.left;
+ var height = origRect.bottom - origRect.top;
+ if (
+ // must be within the element's bounds
+ positionLeft >= 0 && positionLeft < width &&
+ positionTop >= 0 && positionTop < height) {
+ var hit = component.queryHit(positionLeft, positionTop, width, height);
+ if (hit && (
+ // make sure the hit is within activeRange, meaning it's not a dead cell
+ rangeContainsRange(hit.dateProfile.activeRange, hit.dateSpan.range)) &&
+ (!bestHit || hit.layer > bestHit.layer)) {
+ hit.componentId = id;
+ hit.context = component.context;
+ // TODO: better way to re-orient rectangle
+ hit.rect.left += originLeft;
+ hit.rect.right += originLeft;
+ hit.rect.top += originTop;
+ hit.rect.bottom += originTop;
+ bestHit = hit;
+ }
+ }
+ }
+ }
+ return bestHit;
+ };
+ return HitDragging;
+ }());
+ function isHitsEqual(hit0, hit1) {
+ if (!hit0 && !hit1) {
+ return true;
+ }
+ if (Boolean(hit0) !== Boolean(hit1)) {
+ return false;
+ }
+ return isDateSpansEqual(hit0.dateSpan, hit1.dateSpan);
+ }
+
+ function buildDatePointApiWithContext(dateSpan, context) {
+ var props = {};
+ for (var _i = 0, _a = context.pluginHooks.datePointTransforms; _i < _a.length; _i++) {
+ var transform = _a[_i];
+ __assign(props, transform(dateSpan, context));
+ }
+ __assign(props, buildDatePointApi(dateSpan, context.dateEnv));
+ return props;
+ }
+ function buildDatePointApi(span, dateEnv) {
+ return {
+ date: dateEnv.toDate(span.range.start),
+ dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+ allDay: span.allDay,
+ };
+ }
+
+ /*
+ Monitors when the user clicks on a specific date/time of a component.
+ A pointerdown+pointerup on the same "hit" constitutes a click.
+ */
+ var DateClicking = /** @class */ (function (_super) {
+ __extends(DateClicking, _super);
+ function DateClicking(settings) {
+ var _this = _super.call(this, settings) || this;
+ _this.handlePointerDown = function (pev) {
+ var dragging = _this.dragging;
+ var downEl = pev.origEvent.target;
+ // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
+ dragging.setIgnoreMove(!_this.component.isValidDateDownEl(downEl));
+ };
+ // won't even fire if moving was ignored
+ _this.handleDragEnd = function (ev) {
+ var component = _this.component;
+ var pointer = _this.dragging.pointer;
+ if (!pointer.wasTouchScroll) {
+ var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit;
+ if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
+ var context = component.context;
+ var arg = __assign(__assign({}, buildDatePointApiWithContext(initialHit.dateSpan, context)), { dayEl: initialHit.dayEl, jsEvent: ev.origEvent, view: context.viewApi || context.calendarApi.view });
+ context.emitter.trigger('dateClick', arg);
+ }
+ }
+ };
+ // we DO want to watch pointer moves because otherwise finalHit won't get populated
+ _this.dragging = new FeaturefulElementDragging(settings.el);
+ _this.dragging.autoScroller.isEnabled = false;
+ var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+ hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+ hitDragging.emitter.on('dragend', _this.handleDragEnd);
+ return _this;
+ }
+ DateClicking.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ return DateClicking;
+ }(Interaction));
+
+ /*
+ Tracks when the user selects a portion of time of a component,
+ constituted by a drag over date cells, with a possible delay at the beginning of the drag.
+ */
+ var DateSelecting = /** @class */ (function (_super) {
+ __extends(DateSelecting, _super);
+ function DateSelecting(settings) {
+ var _this = _super.call(this, settings) || this;
+ _this.dragSelection = null;
+ _this.handlePointerDown = function (ev) {
+ var _a = _this, component = _a.component, dragging = _a.dragging;
+ var options = component.context.options;
+ var canSelect = options.selectable &&
+ component.isValidDateDownEl(ev.origEvent.target);
+ // don't bother to watch expensive moves if component won't do selection
+ dragging.setIgnoreMove(!canSelect);
+ // if touch, require user to hold down
+ dragging.delay = ev.isTouch ? getComponentTouchDelay$1(component) : null;
+ };
+ _this.handleDragStart = function (ev) {
+ _this.component.context.calendarApi.unselect(ev); // unselect previous selections
+ };
+ _this.handleHitUpdate = function (hit, isFinal) {
+ var context = _this.component.context;
+ var dragSelection = null;
+ var isInvalid = false;
+ if (hit) {
+ var initialHit = _this.hitDragging.initialHit;
+ var disallowed = hit.componentId === initialHit.componentId
+ && _this.isHitComboAllowed
+ && !_this.isHitComboAllowed(initialHit, hit);
+ if (!disallowed) {
+ dragSelection = joinHitsIntoSelection(initialHit, hit, context.pluginHooks.dateSelectionTransformers);
+ }
+ if (!dragSelection || !isDateSelectionValid(dragSelection, hit.dateProfile, context)) {
+ isInvalid = true;
+ dragSelection = null;
+ }
+ }
+ if (dragSelection) {
+ context.dispatch({ type: 'SELECT_DATES', selection: dragSelection });
+ }
+ else if (!isFinal) { // only unselect if moved away while dragging
+ context.dispatch({ type: 'UNSELECT_DATES' });
+ }
+ if (!isInvalid) {
+ enableCursor();
+ }
+ else {
+ disableCursor();
+ }
+ if (!isFinal) {
+ _this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging
+ }
+ };
+ _this.handlePointerUp = function (pev) {
+ if (_this.dragSelection) {
+ // selection is already rendered, so just need to report selection
+ triggerDateSelect(_this.dragSelection, pev, _this.component.context);
+ _this.dragSelection = null;
+ }
+ };
+ var component = settings.component;
+ var options = component.context.options;
+ var dragging = _this.dragging = new FeaturefulElementDragging(settings.el);
+ dragging.touchScrollAllowed = false;
+ dragging.minDistance = options.selectMinDistance || 0;
+ dragging.autoScroller.isEnabled = options.dragScroll;
+ var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+ hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+ hitDragging.emitter.on('dragstart', _this.handleDragStart);
+ hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+ hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+ return _this;
+ }
+ DateSelecting.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ return DateSelecting;
+ }(Interaction));
+ function getComponentTouchDelay$1(component) {
+ var options = component.context.options;
+ var delay = options.selectLongPressDelay;
+ if (delay == null) {
+ delay = options.longPressDelay;
+ }
+ return delay;
+ }
+ function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) {
+ var dateSpan0 = hit0.dateSpan;
+ var dateSpan1 = hit1.dateSpan;
+ var ms = [
+ dateSpan0.range.start,
+ dateSpan0.range.end,
+ dateSpan1.range.start,
+ dateSpan1.range.end,
+ ];
+ ms.sort(compareNumbers);
+ var props = {};
+ for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) {
+ var transformer = dateSelectionTransformers_1[_i];
+ var res = transformer(hit0, hit1);
+ if (res === false) {
+ return null;
+ }
+ if (res) {
+ __assign(props, res);
+ }
+ }
+ props.range = { start: ms[0], end: ms[3] };
+ props.allDay = dateSpan0.allDay;
+ return props;
+ }
+
+ var EventDragging = /** @class */ (function (_super) {
+ __extends(EventDragging, _super);
+ function EventDragging(settings) {
+ var _this = _super.call(this, settings) || this;
+ // internal state
+ _this.subjectEl = null;
+ _this.subjectSeg = null; // the seg being selected/dragged
+ _this.isDragging = false;
+ _this.eventRange = null;
+ _this.relevantEvents = null; // the events being dragged
+ _this.receivingContext = null;
+ _this.validMutation = null;
+ _this.mutatedRelevantEvents = null;
+ _this.handlePointerDown = function (ev) {
+ var origTarget = ev.origEvent.target;
+ var _a = _this, component = _a.component, dragging = _a.dragging;
+ var mirror = dragging.mirror;
+ var options = component.context.options;
+ var initialContext = component.context;
+ _this.subjectEl = ev.subjectEl;
+ var subjectSeg = _this.subjectSeg = getElSeg(ev.subjectEl);
+ var eventRange = _this.eventRange = subjectSeg.eventRange;
+ var eventInstanceId = eventRange.instance.instanceId;
+ _this.relevantEvents = getRelevantEvents(initialContext.getCurrentData().eventStore, eventInstanceId);
+ dragging.minDistance = ev.isTouch ? 0 : options.eventDragMinDistance;
+ dragging.delay =
+ // only do a touch delay if touch and this event hasn't been selected yet
+ (ev.isTouch && eventInstanceId !== component.props.eventSelection) ?
+ getComponentTouchDelay(component) :
+ null;
+ if (options.fixedMirrorParent) {
+ mirror.parentNode = options.fixedMirrorParent;
+ }
+ else {
+ mirror.parentNode = elementClosest(origTarget, '.fc');
+ }
+ mirror.revertDuration = options.dragRevertDuration;
+ var isValid = component.isValidSegDownEl(origTarget) &&
+ !elementClosest(origTarget, '.fc-event-resizer'); // NOT on a resizer
+ dragging.setIgnoreMove(!isValid);
+ // disable dragging for elements that are resizable (ie, selectable)
+ // but are not draggable
+ _this.isDragging = isValid &&
+ ev.subjectEl.classList.contains('fc-event-draggable');
+ };
+ _this.handleDragStart = function (ev) {
+ var initialContext = _this.component.context;
+ var eventRange = _this.eventRange;
+ var eventInstanceId = eventRange.instance.instanceId;
+ if (ev.isTouch) {
+ // need to select a different event?
+ if (eventInstanceId !== _this.component.props.eventSelection) {
+ initialContext.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId });
+ }
+ }
+ else {
+ // if now using mouse, but was previous touch interaction, clear selected event
+ initialContext.dispatch({ type: 'UNSELECT_EVENT' });
+ }
+ if (_this.isDragging) {
+ initialContext.calendarApi.unselect(ev); // unselect *date* selection
+ initialContext.emitter.trigger('eventDragStart', {
+ el: _this.subjectEl,
+ event: new EventApi(initialContext, eventRange.def, eventRange.instance),
+ jsEvent: ev.origEvent,
+ view: initialContext.viewApi,
+ });
+ }
+ };
+ _this.handleHitUpdate = function (hit, isFinal) {
+ if (!_this.isDragging) {
+ return;
+ }
+ var relevantEvents = _this.relevantEvents;
+ var initialHit = _this.hitDragging.initialHit;
+ var initialContext = _this.component.context;
+ // states based on new hit
+ var receivingContext = null;
+ var mutation = null;
+ var mutatedRelevantEvents = null;
+ var isInvalid = false;
+ var interaction = {
+ affectedEvents: relevantEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ };
+ if (hit) {
+ receivingContext = hit.context;
+ var receivingOptions = receivingContext.options;
+ if (initialContext === receivingContext ||
+ (receivingOptions.editable && receivingOptions.droppable)) {
+ mutation = computeEventMutation(initialHit, hit, receivingContext.getCurrentData().pluginHooks.eventDragMutationMassagers);
+ if (mutation) {
+ mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, receivingContext.getCurrentData().eventUiBases, mutation, receivingContext);
+ interaction.mutatedEvents = mutatedRelevantEvents;
+ if (!isInteractionValid(interaction, hit.dateProfile, receivingContext)) {
+ isInvalid = true;
+ mutation = null;
+ mutatedRelevantEvents = null;
+ interaction.mutatedEvents = createEmptyEventStore();
+ }
+ }
+ }
+ else {
+ receivingContext = null;
+ }
+ }
+ _this.displayDrag(receivingContext, interaction);
+ if (!isInvalid) {
+ enableCursor();
+ }
+ else {
+ disableCursor();
+ }
+ if (!isFinal) {
+ if (initialContext === receivingContext && // TODO: write test for this
+ isHitsEqual(initialHit, hit)) {
+ mutation = null;
+ }
+ _this.dragging.setMirrorNeedsRevert(!mutation);
+ // render the mirror if no already-rendered mirror
+ // TODO: wish we could somehow wait for dispatch to guarantee render
+ _this.dragging.setMirrorIsVisible(!hit || !getElRoot(_this.subjectEl).querySelector('.fc-event-mirror'));
+ // assign states based on new hit
+ _this.receivingContext = receivingContext;
+ _this.validMutation = mutation;
+ _this.mutatedRelevantEvents = mutatedRelevantEvents;
+ }
+ };
+ _this.handlePointerUp = function () {
+ if (!_this.isDragging) {
+ _this.cleanup(); // because handleDragEnd won't fire
+ }
+ };
+ _this.handleDragEnd = function (ev) {
+ if (_this.isDragging) {
+ var initialContext_1 = _this.component.context;
+ var initialView = initialContext_1.viewApi;
+ var _a = _this, receivingContext_1 = _a.receivingContext, validMutation = _a.validMutation;
+ var eventDef = _this.eventRange.def;
+ var eventInstance = _this.eventRange.instance;
+ var eventApi = new EventApi(initialContext_1, eventDef, eventInstance);
+ var relevantEvents_1 = _this.relevantEvents;
+ var mutatedRelevantEvents_1 = _this.mutatedRelevantEvents;
+ var finalHit = _this.hitDragging.finalHit;
+ _this.clearDrag(); // must happen after revert animation
+ initialContext_1.emitter.trigger('eventDragStop', {
+ el: _this.subjectEl,
+ event: eventApi,
+ jsEvent: ev.origEvent,
+ view: initialView,
+ });
+ if (validMutation) {
+ // dropped within same calendar
+ if (receivingContext_1 === initialContext_1) {
+ var updatedEventApi = new EventApi(initialContext_1, mutatedRelevantEvents_1.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents_1.instances[eventInstance.instanceId] : null);
+ initialContext_1.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents_1,
+ });
+ var eventChangeArg = {
+ oldEvent: eventApi,
+ event: updatedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents_1, initialContext_1, eventInstance),
+ revert: function () {
+ initialContext_1.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents_1, // the pre-change data
+ });
+ },
+ };
+ var transformed = {};
+ for (var _i = 0, _b = initialContext_1.getCurrentData().pluginHooks.eventDropTransformers; _i < _b.length; _i++) {
+ var transformer = _b[_i];
+ __assign(transformed, transformer(validMutation, initialContext_1));
+ }
+ initialContext_1.emitter.trigger('eventDrop', __assign(__assign(__assign({}, eventChangeArg), transformed), { el: ev.subjectEl, delta: validMutation.datesDelta, jsEvent: ev.origEvent, view: initialView }));
+ initialContext_1.emitter.trigger('eventChange', eventChangeArg);
+ // dropped in different calendar
+ }
+ else if (receivingContext_1) {
+ var eventRemoveArg = {
+ event: eventApi,
+ relatedEvents: buildEventApis(relevantEvents_1, initialContext_1, eventInstance),
+ revert: function () {
+ initialContext_1.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents_1,
+ });
+ },
+ };
+ initialContext_1.emitter.trigger('eventLeave', __assign(__assign({}, eventRemoveArg), { draggedEl: ev.subjectEl, view: initialView }));
+ initialContext_1.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: relevantEvents_1,
+ });
+ initialContext_1.emitter.trigger('eventRemove', eventRemoveArg);
+ var addedEventDef = mutatedRelevantEvents_1.defs[eventDef.defId];
+ var addedEventInstance = mutatedRelevantEvents_1.instances[eventInstance.instanceId];
+ var addedEventApi = new EventApi(receivingContext_1, addedEventDef, addedEventInstance);
+ receivingContext_1.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents_1,
+ });
+ var eventAddArg = {
+ event: addedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents_1, receivingContext_1, addedEventInstance),
+ revert: function () {
+ receivingContext_1.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: mutatedRelevantEvents_1,
+ });
+ },
+ };
+ receivingContext_1.emitter.trigger('eventAdd', eventAddArg);
+ if (ev.isTouch) {
+ receivingContext_1.dispatch({
+ type: 'SELECT_EVENT',
+ eventInstanceId: eventInstance.instanceId,
+ });
+ }
+ receivingContext_1.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext_1)), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.context.viewApi }));
+ receivingContext_1.emitter.trigger('eventReceive', __assign(__assign({}, eventAddArg), { draggedEl: ev.subjectEl, view: finalHit.context.viewApi }));
+ }
+ }
+ else {
+ initialContext_1.emitter.trigger('_noEventDrop');
+ }
+ }
+ _this.cleanup();
+ };
+ var component = _this.component;
+ var options = component.context.options;
+ var dragging = _this.dragging = new FeaturefulElementDragging(settings.el);
+ dragging.pointer.selector = EventDragging.SELECTOR;
+ dragging.touchScrollAllowed = false;
+ dragging.autoScroller.isEnabled = options.dragScroll;
+ var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsStore);
+ hitDragging.useSubjectCenter = settings.useEventCenter;
+ hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+ hitDragging.emitter.on('dragstart', _this.handleDragStart);
+ hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+ hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+ hitDragging.emitter.on('dragend', _this.handleDragEnd);
+ return _this;
+ }
+ EventDragging.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ // render a drag state on the next receivingCalendar
+ EventDragging.prototype.displayDrag = function (nextContext, state) {
+ var initialContext = this.component.context;
+ var prevContext = this.receivingContext;
+ // does the previous calendar need to be cleared?
+ if (prevContext && prevContext !== nextContext) {
+ // does the initial calendar need to be cleared?
+ // if so, don't clear all the way. we still need to to hide the affectedEvents
+ if (prevContext === initialContext) {
+ prevContext.dispatch({
+ type: 'SET_EVENT_DRAG',
+ state: {
+ affectedEvents: state.affectedEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ },
+ });
+ // completely clear the old calendar if it wasn't the initial
+ }
+ else {
+ prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
+ }
+ }
+ if (nextContext) {
+ nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+ }
+ };
+ EventDragging.prototype.clearDrag = function () {
+ var initialCalendar = this.component.context;
+ var receivingContext = this.receivingContext;
+ if (receivingContext) {
+ receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
+ }
+ // the initial calendar might have an dummy drag state from displayDrag
+ if (initialCalendar !== receivingContext) {
+ initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+ }
+ };
+ EventDragging.prototype.cleanup = function () {
+ this.subjectSeg = null;
+ this.isDragging = false;
+ this.eventRange = null;
+ this.relevantEvents = null;
+ this.receivingContext = null;
+ this.validMutation = null;
+ this.mutatedRelevantEvents = null;
+ };
+ // TODO: test this in IE11
+ // QUESTION: why do we need it on the resizable???
+ EventDragging.SELECTOR = '.fc-event-draggable, .fc-event-resizable';
+ return EventDragging;
+ }(Interaction));
+ function computeEventMutation(hit0, hit1, massagers) {
+ var dateSpan0 = hit0.dateSpan;
+ var dateSpan1 = hit1.dateSpan;
+ var date0 = dateSpan0.range.start;
+ var date1 = dateSpan1.range.start;
+ var standardProps = {};
+ if (dateSpan0.allDay !== dateSpan1.allDay) {
+ standardProps.allDay = dateSpan1.allDay;
+ standardProps.hasEnd = hit1.context.options.allDayMaintainDuration;
+ if (dateSpan1.allDay) {
+ // means date1 is already start-of-day,
+ // but date0 needs to be converted
+ date0 = startOfDay(date0);
+ }
+ }
+ var delta = diffDates(date0, date1, hit0.context.dateEnv, hit0.componentId === hit1.componentId ?
+ hit0.largeUnit :
+ null);
+ if (delta.milliseconds) { // has hours/minutes/seconds
+ standardProps.allDay = false;
+ }
+ var mutation = {
+ datesDelta: delta,
+ standardProps: standardProps,
+ };
+ for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) {
+ var massager = massagers_1[_i];
+ massager(mutation, hit0, hit1);
+ }
+ return mutation;
+ }
+ function getComponentTouchDelay(component) {
+ var options = component.context.options;
+ var delay = options.eventLongPressDelay;
+ if (delay == null) {
+ delay = options.longPressDelay;
+ }
+ return delay;
+ }
+
+ var EventResizing = /** @class */ (function (_super) {
+ __extends(EventResizing, _super);
+ function EventResizing(settings) {
+ var _this = _super.call(this, settings) || this;
+ // internal state
+ _this.draggingSegEl = null;
+ _this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg?
+ _this.eventRange = null;
+ _this.relevantEvents = null;
+ _this.validMutation = null;
+ _this.mutatedRelevantEvents = null;
+ _this.handlePointerDown = function (ev) {
+ var component = _this.component;
+ var segEl = _this.querySegEl(ev);
+ var seg = getElSeg(segEl);
+ var eventRange = _this.eventRange = seg.eventRange;
+ _this.dragging.minDistance = component.context.options.eventDragMinDistance;
+ // if touch, need to be working with a selected event
+ _this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) ||
+ (ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId));
+ };
+ _this.handleDragStart = function (ev) {
+ var context = _this.component.context;
+ var eventRange = _this.eventRange;
+ _this.relevantEvents = getRelevantEvents(context.getCurrentData().eventStore, _this.eventRange.instance.instanceId);
+ var segEl = _this.querySegEl(ev);
+ _this.draggingSegEl = segEl;
+ _this.draggingSeg = getElSeg(segEl);
+ context.calendarApi.unselect();
+ context.emitter.trigger('eventResizeStart', {
+ el: segEl,
+ event: new EventApi(context, eventRange.def, eventRange.instance),
+ jsEvent: ev.origEvent,
+ view: context.viewApi,
+ });
+ };
+ _this.handleHitUpdate = function (hit, isFinal, ev) {
+ var context = _this.component.context;
+ var relevantEvents = _this.relevantEvents;
+ var initialHit = _this.hitDragging.initialHit;
+ var eventInstance = _this.eventRange.instance;
+ var mutation = null;
+ var mutatedRelevantEvents = null;
+ var isInvalid = false;
+ var interaction = {
+ affectedEvents: relevantEvents,
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: true,
+ };
+ if (hit) {
+ var disallowed = hit.componentId === initialHit.componentId
+ && _this.isHitComboAllowed
+ && !_this.isHitComboAllowed(initialHit, hit);
+ if (!disallowed) {
+ mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-event-resizer-start'), eventInstance.range);
+ }
+ }
+ if (mutation) {
+ mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, context.getCurrentData().eventUiBases, mutation, context);
+ interaction.mutatedEvents = mutatedRelevantEvents;
+ if (!isInteractionValid(interaction, hit.dateProfile, context)) {
+ isInvalid = true;
+ mutation = null;
+ mutatedRelevantEvents = null;
+ interaction.mutatedEvents = null;
+ }
+ }
+ if (mutatedRelevantEvents) {
+ context.dispatch({
+ type: 'SET_EVENT_RESIZE',
+ state: interaction,
+ });
+ }
+ else {
+ context.dispatch({ type: 'UNSET_EVENT_RESIZE' });
+ }
+ if (!isInvalid) {
+ enableCursor();
+ }
+ else {
+ disableCursor();
+ }
+ if (!isFinal) {
+ if (mutation && isHitsEqual(initialHit, hit)) {
+ mutation = null;
+ }
+ _this.validMutation = mutation;
+ _this.mutatedRelevantEvents = mutatedRelevantEvents;
+ }
+ };
+ _this.handleDragEnd = function (ev) {
+ var context = _this.component.context;
+ var eventDef = _this.eventRange.def;
+ var eventInstance = _this.eventRange.instance;
+ var eventApi = new EventApi(context, eventDef, eventInstance);
+ var relevantEvents = _this.relevantEvents;
+ var mutatedRelevantEvents = _this.mutatedRelevantEvents;
+ context.emitter.trigger('eventResizeStop', {
+ el: _this.draggingSegEl,
+ event: eventApi,
+ jsEvent: ev.origEvent,
+ view: context.viewApi,
+ });
+ if (_this.validMutation) {
+ var updatedEventApi = new EventApi(context, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null);
+ context.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: mutatedRelevantEvents,
+ });
+ var eventChangeArg = {
+ oldEvent: eventApi,
+ event: updatedEventApi,
+ relatedEvents: buildEventApis(mutatedRelevantEvents, context, eventInstance),
+ revert: function () {
+ context.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: relevantEvents, // the pre-change events
+ });
+ },
+ };
+ context.emitter.trigger('eventResize', __assign(__assign({}, eventChangeArg), { el: _this.draggingSegEl, startDelta: _this.validMutation.startDelta || createDuration(0), endDelta: _this.validMutation.endDelta || createDuration(0), jsEvent: ev.origEvent, view: context.viewApi }));
+ context.emitter.trigger('eventChange', eventChangeArg);
+ }
+ else {
+ context.emitter.trigger('_noEventResize');
+ }
+ // reset all internal state
+ _this.draggingSeg = null;
+ _this.relevantEvents = null;
+ _this.validMutation = null;
+ // okay to keep eventInstance around. useful to set it in handlePointerDown
+ };
+ var component = settings.component;
+ var dragging = _this.dragging = new FeaturefulElementDragging(settings.el);
+ dragging.pointer.selector = '.fc-event-resizer';
+ dragging.touchScrollAllowed = false;
+ dragging.autoScroller.isEnabled = component.context.options.dragScroll;
+ var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+ hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+ hitDragging.emitter.on('dragstart', _this.handleDragStart);
+ hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+ hitDragging.emitter.on('dragend', _this.handleDragEnd);
+ return _this;
+ }
+ EventResizing.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ EventResizing.prototype.querySegEl = function (ev) {
+ return elementClosest(ev.subjectEl, '.fc-event');
+ };
+ return EventResizing;
+ }(Interaction));
+ function computeMutation(hit0, hit1, isFromStart, instanceRange) {
+ var dateEnv = hit0.context.dateEnv;
+ var date0 = hit0.dateSpan.range.start;
+ var date1 = hit1.dateSpan.range.start;
+ var delta = diffDates(date0, date1, dateEnv, hit0.largeUnit);
+ if (isFromStart) {
+ if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
+ return { startDelta: delta };
+ }
+ }
+ else if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
+ return { endDelta: delta };
+ }
+ return null;
+ }
+
+ var UnselectAuto = /** @class */ (function () {
+ function UnselectAuto(context) {
+ var _this = this;
+ this.context = context;
+ this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system
+ this.matchesCancel = false;
+ this.matchesEvent = false;
+ this.onSelect = function (selectInfo) {
+ if (selectInfo.jsEvent) {
+ _this.isRecentPointerDateSelect = true;
+ }
+ };
+ this.onDocumentPointerDown = function (pev) {
+ var unselectCancel = _this.context.options.unselectCancel;
+ var downEl = getEventTargetViaRoot(pev.origEvent);
+ _this.matchesCancel = !!elementClosest(downEl, unselectCancel);
+ _this.matchesEvent = !!elementClosest(downEl, EventDragging.SELECTOR); // interaction started on an event?
+ };
+ this.onDocumentPointerUp = function (pev) {
+ var context = _this.context;
+ var documentPointer = _this.documentPointer;
+ var calendarState = context.getCurrentData();
+ // touch-scrolling should never unfocus any type of selection
+ if (!documentPointer.wasTouchScroll) {
+ if (calendarState.dateSelection && // an existing date selection?
+ !_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
+ ) {
+ var unselectAuto = context.options.unselectAuto;
+ if (unselectAuto && (!unselectAuto || !_this.matchesCancel)) {
+ context.calendarApi.unselect(pev);
+ }
+ }
+ if (calendarState.eventSelection && // an existing event selected?
+ !_this.matchesEvent // interaction DIDN'T start on an event
+ ) {
+ context.dispatch({ type: 'UNSELECT_EVENT' });
+ }
+ }
+ _this.isRecentPointerDateSelect = false;
+ };
+ var documentPointer = this.documentPointer = new PointerDragging(document);
+ documentPointer.shouldIgnoreMove = true;
+ documentPointer.shouldWatchScroll = false;
+ documentPointer.emitter.on('pointerdown', this.onDocumentPointerDown);
+ documentPointer.emitter.on('pointerup', this.onDocumentPointerUp);
+ /*
+ TODO: better way to know about whether there was a selection with the pointer
+ */
+ context.emitter.on('select', this.onSelect);
+ }
+ UnselectAuto.prototype.destroy = function () {
+ this.context.emitter.off('select', this.onSelect);
+ this.documentPointer.destroy();
+ };
+ return UnselectAuto;
+ }());
+
+ var OPTION_REFINERS$3 = {
+ fixedMirrorParent: identity,
+ };
+ var LISTENER_REFINERS = {
+ dateClick: identity,
+ eventDragStart: identity,
+ eventDragStop: identity,
+ eventDrop: identity,
+ eventResizeStart: identity,
+ eventResizeStop: identity,
+ eventResize: identity,
+ drop: identity,
+ eventReceive: identity,
+ eventLeave: identity,
+ };
+
+ /*
+ Given an already instantiated draggable object for one-or-more elements,
+ Interprets any dragging as an attempt to drag an events that lives outside
+ of a calendar onto a calendar.
+ */
+ var ExternalElementDragging = /** @class */ (function () {
+ function ExternalElementDragging(dragging, suppliedDragMeta) {
+ var _this = this;
+ this.receivingContext = null;
+ this.droppableEvent = null; // will exist for all drags, even if create:false
+ this.suppliedDragMeta = null;
+ this.dragMeta = null;
+ this.handleDragStart = function (ev) {
+ _this.dragMeta = _this.buildDragMeta(ev.subjectEl);
+ };
+ this.handleHitUpdate = function (hit, isFinal, ev) {
+ var dragging = _this.hitDragging.dragging;
+ var receivingContext = null;
+ var droppableEvent = null;
+ var isInvalid = false;
+ var interaction = {
+ affectedEvents: createEmptyEventStore(),
+ mutatedEvents: createEmptyEventStore(),
+ isEvent: _this.dragMeta.create,
+ };
+ if (hit) {
+ receivingContext = hit.context;
+ if (_this.canDropElOnCalendar(ev.subjectEl, receivingContext)) {
+ droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingContext);
+ interaction.mutatedEvents = eventTupleToStore(droppableEvent);
+ isInvalid = !isInteractionValid(interaction, hit.dateProfile, receivingContext);
+ if (isInvalid) {
+ interaction.mutatedEvents = createEmptyEventStore();
+ droppableEvent = null;
+ }
+ }
+ }
+ _this.displayDrag(receivingContext, interaction);
+ // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?)
+ // TODO: wish we could somehow wait for dispatch to guarantee render
+ dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-event-mirror'));
+ if (!isInvalid) {
+ enableCursor();
+ }
+ else {
+ disableCursor();
+ }
+ if (!isFinal) {
+ dragging.setMirrorNeedsRevert(!droppableEvent);
+ _this.receivingContext = receivingContext;
+ _this.droppableEvent = droppableEvent;
+ }
+ };
+ this.handleDragEnd = function (pev) {
+ var _a = _this, receivingContext = _a.receivingContext, droppableEvent = _a.droppableEvent;
+ _this.clearDrag();
+ if (receivingContext && droppableEvent) {
+ var finalHit = _this.hitDragging.finalHit;
+ var finalView = finalHit.context.viewApi;
+ var dragMeta = _this.dragMeta;
+ receivingContext.emitter.trigger('drop', __assign(__assign({}, buildDatePointApiWithContext(finalHit.dateSpan, receivingContext)), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView }));
+ if (dragMeta.create) {
+ var addingEvents_1 = eventTupleToStore(droppableEvent);
+ receivingContext.dispatch({
+ type: 'MERGE_EVENTS',
+ eventStore: addingEvents_1,
+ });
+ if (pev.isTouch) {
+ receivingContext.dispatch({
+ type: 'SELECT_EVENT',
+ eventInstanceId: droppableEvent.instance.instanceId,
+ });
+ }
+ // signal that an external event landed
+ receivingContext.emitter.trigger('eventReceive', {
+ event: new EventApi(receivingContext, droppableEvent.def, droppableEvent.instance),
+ relatedEvents: [],
+ revert: function () {
+ receivingContext.dispatch({
+ type: 'REMOVE_EVENTS',
+ eventStore: addingEvents_1,
+ });
+ },
+ draggedEl: pev.subjectEl,
+ view: finalView,
+ });
+ }
+ }
+ _this.receivingContext = null;
+ _this.droppableEvent = null;
+ };
+ var hitDragging = this.hitDragging = new HitDragging(dragging, interactionSettingsStore);
+ hitDragging.requireInitial = false; // will start outside of a component
+ hitDragging.emitter.on('dragstart', this.handleDragStart);
+ hitDragging.emitter.on('hitupdate', this.handleHitUpdate);
+ hitDragging.emitter.on('dragend', this.handleDragEnd);
+ this.suppliedDragMeta = suppliedDragMeta;
+ }
+ ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) {
+ if (typeof this.suppliedDragMeta === 'object') {
+ return parseDragMeta(this.suppliedDragMeta);
+ }
+ if (typeof this.suppliedDragMeta === 'function') {
+ return parseDragMeta(this.suppliedDragMeta(subjectEl));
+ }
+ return getDragMetaFromEl(subjectEl);
+ };
+ ExternalElementDragging.prototype.displayDrag = function (nextContext, state) {
+ var prevContext = this.receivingContext;
+ if (prevContext && prevContext !== nextContext) {
+ prevContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
+ }
+ if (nextContext) {
+ nextContext.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+ }
+ };
+ ExternalElementDragging.prototype.clearDrag = function () {
+ if (this.receivingContext) {
+ this.receivingContext.dispatch({ type: 'UNSET_EVENT_DRAG' });
+ }
+ };
+ ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingContext) {
+ var dropAccept = receivingContext.options.dropAccept;
+ if (typeof dropAccept === 'function') {
+ return dropAccept.call(receivingContext.calendarApi, el);
+ }
+ if (typeof dropAccept === 'string' && dropAccept) {
+ return Boolean(elementMatches(el, dropAccept));
+ }
+ return true;
+ };
+ return ExternalElementDragging;
+ }());
+ // Utils for computing event store from the DragMeta
+ // ----------------------------------------------------------------------------------------------------
+ function computeEventForDateSpan(dateSpan, dragMeta, context) {
+ var defProps = __assign({}, dragMeta.leftoverProps);
+ for (var _i = 0, _a = context.pluginHooks.externalDefTransforms; _i < _a.length; _i++) {
+ var transform = _a[_i];
+ __assign(defProps, transform(dateSpan, dragMeta));
+ }
+ var _b = refineEventDef(defProps, context), refined = _b.refined, extra = _b.extra;
+ var def = parseEventDef(refined, extra, dragMeta.sourceId, dateSpan.allDay, context.options.forceEventDuration || Boolean(dragMeta.duration), // hasEnd
+ context);
+ var start = dateSpan.range.start;
+ // only rely on time info if drop zone is all-day,
+ // otherwise, we already know the time
+ if (dateSpan.allDay && dragMeta.startTime) {
+ start = context.dateEnv.add(start, dragMeta.startTime);
+ }
+ var end = dragMeta.duration ?
+ context.dateEnv.add(start, dragMeta.duration) :
+ getDefaultEventEnd(dateSpan.allDay, start, context);
+ var instance = createEventInstance(def.defId, { start: start, end: end });
+ return { def: def, instance: instance };
+ }
+ // Utils for extracting data from element
+ // ----------------------------------------------------------------------------------------------------
+ function getDragMetaFromEl(el) {
+ var str = getEmbeddedElData(el, 'event');
+ var obj = str ?
+ JSON.parse(str) :
+ { create: false }; // if no embedded data, assume no event creation
+ return parseDragMeta(obj);
+ }
+ config.dataAttrPrefix = '';
+ function getEmbeddedElData(el, name) {
+ var prefix = config.dataAttrPrefix;
+ var prefixedName = (prefix ? prefix + '-' : '') + name;
+ return el.getAttribute('data-' + prefixedName) || '';
+ }
+
+ /*
+ Makes an element (that is *external* to any calendar) draggable.
+ Can pass in data that determines how an event will be created when dropped onto a calendar.
+ Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system.
+ */
+ var ExternalDraggable = /** @class */ (function () {
+ function ExternalDraggable(el, settings) {
+ var _this = this;
+ if (settings === void 0) { settings = {}; }
+ this.handlePointerDown = function (ev) {
+ var dragging = _this.dragging;
+ var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay;
+ dragging.minDistance =
+ minDistance != null ?
+ minDistance :
+ (ev.isTouch ? 0 : BASE_OPTION_DEFAULTS.eventDragMinDistance);
+ dragging.delay =
+ ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
+ (longPressDelay != null ? longPressDelay : BASE_OPTION_DEFAULTS.longPressDelay) :
+ 0;
+ };
+ this.handleDragStart = function (ev) {
+ if (ev.isTouch &&
+ _this.dragging.delay &&
+ ev.subjectEl.classList.contains('fc-event')) {
+ _this.dragging.mirror.getMirrorEl().classList.add('fc-event-selected');
+ }
+ };
+ this.settings = settings;
+ var dragging = this.dragging = new FeaturefulElementDragging(el);
+ dragging.touchScrollAllowed = false;
+ if (settings.itemSelector != null) {
+ dragging.pointer.selector = settings.itemSelector;
+ }
+ if (settings.appendTo != null) {
+ dragging.mirror.parentNode = settings.appendTo; // TODO: write tests
+ }
+ dragging.emitter.on('pointerdown', this.handlePointerDown);
+ dragging.emitter.on('dragstart', this.handleDragStart);
+ new ExternalElementDragging(dragging, settings.eventData); // eslint-disable-line no-new
+ }
+ ExternalDraggable.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ return ExternalDraggable;
+ }());
+
+ /*
+ Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements.
+ The third-party system is responsible for drawing the visuals effects of the drag.
+ This class simply monitors for pointer movements and fires events.
+ It also has the ability to hide the moving element (the "mirror") during the drag.
+ */
+ var InferredElementDragging = /** @class */ (function (_super) {
+ __extends(InferredElementDragging, _super);
+ function InferredElementDragging(containerEl) {
+ var _this = _super.call(this, containerEl) || this;
+ _this.shouldIgnoreMove = false;
+ _this.mirrorSelector = '';
+ _this.currentMirrorEl = null;
+ _this.handlePointerDown = function (ev) {
+ _this.emitter.trigger('pointerdown', ev);
+ if (!_this.shouldIgnoreMove) {
+ // fire dragstart right away. does not support delay or min-distance
+ _this.emitter.trigger('dragstart', ev);
+ }
+ };
+ _this.handlePointerMove = function (ev) {
+ if (!_this.shouldIgnoreMove) {
+ _this.emitter.trigger('dragmove', ev);
+ }
+ };
+ _this.handlePointerUp = function (ev) {
+ _this.emitter.trigger('pointerup', ev);
+ if (!_this.shouldIgnoreMove) {
+ // fire dragend right away. does not support a revert animation
+ _this.emitter.trigger('dragend', ev);
+ }
+ };
+ var pointer = _this.pointer = new PointerDragging(containerEl);
+ pointer.emitter.on('pointerdown', _this.handlePointerDown);
+ pointer.emitter.on('pointermove', _this.handlePointerMove);
+ pointer.emitter.on('pointerup', _this.handlePointerUp);
+ return _this;
+ }
+ InferredElementDragging.prototype.destroy = function () {
+ this.pointer.destroy();
+ };
+ InferredElementDragging.prototype.setIgnoreMove = function (bool) {
+ this.shouldIgnoreMove = bool;
+ };
+ InferredElementDragging.prototype.setMirrorIsVisible = function (bool) {
+ if (bool) {
+ // restore a previously hidden element.
+ // use the reference in case the selector class has already been removed.
+ if (this.currentMirrorEl) {
+ this.currentMirrorEl.style.visibility = '';
+ this.currentMirrorEl = null;
+ }
+ }
+ else {
+ var mirrorEl = this.mirrorSelector
+ // TODO: somehow query FullCalendars WITHIN shadow-roots
+ ? document.querySelector(this.mirrorSelector)
+ : null;
+ if (mirrorEl) {
+ this.currentMirrorEl = mirrorEl;
+ mirrorEl.style.visibility = 'hidden';
+ }
+ }
+ };
+ return InferredElementDragging;
+ }(ElementDragging));
+
+ /*
+ Bridges third-party drag-n-drop systems with FullCalendar.
+ Must be instantiated and destroyed by caller.
+ */
+ var ThirdPartyDraggable = /** @class */ (function () {
+ function ThirdPartyDraggable(containerOrSettings, settings) {
+ var containerEl = document;
+ if (
+ // wish we could just test instanceof EventTarget, but doesn't work in IE11
+ containerOrSettings === document ||
+ containerOrSettings instanceof Element) {
+ containerEl = containerOrSettings;
+ settings = settings || {};
+ }
+ else {
+ settings = (containerOrSettings || {});
+ }
+ var dragging = this.dragging = new InferredElementDragging(containerEl);
+ if (typeof settings.itemSelector === 'string') {
+ dragging.pointer.selector = settings.itemSelector;
+ }
+ else if (containerEl === document) {
+ dragging.pointer.selector = '[data-event]';
+ }
+ if (typeof settings.mirrorSelector === 'string') {
+ dragging.mirrorSelector = settings.mirrorSelector;
+ }
+ new ExternalElementDragging(dragging, settings.eventData); // eslint-disable-line no-new
+ }
+ ThirdPartyDraggable.prototype.destroy = function () {
+ this.dragging.destroy();
+ };
+ return ThirdPartyDraggable;
+ }());
+
+ var interactionPlugin = createPlugin({
+ componentInteractions: [DateClicking, DateSelecting, EventDragging, EventResizing],
+ calendarInteractions: [UnselectAuto],
+ elementDraggingImpl: FeaturefulElementDragging,
+ optionRefiners: OPTION_REFINERS$3,
+ listenerRefiners: LISTENER_REFINERS,
+ });
+
+ /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
+ ----------------------------------------------------------------------------------------------------------------------*/
+ // It is a manager for a Table subcomponent, which does most of the heavy lifting.
+ // It is responsible for managing width/height.
+ var TableView = /** @class */ (function (_super) {
+ __extends(TableView, _super);
+ function TableView() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.headerElRef = createRef();
+ return _this;
+ }
+ TableView.prototype.renderSimpleLayout = function (headerRowContent, bodyContent) {
+ var _a = this, props = _a.props, context = _a.context;
+ var sections = [];
+ var stickyHeaderDates = getStickyHeaderDates(context.options);
+ if (headerRowContent) {
+ sections.push({
+ type: 'header',
+ key: 'header',
+ isSticky: stickyHeaderDates,
+ chunk: {
+ elRef: this.headerElRef,
+ tableClassName: 'fc-col-header',
+ rowContent: headerRowContent,
+ },
+ });
+ }
+ sections.push({
+ type: 'body',
+ key: 'body',
+ liquid: true,
+ chunk: { content: bodyContent },
+ });
+ return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') },
+ createElement(SimpleScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: props.forPrint, cols: [] /* TODO: make optional? */, sections: sections }))); }));
+ };
+ TableView.prototype.renderHScrollLayout = function (headerRowContent, bodyContent, colCnt, dayMinWidth) {
+ var ScrollGrid = this.context.pluginHooks.scrollGridImpl;
+ if (!ScrollGrid) {
+ throw new Error('No ScrollGrid implementation');
+ }
+ var _a = this, props = _a.props, context = _a.context;
+ var stickyHeaderDates = !props.forPrint && getStickyHeaderDates(context.options);
+ var stickyFooterScrollbar = !props.forPrint && getStickyFooterScrollbar(context.options);
+ var sections = [];
+ if (headerRowContent) {
+ sections.push({
+ type: 'header',
+ key: 'header',
+ isSticky: stickyHeaderDates,
+ chunks: [{
+ key: 'main',
+ elRef: this.headerElRef,
+ tableClassName: 'fc-col-header',
+ rowContent: headerRowContent,
+ }],
+ });
+ }
+ sections.push({
+ type: 'body',
+ key: 'body',
+ liquid: true,
+ chunks: [{
+ key: 'main',
+ content: bodyContent,
+ }],
+ });
+ if (stickyFooterScrollbar) {
+ sections.push({
+ type: 'footer',
+ key: 'footer',
+ isSticky: true,
+ chunks: [{
+ key: 'main',
+ content: renderScrollShim,
+ }],
+ });
+ }
+ return (createElement(ViewRoot, { viewSpec: context.viewSpec }, function (rootElRef, classNames) { return (createElement("div", { ref: rootElRef, className: ['fc-daygrid'].concat(classNames).join(' ') },
+ createElement(ScrollGrid, { liquid: !props.isHeightAuto && !props.forPrint, collapsibleWidth: props.forPrint, colGroups: [{ cols: [{ span: colCnt, minWidth: dayMinWidth }] }], sections: sections }))); }));
+ };
+ return TableView;
+ }(DateComponent));
+
+ function splitSegsByRow(segs, rowCnt) {
+ var byRow = [];
+ for (var i = 0; i < rowCnt; i += 1) {
+ byRow[i] = [];
+ }
+ for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+ var seg = segs_1[_i];
+ byRow[seg.row].push(seg);
+ }
+ return byRow;
+ }
+ function splitSegsByFirstCol(segs, colCnt) {
+ var byCol = [];
+ for (var i = 0; i < colCnt; i += 1) {
+ byCol[i] = [];
+ }
+ for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+ var seg = segs_2[_i];
+ byCol[seg.firstCol].push(seg);
+ }
+ return byCol;
+ }
+ function splitInteractionByRow(ui, rowCnt) {
+ var byRow = [];
+ if (!ui) {
+ for (var i = 0; i < rowCnt; i += 1) {
+ byRow[i] = null;
+ }
+ }
+ else {
+ for (var i = 0; i < rowCnt; i += 1) {
+ byRow[i] = {
+ affectedInstances: ui.affectedInstances,
+ isEvent: ui.isEvent,
+ segs: [],
+ };
+ }
+ for (var _i = 0, _a = ui.segs; _i < _a.length; _i++) {
+ var seg = _a[_i];
+ byRow[seg.row].segs.push(seg);
+ }
+ }
+ return byRow;
+ }
+
+ var TableCellTop = /** @class */ (function (_super) {
+ __extends(TableCellTop, _super);
+ function TableCellTop() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ TableCellTop.prototype.render = function () {
+ var props = this.props;
+ var navLinkAttrs = this.context.options.navLinks
+ ? { 'data-navlink': buildNavLinkData(props.date), tabIndex: 0 }
+ : {};
+ return (createElement(DayCellContent, { date: props.date, dateProfile: props.dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, defaultContent: renderTopInner }, function (innerElRef, innerContent) { return ((innerContent || props.forceDayTop) && (createElement("div", { className: "fc-daygrid-day-top", ref: innerElRef },
+ createElement("a", __assign({ className: "fc-daygrid-day-number" }, navLinkAttrs), innerContent || createElement(Fragment, null, "\u00A0"))))); }));
+ };
+ return TableCellTop;
+ }(BaseComponent));
+ function renderTopInner(props) {
+ return props.dayNumberText;
+ }
+
+ var DEFAULT_TABLE_EVENT_TIME_FORMAT = createFormatter({
+ hour: 'numeric',
+ minute: '2-digit',
+ omitZeroMinute: true,
+ meridiem: 'narrow',
+ });
+ function hasListItemDisplay(seg) {
+ var display = seg.eventRange.ui.display;
+ return display === 'list-item' || (display === 'auto' &&
+ !seg.eventRange.def.allDay &&
+ seg.firstCol === seg.lastCol && // can't be multi-day
+ seg.isStart && // "
+ seg.isEnd // "
+ );
+ }
+
+ var TableBlockEvent = /** @class */ (function (_super) {
+ __extends(TableBlockEvent, _super);
+ function TableBlockEvent() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ TableBlockEvent.prototype.render = function () {
+ var props = this.props;
+ return (createElement(StandardEvent, __assign({}, props, { extraClassNames: ['fc-daygrid-event', 'fc-daygrid-block-event', 'fc-h-event'], defaultTimeFormat: DEFAULT_TABLE_EVENT_TIME_FORMAT, defaultDisplayEventEnd: props.defaultDisplayEventEnd, disableResizing: !props.seg.eventRange.def.allDay })));
+ };
+ return TableBlockEvent;
+ }(BaseComponent));
+
+ var TableListItemEvent = /** @class */ (function (_super) {
+ __extends(TableListItemEvent, _super);
+ function TableListItemEvent() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ TableListItemEvent.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ var timeFormat = context.options.eventTimeFormat || DEFAULT_TABLE_EVENT_TIME_FORMAT;
+ var timeText = buildSegTimeText(props.seg, timeFormat, context, true, props.defaultDisplayEventEnd);
+ return (createElement(EventRoot, { seg: props.seg, timeText: timeText, defaultContent: renderInnerContent$2, isDragging: props.isDragging, isResizing: false, isDateSelecting: false, isSelected: props.isSelected, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday }, function (rootElRef, classNames, innerElRef, innerContent) { return ( // we don't use styles!
+ createElement("a", __assign({ className: ['fc-daygrid-event', 'fc-daygrid-dot-event'].concat(classNames).join(' '), ref: rootElRef }, getSegAnchorAttrs(props.seg)), innerContent)); }));
+ };
+ return TableListItemEvent;
+ }(BaseComponent));
+ function renderInnerContent$2(innerProps) {
+ return (createElement(Fragment, null,
+ createElement("div", { className: "fc-daygrid-event-dot", style: { borderColor: innerProps.borderColor || innerProps.backgroundColor } }),
+ innerProps.timeText && (createElement("div", { className: "fc-event-time" }, innerProps.timeText)),
+ createElement("div", { className: "fc-event-title" }, innerProps.event.title || createElement(Fragment, null, "\u00A0"))));
+ }
+ function getSegAnchorAttrs(seg) {
+ var url = seg.eventRange.def.url;
+ return url ? { href: url } : {};
+ }
+
+ var TableCellMoreLink = /** @class */ (function (_super) {
+ __extends(TableCellMoreLink, _super);
+ function TableCellMoreLink() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.compileSegs = memoize(compileSegs);
+ return _this;
+ }
+ TableCellMoreLink.prototype.render = function () {
+ var props = this.props;
+ var _a = this.compileSegs(props.singlePlacements), allSegs = _a.allSegs, invisibleSegs = _a.invisibleSegs;
+ return (createElement(MoreLinkRoot, { dateProfile: props.dateProfile, todayRange: props.todayRange, allDayDate: props.allDayDate, moreCnt: props.moreCnt, allSegs: allSegs, hiddenSegs: invisibleSegs, alignmentElRef: props.alignmentElRef, alignGridTop: props.alignGridTop, extraDateSpan: props.extraDateSpan, popoverContent: function () {
+ var isForcedInvisible = (props.eventDrag ? props.eventDrag.affectedInstances : null) ||
+ (props.eventResize ? props.eventResize.affectedInstances : null) ||
+ {};
+ return (createElement(Fragment, null, allSegs.map(function (seg) {
+ var instanceId = seg.eventRange.instance.instanceId;
+ return (createElement("div", { className: "fc-daygrid-event-harness", key: instanceId, style: {
+ visibility: isForcedInvisible[instanceId] ? 'hidden' : '',
+ } }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, __assign({ seg: seg, isDragging: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange)))) : (createElement(TableBlockEvent, __assign({ seg: seg, isDragging: false, isResizing: false, isDateSelecting: false, isSelected: instanceId === props.eventSelection, defaultDisplayEventEnd: false }, getSegMeta(seg, props.todayRange))))));
+ })));
+ } }, function (rootElRef, classNames, innerElRef, innerContent, handleClick) { return (createElement("a", { ref: rootElRef, className: ['fc-daygrid-more-link'].concat(classNames).join(' '), onClick: handleClick }, innerContent)); }));
+ };
+ return TableCellMoreLink;
+ }(BaseComponent));
+ function compileSegs(singlePlacements) {
+ var allSegs = [];
+ var invisibleSegs = [];
+ for (var _i = 0, singlePlacements_1 = singlePlacements; _i < singlePlacements_1.length; _i++) {
+ var placement = singlePlacements_1[_i];
+ allSegs.push(placement.seg);
+ if (!placement.isVisible) {
+ invisibleSegs.push(placement.seg);
+ }
+ }
+ return { allSegs: allSegs, invisibleSegs: invisibleSegs };
+ }
+
+ var DEFAULT_WEEK_NUM_FORMAT$1 = createFormatter({ week: 'narrow' });
+ var TableCell = /** @class */ (function (_super) {
+ __extends(TableCell, _super);
+ function TableCell() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.rootElRef = createRef();
+ _this.handleRootEl = function (el) {
+ setRef(_this.rootElRef, el);
+ setRef(_this.props.elRef, el);
+ };
+ return _this;
+ }
+ TableCell.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context, rootElRef = _a.rootElRef;
+ var options = context.options;
+ var date = props.date, dateProfile = props.dateProfile;
+ var navLinkAttrs = options.navLinks
+ ? { 'data-navlink': buildNavLinkData(date, 'week'), tabIndex: 0 }
+ : {};
+ return (createElement(DayCellRoot, { date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraHookProps: props.extraHookProps, elRef: this.handleRootEl }, function (dayElRef, dayClassNames, rootDataAttrs, isDisabled) { return (createElement("td", __assign({ ref: dayElRef, className: ['fc-daygrid-day'].concat(dayClassNames, props.extraClassNames || []).join(' ') }, rootDataAttrs, props.extraDataAttrs),
+ createElement("div", { className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", ref: props.innerElRef /* different from hook system! RENAME */ },
+ props.showWeekNumber && (createElement(WeekNumberRoot, { date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT$1 }, function (weekElRef, weekClassNames, innerElRef, innerContent) { return (createElement("a", __assign({ ref: weekElRef, className: ['fc-daygrid-week-number'].concat(weekClassNames).join(' ') }, navLinkAttrs), innerContent)); })),
+ !isDisabled && (createElement(TableCellTop, { date: date, dateProfile: dateProfile, showDayNumber: props.showDayNumber, forceDayTop: props.forceDayTop, todayRange: props.todayRange, extraHookProps: props.extraHookProps })),
+ createElement("div", { className: "fc-daygrid-day-events", ref: props.fgContentElRef },
+ props.fgContent,
+ createElement("div", { className: "fc-daygrid-day-bottom", style: { marginTop: props.moreMarginTop } },
+ createElement(TableCellMoreLink, { allDayDate: date, singlePlacements: props.singlePlacements, moreCnt: props.moreCnt, alignmentElRef: rootElRef, alignGridTop: !props.showDayNumber, extraDateSpan: props.extraDateSpan, dateProfile: props.dateProfile, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, todayRange: props.todayRange }))),
+ createElement("div", { className: "fc-daygrid-day-bg" }, props.bgContent)))); }));
+ };
+ return TableCell;
+ }(DateComponent));
+
+ function computeFgSegPlacement(segs, // assumed already sorted
+ dayMaxEvents, dayMaxEventRows, strictOrder, eventInstanceHeights, maxContentHeight, cells) {
+ var hierarchy = new DayGridSegHierarchy();
+ hierarchy.allowReslicing = true;
+ hierarchy.strictOrder = strictOrder;
+ if (dayMaxEvents === true || dayMaxEventRows === true) {
+ hierarchy.maxCoord = maxContentHeight;
+ hierarchy.hiddenConsumes = true;
+ }
+ else if (typeof dayMaxEvents === 'number') {
+ hierarchy.maxStackCnt = dayMaxEvents;
+ }
+ else if (typeof dayMaxEventRows === 'number') {
+ hierarchy.maxStackCnt = dayMaxEventRows;
+ hierarchy.hiddenConsumes = true;
+ }
+ // create segInputs only for segs with known heights
+ var segInputs = [];
+ var unknownHeightSegs = [];
+ for (var i = 0; i < segs.length; i += 1) {
+ var seg = segs[i];
+ var instanceId = seg.eventRange.instance.instanceId;
+ var eventHeight = eventInstanceHeights[instanceId];
+ if (eventHeight != null) {
+ segInputs.push({
+ index: i,
+ thickness: eventHeight,
+ span: {
+ start: seg.firstCol,
+ end: seg.lastCol + 1,
+ },
+ });
+ }
+ else {
+ unknownHeightSegs.push(seg);
+ }
+ }
+ var hiddenEntries = hierarchy.addSegs(segInputs);
+ var segRects = hierarchy.toRects();
+ var _a = placeRects(segRects, segs, cells), singleColPlacements = _a.singleColPlacements, multiColPlacements = _a.multiColPlacements, leftoverMargins = _a.leftoverMargins;
+ var moreCnts = [];
+ var moreMarginTops = [];
+ // add segs with unknown heights
+ for (var _i = 0, unknownHeightSegs_1 = unknownHeightSegs; _i < unknownHeightSegs_1.length; _i++) {
+ var seg = unknownHeightSegs_1[_i];
+ multiColPlacements[seg.firstCol].push({
+ seg: seg,
+ isVisible: false,
+ isAbsolute: true,
+ absoluteTop: 0,
+ marginTop: 0,
+ });
+ for (var col = seg.firstCol; col <= seg.lastCol; col += 1) {
+ singleColPlacements[col].push({
+ seg: resliceSeg(seg, col, col + 1, cells),
+ isVisible: false,
+ isAbsolute: false,
+ absoluteTop: 0,
+ marginTop: 0,
+ });
+ }
+ }
+ // add the hidden entries
+ for (var col = 0; col < cells.length; col += 1) {
+ moreCnts.push(0);
+ }
+ for (var _b = 0, hiddenEntries_1 = hiddenEntries; _b < hiddenEntries_1.length; _b++) {
+ var hiddenEntry = hiddenEntries_1[_b];
+ var seg = segs[hiddenEntry.index];
+ var hiddenSpan = hiddenEntry.span;
+ multiColPlacements[hiddenSpan.start].push({
+ seg: resliceSeg(seg, hiddenSpan.start, hiddenSpan.end, cells),
+ isVisible: false,
+ isAbsolute: true,
+ absoluteTop: 0,
+ marginTop: 0,
+ });
+ for (var col = hiddenSpan.start; col < hiddenSpan.end; col += 1) {
+ moreCnts[col] += 1;
+ singleColPlacements[col].push({
+ seg: resliceSeg(seg, col, col + 1, cells),
+ isVisible: false,
+ isAbsolute: false,
+ absoluteTop: 0,
+ marginTop: 0,
+ });
+ }
+ }
+ // deal with leftover margins
+ for (var col = 0; col < cells.length; col += 1) {
+ moreMarginTops.push(leftoverMargins[col]);
+ }
+ return { singleColPlacements: singleColPlacements, multiColPlacements: multiColPlacements, moreCnts: moreCnts, moreMarginTops: moreMarginTops };
+ }
+ // rects ordered by top coord, then left
+ function placeRects(allRects, segs, cells) {
+ var rectsByEachCol = groupRectsByEachCol(allRects, cells.length);
+ var singleColPlacements = [];
+ var multiColPlacements = [];
+ var leftoverMargins = [];
+ for (var col = 0; col < cells.length; col += 1) {
+ var rects = rectsByEachCol[col];
+ // compute all static segs in singlePlacements
+ var singlePlacements = [];
+ var currentHeight = 0;
+ var currentMarginTop = 0;
+ for (var _i = 0, rects_1 = rects; _i < rects_1.length; _i++) {
+ var rect = rects_1[_i];
+ var seg = segs[rect.index];
+ singlePlacements.push({
+ seg: resliceSeg(seg, col, col + 1, cells),
+ isVisible: true,
+ isAbsolute: false,
+ absoluteTop: rect.levelCoord,
+ marginTop: rect.levelCoord - currentHeight,
+ });
+ currentHeight = rect.levelCoord + rect.thickness;
+ }
+ // compute mixed static/absolute segs in multiPlacements
+ var multiPlacements = [];
+ currentHeight = 0;
+ currentMarginTop = 0;
+ for (var _a = 0, rects_2 = rects; _a < rects_2.length; _a++) {
+ var rect = rects_2[_a];
+ var seg = segs[rect.index];
+ var isAbsolute = rect.span.end - rect.span.start > 1; // multi-column?
+ var isFirstCol = rect.span.start === col;
+ currentMarginTop += rect.levelCoord - currentHeight; // amount of space since bottom of previous seg
+ currentHeight = rect.levelCoord + rect.thickness; // height will now be bottom of current seg
+ if (isAbsolute) {
+ currentMarginTop += rect.thickness;
+ if (isFirstCol) {
+ multiPlacements.push({
+ seg: resliceSeg(seg, rect.span.start, rect.span.end, cells),
+ isVisible: true,
+ isAbsolute: true,
+ absoluteTop: rect.levelCoord,
+ marginTop: 0,
+ });
+ }
+ }
+ else if (isFirstCol) {
+ multiPlacements.push({
+ seg: resliceSeg(seg, rect.span.start, rect.span.end, cells),
+ isVisible: true,
+ isAbsolute: false,
+ absoluteTop: rect.levelCoord,
+ marginTop: currentMarginTop, // claim the margin
+ });
+ currentMarginTop = 0;
+ }
+ }
+ singleColPlacements.push(singlePlacements);
+ multiColPlacements.push(multiPlacements);
+ leftoverMargins.push(currentMarginTop);
+ }
+ return { singleColPlacements: singleColPlacements, multiColPlacements: multiColPlacements, leftoverMargins: leftoverMargins };
+ }
+ function groupRectsByEachCol(rects, colCnt) {
+ var rectsByEachCol = [];
+ for (var col = 0; col < colCnt; col += 1) {
+ rectsByEachCol.push([]);
+ }
+ for (var _i = 0, rects_3 = rects; _i < rects_3.length; _i++) {
+ var rect = rects_3[_i];
+ for (var col = rect.span.start; col < rect.span.end; col += 1) {
+ rectsByEachCol[col].push(rect);
+ }
+ }
+ return rectsByEachCol;
+ }
+ function resliceSeg(seg, spanStart, spanEnd, cells) {
+ if (seg.firstCol === spanStart && seg.lastCol === spanEnd - 1) {
+ return seg;
+ }
+ var eventRange = seg.eventRange;
+ var origRange = eventRange.range;
+ var slicedRange = intersectRanges(origRange, {
+ start: cells[spanStart].date,
+ end: addDays(cells[spanEnd - 1].date, 1),
+ });
+ return __assign(__assign({}, seg), { firstCol: spanStart, lastCol: spanEnd - 1, eventRange: {
+ def: eventRange.def,
+ ui: __assign(__assign({}, eventRange.ui), { durationEditable: false }),
+ instance: eventRange.instance,
+ range: slicedRange,
+ }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() });
+ }
+ var DayGridSegHierarchy = /** @class */ (function (_super) {
+ __extends(DayGridSegHierarchy, _super);
+ function DayGridSegHierarchy() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ // config
+ _this.hiddenConsumes = false;
+ // allows us to keep hidden entries in the hierarchy so they take up space
+ _this.forceHidden = {};
+ return _this;
+ }
+ DayGridSegHierarchy.prototype.addSegs = function (segInputs) {
+ var _this = this;
+ var hiddenSegs = _super.prototype.addSegs.call(this, segInputs);
+ var entriesByLevel = this.entriesByLevel;
+ var excludeHidden = function (entry) { return !_this.forceHidden[buildEntryKey(entry)]; };
+ // remove the forced-hidden segs
+ for (var level = 0; level < entriesByLevel.length; level += 1) {
+ entriesByLevel[level] = entriesByLevel[level].filter(excludeHidden);
+ }
+ return hiddenSegs;
+ };
+ DayGridSegHierarchy.prototype.handleInvalidInsertion = function (insertion, entry, hiddenEntries) {
+ var _a = this, entriesByLevel = _a.entriesByLevel, forceHidden = _a.forceHidden;
+ var touchingEntry = insertion.touchingEntry, touchingLevel = insertion.touchingLevel, touchingLateral = insertion.touchingLateral;
+ if (this.hiddenConsumes && touchingEntry) {
+ var touchingEntryId = buildEntryKey(touchingEntry);
+ // if not already hidden
+ if (!forceHidden[touchingEntryId]) {
+ if (this.allowReslicing) {
+ var placeholderEntry = __assign(__assign({}, touchingEntry), { span: intersectSpans(touchingEntry.span, entry.span) });
+ var placeholderEntryId = buildEntryKey(placeholderEntry);
+ forceHidden[placeholderEntryId] = true;
+ entriesByLevel[touchingLevel][touchingLateral] = placeholderEntry; // replace touchingEntry with our placeholder
+ this.splitEntry(touchingEntry, entry, hiddenEntries); // split up the touchingEntry, reinsert it
+ }
+ else {
+ forceHidden[touchingEntryId] = true;
+ hiddenEntries.push(touchingEntry);
+ }
+ }
+ }
+ return _super.prototype.handleInvalidInsertion.call(this, insertion, entry, hiddenEntries);
+ };
+ return DayGridSegHierarchy;
+ }(SegHierarchy));
+
+ var TableRow = /** @class */ (function (_super) {
+ __extends(TableRow, _super);
+ function TableRow() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.cellElRefs = new RefMap(); // the
+ _this.frameElRefs = new RefMap(); // the fc-daygrid-day-frame
+ _this.fgElRefs = new RefMap(); // the fc-daygrid-day-events
+ _this.segHarnessRefs = new RefMap(); // indexed by "instanceId:firstCol"
+ _this.rootElRef = createRef();
+ _this.state = {
+ framePositions: null,
+ maxContentHeight: null,
+ eventInstanceHeights: {},
+ };
+ return _this;
+ }
+ TableRow.prototype.render = function () {
+ var _this = this;
+ var _a = this, props = _a.props, state = _a.state, context = _a.context;
+ var options = context.options;
+ var colCnt = props.cells.length;
+ var businessHoursByCol = splitSegsByFirstCol(props.businessHourSegs, colCnt);
+ var bgEventSegsByCol = splitSegsByFirstCol(props.bgEventSegs, colCnt);
+ var highlightSegsByCol = splitSegsByFirstCol(this.getHighlightSegs(), colCnt);
+ var mirrorSegsByCol = splitSegsByFirstCol(this.getMirrorSegs(), colCnt);
+ var _b = computeFgSegPlacement(sortEventSegs(props.fgEventSegs, options.eventOrder), props.dayMaxEvents, props.dayMaxEventRows, options.eventOrderStrict, state.eventInstanceHeights, state.maxContentHeight, props.cells), singleColPlacements = _b.singleColPlacements, multiColPlacements = _b.multiColPlacements, moreCnts = _b.moreCnts, moreMarginTops = _b.moreMarginTops;
+ var isForcedInvisible = // TODO: messy way to compute this
+ (props.eventDrag && props.eventDrag.affectedInstances) ||
+ (props.eventResize && props.eventResize.affectedInstances) ||
+ {};
+ return (createElement("tr", { ref: this.rootElRef },
+ props.renderIntro && props.renderIntro(),
+ props.cells.map(function (cell, col) {
+ var normalFgNodes = _this.renderFgSegs(col, props.forPrint ? singleColPlacements[col] : multiColPlacements[col], props.todayRange, isForcedInvisible);
+ var mirrorFgNodes = _this.renderFgSegs(col, buildMirrorPlacements(mirrorSegsByCol[col], multiColPlacements), props.todayRange, {}, Boolean(props.eventDrag), Boolean(props.eventResize), false);
+ return (createElement(TableCell, { key: cell.key, elRef: _this.cellElRefs.createRef(cell.key), innerElRef: _this.frameElRefs.createRef(cell.key) /* FF
problem, but okay to use for left/right. TODO: rename prop */, dateProfile: props.dateProfile, date: cell.date, showDayNumber: props.showDayNumbers, showWeekNumber: props.showWeekNumbers && col === 0, forceDayTop: props.showWeekNumbers /* even displaying weeknum for row, not necessarily day */, todayRange: props.todayRange, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, extraHookProps: cell.extraHookProps, extraDataAttrs: cell.extraDataAttrs, extraClassNames: cell.extraClassNames, extraDateSpan: cell.extraDateSpan, moreCnt: moreCnts[col], moreMarginTop: moreMarginTops[col], singlePlacements: singleColPlacements[col], fgContentElRef: _this.fgElRefs.createRef(cell.key), fgContent: ( // Fragment scopes the keys
+ createElement(Fragment, null,
+ createElement(Fragment, null, normalFgNodes),
+ createElement(Fragment, null, mirrorFgNodes))), bgContent: ( // Fragment scopes the keys
+ createElement(Fragment, null,
+ _this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
+ _this.renderFillSegs(businessHoursByCol[col], 'non-business'),
+ _this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))) }));
+ })));
+ };
+ TableRow.prototype.componentDidMount = function () {
+ this.updateSizing(true);
+ };
+ TableRow.prototype.componentDidUpdate = function (prevProps, prevState) {
+ var currentProps = this.props;
+ this.updateSizing(!isPropsEqual(prevProps, currentProps));
+ };
+ TableRow.prototype.getHighlightSegs = function () {
+ var props = this.props;
+ if (props.eventDrag && props.eventDrag.segs.length) { // messy check
+ return props.eventDrag.segs;
+ }
+ if (props.eventResize && props.eventResize.segs.length) { // messy check
+ return props.eventResize.segs;
+ }
+ return props.dateSelectionSegs;
+ };
+ TableRow.prototype.getMirrorSegs = function () {
+ var props = this.props;
+ if (props.eventResize && props.eventResize.segs.length) { // messy check
+ return props.eventResize.segs;
+ }
+ return [];
+ };
+ TableRow.prototype.renderFgSegs = function (col, segPlacements, todayRange, isForcedInvisible, isDragging, isResizing, isDateSelecting) {
+ var context = this.context;
+ var eventSelection = this.props.eventSelection;
+ var framePositions = this.state.framePositions;
+ var defaultDisplayEventEnd = this.props.cells.length === 1; // colCnt === 1
+ var isMirror = isDragging || isResizing || isDateSelecting;
+ var nodes = [];
+ if (framePositions) {
+ for (var _i = 0, segPlacements_1 = segPlacements; _i < segPlacements_1.length; _i++) {
+ var placement = segPlacements_1[_i];
+ var seg = placement.seg;
+ var instanceId = seg.eventRange.instance.instanceId;
+ var key = instanceId + ':' + col;
+ var isVisible = placement.isVisible && !isForcedInvisible[instanceId];
+ var isAbsolute = placement.isAbsolute;
+ var left = '';
+ var right = '';
+ if (isAbsolute) {
+ if (context.isRtl) {
+ right = 0;
+ left = framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol];
+ }
+ else {
+ left = 0;
+ right = framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol];
+ }
+ }
+ /*
+ known bug: events that are force to be list-item but span multiple days still take up space in later columns
+ todo: in print view, for multi-day events, don't display title within non-start/end segs
+ */
+ nodes.push(createElement("div", { className: 'fc-daygrid-event-harness' + (isAbsolute ? ' fc-daygrid-event-harness-abs' : ''), key: key, ref: isMirror ? null : this.segHarnessRefs.createRef(key), style: {
+ visibility: isVisible ? '' : 'hidden',
+ marginTop: isAbsolute ? '' : placement.marginTop,
+ top: isAbsolute ? placement.absoluteTop : '',
+ left: left,
+ right: right,
+ } }, hasListItemDisplay(seg) ? (createElement(TableListItemEvent, __assign({ seg: seg, isDragging: isDragging, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange)))) : (createElement(TableBlockEvent, __assign({ seg: seg, isDragging: isDragging, isResizing: isResizing, isDateSelecting: isDateSelecting, isSelected: instanceId === eventSelection, defaultDisplayEventEnd: defaultDisplayEventEnd }, getSegMeta(seg, todayRange))))));
+ }
+ }
+ return nodes;
+ };
+ TableRow.prototype.renderFillSegs = function (segs, fillType) {
+ var isRtl = this.context.isRtl;
+ var todayRange = this.props.todayRange;
+ var framePositions = this.state.framePositions;
+ var nodes = [];
+ if (framePositions) {
+ for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+ var seg = segs_1[_i];
+ var leftRightCss = isRtl ? {
+ right: 0,
+ left: framePositions.lefts[seg.lastCol] - framePositions.lefts[seg.firstCol],
+ } : {
+ left: 0,
+ right: framePositions.rights[seg.firstCol] - framePositions.rights[seg.lastCol],
+ };
+ nodes.push(createElement("div", { key: buildEventRangeKey(seg.eventRange), className: "fc-daygrid-bg-harness", style: leftRightCss }, fillType === 'bg-event' ?
+ createElement(BgEvent, __assign({ seg: seg }, getSegMeta(seg, todayRange))) :
+ renderFill(fillType)));
+ }
+ }
+ return createElement.apply(void 0, __spreadArray([Fragment, {}], nodes));
+ };
+ TableRow.prototype.updateSizing = function (isExternalSizingChange) {
+ var _a = this, props = _a.props, frameElRefs = _a.frameElRefs;
+ if (!props.forPrint &&
+ props.clientWidth !== null // positioning ready?
+ ) {
+ if (isExternalSizingChange) {
+ var frameEls = props.cells.map(function (cell) { return frameElRefs.currentMap[cell.key]; });
+ if (frameEls.length) {
+ var originEl = this.rootElRef.current;
+ this.setState({
+ framePositions: new PositionCache(originEl, frameEls, true, // isHorizontal
+ false),
+ });
+ }
+ }
+ var limitByContentHeight = props.dayMaxEvents === true || props.dayMaxEventRows === true;
+ this.setState({
+ eventInstanceHeights: this.queryEventInstanceHeights(),
+ maxContentHeight: limitByContentHeight ? this.computeMaxContentHeight() : null,
+ });
+ }
+ };
+ TableRow.prototype.queryEventInstanceHeights = function () {
+ var segElMap = this.segHarnessRefs.currentMap;
+ var eventInstanceHeights = {};
+ // get the max height amongst instance segs
+ for (var key in segElMap) {
+ var height = Math.round(segElMap[key].getBoundingClientRect().height);
+ var instanceId = key.split(':')[0]; // deconstruct how renderFgSegs makes the key
+ eventInstanceHeights[instanceId] = Math.max(eventInstanceHeights[instanceId] || 0, height);
+ }
+ return eventInstanceHeights;
+ };
+ TableRow.prototype.computeMaxContentHeight = function () {
+ var firstKey = this.props.cells[0].key;
+ var cellEl = this.cellElRefs.currentMap[firstKey];
+ var fcContainerEl = this.fgElRefs.currentMap[firstKey];
+ return cellEl.getBoundingClientRect().bottom - fcContainerEl.getBoundingClientRect().top;
+ };
+ TableRow.prototype.getCellEls = function () {
+ var elMap = this.cellElRefs.currentMap;
+ return this.props.cells.map(function (cell) { return elMap[cell.key]; });
+ };
+ return TableRow;
+ }(DateComponent));
+ TableRow.addStateEquality({
+ eventInstanceHeights: isPropsEqual,
+ });
+ function buildMirrorPlacements(mirrorSegs, colPlacements) {
+ if (!mirrorSegs.length) {
+ return [];
+ }
+ var topsByInstanceId = buildAbsoluteTopHash(colPlacements); // TODO: cache this at first render?
+ return mirrorSegs.map(function (seg) { return ({
+ seg: seg,
+ isVisible: true,
+ isAbsolute: true,
+ absoluteTop: topsByInstanceId[seg.eventRange.instance.instanceId],
+ marginTop: 0,
+ }); });
+ }
+ function buildAbsoluteTopHash(colPlacements) {
+ var topsByInstanceId = {};
+ for (var _i = 0, colPlacements_1 = colPlacements; _i < colPlacements_1.length; _i++) {
+ var placements = colPlacements_1[_i];
+ for (var _a = 0, placements_1 = placements; _a < placements_1.length; _a++) {
+ var placement = placements_1[_a];
+ topsByInstanceId[placement.seg.eventRange.instance.instanceId] = placement.absoluteTop;
+ }
+ }
+ return topsByInstanceId;
+ }
+
+ var Table = /** @class */ (function (_super) {
+ __extends(Table, _super);
+ function Table() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.splitBusinessHourSegs = memoize(splitSegsByRow);
+ _this.splitBgEventSegs = memoize(splitSegsByRow);
+ _this.splitFgEventSegs = memoize(splitSegsByRow);
+ _this.splitDateSelectionSegs = memoize(splitSegsByRow);
+ _this.splitEventDrag = memoize(splitInteractionByRow);
+ _this.splitEventResize = memoize(splitInteractionByRow);
+ _this.rowRefs = new RefMap();
+ _this.handleRootEl = function (rootEl) {
+ _this.rootEl = rootEl;
+ if (rootEl) {
+ _this.context.registerInteractiveComponent(_this, {
+ el: rootEl,
+ isHitComboAllowed: _this.props.isHitComboAllowed,
+ });
+ }
+ else {
+ _this.context.unregisterInteractiveComponent(_this);
+ }
+ };
+ return _this;
+ }
+ Table.prototype.render = function () {
+ var _this = this;
+ var props = this.props;
+ var dateProfile = props.dateProfile, dayMaxEventRows = props.dayMaxEventRows, dayMaxEvents = props.dayMaxEvents, expandRows = props.expandRows;
+ var rowCnt = props.cells.length;
+ var businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
+ var bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
+ var fgEventSegsByRow = this.splitFgEventSegs(props.fgEventSegs, rowCnt);
+ var dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
+ var eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
+ var eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt);
+ var limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
+ // if rows can't expand to fill fixed height, can't do balanced-height event limit
+ // TODO: best place to normalize these options?
+ if (limitViaBalanced && !expandRows) {
+ limitViaBalanced = false;
+ dayMaxEventRows = null;
+ dayMaxEvents = null;
+ }
+ var classNames = [
+ 'fc-daygrid-body',
+ limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
+ expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
+ ];
+ return (createElement("div", { className: classNames.join(' '), ref: this.handleRootEl, style: {
+ // these props are important to give this wrapper correct dimensions for interactions
+ // TODO: if we set it here, can we avoid giving to inner tables?
+ width: props.clientWidth,
+ minWidth: props.tableMinWidth,
+ } },
+ createElement(NowTimer, { unit: "day" }, function (nowDate, todayRange) { return (createElement(Fragment, null,
+ createElement("table", { className: "fc-scrollgrid-sync-table", style: {
+ width: props.clientWidth,
+ minWidth: props.tableMinWidth,
+ height: expandRows ? props.clientHeight : '',
+ } },
+ props.colGroupNode,
+ createElement("tbody", null, props.cells.map(function (cells, row) { return (createElement(TableRow, { ref: _this.rowRefs.createRef(row), key: cells.length
+ ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
+ : row // in case there are no cells (like when resource view is loading)
+ , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint })); }))))); })));
+ };
+ // Hit System
+ // ----------------------------------------------------------------------------------------------------
+ Table.prototype.prepareHits = function () {
+ this.rowPositions = new PositionCache(this.rootEl, this.rowRefs.collect().map(function (rowObj) { return rowObj.getCellEls()[0]; }), // first cell el in each row. TODO: not optimal
+ false, true);
+ this.colPositions = new PositionCache(this.rootEl, this.rowRefs.currentMap[0].getCellEls(), // cell els in first row
+ true, // horizontal
+ false);
+ };
+ Table.prototype.queryHit = function (positionLeft, positionTop) {
+ var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions;
+ var col = colPositions.leftToIndex(positionLeft);
+ var row = rowPositions.topToIndex(positionTop);
+ if (row != null && col != null) {
+ var cell = this.props.cells[row][col];
+ return {
+ dateProfile: this.props.dateProfile,
+ dateSpan: __assign({ range: this.getCellRange(row, col), allDay: true }, cell.extraDateSpan),
+ dayEl: this.getCellEl(row, col),
+ rect: {
+ left: colPositions.lefts[col],
+ right: colPositions.rights[col],
+ top: rowPositions.tops[row],
+ bottom: rowPositions.bottoms[row],
+ },
+ layer: 0,
+ };
+ }
+ return null;
+ };
+ Table.prototype.getCellEl = function (row, col) {
+ return this.rowRefs.currentMap[row].getCellEls()[col]; // TODO: not optimal
+ };
+ Table.prototype.getCellRange = function (row, col) {
+ var start = this.props.cells[row][col].date;
+ var end = addDays(start, 1);
+ return { start: start, end: end };
+ };
+ return Table;
+ }(DateComponent));
+ function isSegAllDay(seg) {
+ return seg.eventRange.def.allDay;
+ }
+
+ var DayTableSlicer = /** @class */ (function (_super) {
+ __extends(DayTableSlicer, _super);
+ function DayTableSlicer() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.forceDayIfListItem = true;
+ return _this;
+ }
+ DayTableSlicer.prototype.sliceRange = function (dateRange, dayTableModel) {
+ return dayTableModel.sliceRange(dateRange);
+ };
+ return DayTableSlicer;
+ }(Slicer));
+
+ var DayTable = /** @class */ (function (_super) {
+ __extends(DayTable, _super);
+ function DayTable() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.slicer = new DayTableSlicer();
+ _this.tableRef = createRef();
+ return _this;
+ }
+ DayTable.prototype.render = function () {
+ var _a = this, props = _a.props, context = _a.context;
+ return (createElement(Table, __assign({ ref: this.tableRef }, this.slicer.sliceProps(props, props.dateProfile, props.nextDayThreshold, context, props.dayTableModel), { dateProfile: props.dateProfile, cells: props.dayTableModel.cells, colGroupNode: props.colGroupNode, tableMinWidth: props.tableMinWidth, renderRowIntro: props.renderRowIntro, dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, showWeekNumbers: props.showWeekNumbers, expandRows: props.expandRows, headerAlignElRef: props.headerAlignElRef, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint })));
+ };
+ return DayTable;
+ }(DateComponent));
+
+ var DayTableView = /** @class */ (function (_super) {
+ __extends(DayTableView, _super);
+ function DayTableView() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.buildDayTableModel = memoize(buildDayTableModel);
+ _this.headerRef = createRef();
+ _this.tableRef = createRef();
+ return _this;
+ }
+ DayTableView.prototype.render = function () {
+ var _this = this;
+ var _a = this.context, options = _a.options, dateProfileGenerator = _a.dateProfileGenerator;
+ var props = this.props;
+ var dayTableModel = this.buildDayTableModel(props.dateProfile, dateProfileGenerator);
+ var headerContent = options.dayHeaders && (createElement(DayHeader, { ref: this.headerRef, dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: dayTableModel.rowCnt === 1 }));
+ var bodyContent = function (contentArg) { return (createElement(DayTable, { ref: _this.tableRef, dateProfile: props.dateProfile, dayTableModel: dayTableModel, businessHours: props.businessHours, dateSelection: props.dateSelection, eventStore: props.eventStore, eventUiBases: props.eventUiBases, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, nextDayThreshold: options.nextDayThreshold, colGroupNode: contentArg.tableColGroupNode, tableMinWidth: contentArg.tableMinWidth, dayMaxEvents: options.dayMaxEvents, dayMaxEventRows: options.dayMaxEventRows, showWeekNumbers: options.weekNumbers, expandRows: !props.isHeightAuto, headerAlignElRef: _this.headerElRef, clientWidth: contentArg.clientWidth, clientHeight: contentArg.clientHeight, forPrint: props.forPrint })); };
+ return options.dayMinWidth
+ ? this.renderHScrollLayout(headerContent, bodyContent, dayTableModel.colCnt, options.dayMinWidth)
+ : this.renderSimpleLayout(headerContent, bodyContent);
+ };
+ return DayTableView;
+ }(TableView));
+ function buildDayTableModel(dateProfile, dateProfileGenerator) {
+ var daySeries = new DaySeriesModel(dateProfile.renderRange, dateProfileGenerator);
+ return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
+ }
+
+ var TableDateProfileGenerator = /** @class */ (function (_super) {
+ __extends(TableDateProfileGenerator, _super);
+ function TableDateProfileGenerator() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ // Computes the date range that will be rendered.
+ TableDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) {
+ var dateEnv = this.props.dateEnv;
+ var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay);
+ var start = renderRange.start;
+ var end = renderRange.end;
+ var endOfWeek;
+ // year and month views should be aligned with weeks. this is already done for week
+ if (/^(year|month)$/.test(currentRangeUnit)) {
+ start = dateEnv.startOfWeek(start);
+ // make end-of-week if not already
+ endOfWeek = dateEnv.startOfWeek(end);
+ if (endOfWeek.valueOf() !== end.valueOf()) {
+ end = addWeeks(endOfWeek, 1);
+ }
+ }
+ // ensure 6 weeks
+ if (this.props.monthMode &&
+ this.props.fixedWeekCount) {
+ var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
+ diffWeeks(start, end));
+ end = addWeeks(end, 6 - rowCnt);
+ }
+ return { start: start, end: end };
+ };
+ return TableDateProfileGenerator;
+ }(DateProfileGenerator));
+
+ var dayGridPlugin = createPlugin({
+ initialView: 'dayGridMonth',
+ views: {
+ dayGrid: {
+ component: DayTableView,
+ dateProfileGeneratorClass: TableDateProfileGenerator,
+ },
+ dayGridDay: {
+ type: 'dayGrid',
+ duration: { days: 1 },
+ },
+ dayGridWeek: {
+ type: 'dayGrid',
+ duration: { weeks: 1 },
+ },
+ dayGridMonth: {
+ type: 'dayGrid',
+ duration: { months: 1 },
+ monthMode: true,
+ fixedWeekCount: true,
+ },
+ },
+ });
+
+ var AllDaySplitter = /** @class */ (function (_super) {
+ __extends(AllDaySplitter, _super);
+ function AllDaySplitter() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ AllDaySplitter.prototype.getKeyInfo = function () {
+ return {
+ allDay: {},
+ timed: {},
+ };
+ };
+ AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+ if (dateSpan.allDay) {
+ return ['allDay'];
+ }
+ return ['timed'];
+ };
+ AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
+ if (!eventDef.allDay) {
+ return ['timed'];
+ }
+ if (hasBgRendering(eventDef)) {
+ return ['timed', 'allDay'];
+ }
+ return ['allDay'];
+ };
+ return AllDaySplitter;
+ }(Splitter));
+
+ var DEFAULT_SLAT_LABEL_FORMAT = createFormatter({
+ hour: 'numeric',
+ minute: '2-digit',
+ omitZeroMinute: true,
+ meridiem: 'short',
+ });
+ function TimeColsAxisCell(props) {
+ var classNames = [
+ 'fc-timegrid-slot',
+ 'fc-timegrid-slot-label',
+ props.isLabeled ? 'fc-scrollgrid-shrink' : 'fc-timegrid-slot-minor',
+ ];
+ return (createElement(ViewContextType.Consumer, null, function (context) {
+ if (!props.isLabeled) {
+ return (createElement("td", { className: classNames.join(' '), "data-time": props.isoTimeStr }));
+ }
+ var dateEnv = context.dateEnv, options = context.options, viewApi = context.viewApi;
+ var labelFormat = // TODO: fully pre-parse
+ options.slotLabelFormat == null ? DEFAULT_SLAT_LABEL_FORMAT :
+ Array.isArray(options.slotLabelFormat) ? createFormatter(options.slotLabelFormat[0]) :
+ createFormatter(options.slotLabelFormat);
+ var hookProps = {
+ level: 0,
+ time: props.time,
+ date: dateEnv.toDate(props.date),
+ view: viewApi,
+ text: dateEnv.format(props.date, labelFormat),
+ };
+ return (createElement(RenderHook, { hookProps: hookProps, classNames: options.slotLabelClassNames, content: options.slotLabelContent, defaultContent: renderInnerContent$1, didMount: options.slotLabelDidMount, willUnmount: options.slotLabelWillUnmount }, function (rootElRef, customClassNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: classNames.concat(customClassNames).join(' '), "data-time": props.isoTimeStr },
+ createElement("div", { className: "fc-timegrid-slot-label-frame fc-scrollgrid-shrink-frame" },
+ createElement("div", { className: "fc-timegrid-slot-label-cushion fc-scrollgrid-shrink-cushion", ref: innerElRef }, innerContent)))); }));
+ }));
+ }
+ function renderInnerContent$1(props) {
+ return props.text;
+ }
+
+ var TimeBodyAxis = /** @class */ (function (_super) {
+ __extends(TimeBodyAxis, _super);
+ function TimeBodyAxis() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ TimeBodyAxis.prototype.render = function () {
+ return this.props.slatMetas.map(function (slatMeta) { return (createElement("tr", { key: slatMeta.key },
+ createElement(TimeColsAxisCell, __assign({}, slatMeta)))); });
+ };
+ return TimeBodyAxis;
+ }(BaseComponent));
+
+ var DEFAULT_WEEK_NUM_FORMAT = createFormatter({ week: 'short' });
+ var AUTO_ALL_DAY_MAX_EVENT_ROWS = 5;
+ var TimeColsView = /** @class */ (function (_super) {
+ __extends(TimeColsView, _super);
+ function TimeColsView() {
+ var _this = _super !== null && _super.apply(this, arguments) || this;
+ _this.allDaySplitter = new AllDaySplitter(); // for use by subclasses
+ _this.headerElRef = createRef();
+ _this.rootElRef = createRef();
+ _this.scrollerElRef = createRef();
+ _this.state = {
+ slatCoords: null,
+ };
+ _this.handleScrollTopRequest = function (scrollTop) {
+ var scrollerEl = _this.scrollerElRef.current;
+ if (scrollerEl) { // TODO: not sure how this could ever be null. weirdness with the reducer
+ scrollerEl.scrollTop = scrollTop;
+ }
+ };
+ /* Header Render Methods
+ ------------------------------------------------------------------------------------------------------------------*/
+ _this.renderHeadAxis = function (rowKey, frameHeight) {
+ if (frameHeight === void 0) { frameHeight = ''; }
+ var options = _this.context.options;
+ var dateProfile = _this.props.dateProfile;
+ var range = dateProfile.renderRange;
+ var dayCnt = diffDays(range.start, range.end);
+ var navLinkAttrs = (options.navLinks && dayCnt === 1) // only do in day views (to avoid doing in week views that dont need it)
+ ? { 'data-navlink': buildNavLinkData(range.start, 'week'), tabIndex: 0 }
+ : {};
+ if (options.weekNumbers && rowKey === 'day') {
+ return (createElement(WeekNumberRoot, { date: range.start, defaultFormat: DEFAULT_WEEK_NUM_FORMAT }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("th", { ref: rootElRef, className: [
+ 'fc-timegrid-axis',
+ 'fc-scrollgrid-shrink',
+ ].concat(classNames).join(' ') },
+ createElement("div", { className: "fc-timegrid-axis-frame fc-scrollgrid-shrink-frame fc-timegrid-axis-frame-liquid", style: { height: frameHeight } },
+ createElement("a", __assign({ ref: innerElRef, className: "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner" }, navLinkAttrs), innerContent)))); }));
+ }
+ return (createElement("th", { className: "fc-timegrid-axis" },
+ createElement("div", { className: "fc-timegrid-axis-frame", style: { height: frameHeight } })));
+ };
+ /* Table Component Render Methods
+ ------------------------------------------------------------------------------------------------------------------*/
+ // only a one-way height sync. we don't send the axis inner-content height to the DayGrid,
+ // but DayGrid still needs to have classNames on inner elements in order to measure.
+ _this.renderTableRowAxis = function (rowHeight) {
+ var _a = _this.context, options = _a.options, viewApi = _a.viewApi;
+ var hookProps = {
+ text: options.allDayText,
+ view: viewApi,
+ };
+ return (
+ // TODO: make reusable hook. used in list view too
+ createElement(RenderHook, { hookProps: hookProps, classNames: options.allDayClassNames, content: options.allDayContent, defaultContent: renderAllDayInner$1, didMount: options.allDayDidMount, willUnmount: options.allDayWillUnmount }, function (rootElRef, classNames, innerElRef, innerContent) { return (createElement("td", { ref: rootElRef, className: [
+ 'fc-timegrid-axis',
+ 'fc-scrollgrid-shrink',
+ ].concat(classNames).join(' ') },
+ createElement("div", { className: 'fc-timegrid-axis-frame fc-scrollgrid-shrink-frame' + (rowHeight == null ? ' fc-timegrid-axis-frame-liquid' : ''), style: { height: rowHeight } },
+ createElement("span", { className: "fc-timegrid-axis-cushion fc-scrollgrid-shrink-cushion fc-scrollgrid-sync-inner", ref: innerElRef }, innerContent)))); }));
+ };
+ _this.handleSlatCoords = function (slatCoords) {
+ _this.setState({ slatCoords: slatCoords });
+ };
+ return _this;
+ }
+ // rendering
+ // ----------------------------------------------------------------------------------------------------
+ TimeColsView.prototype.renderSimpleLayout = function (headerRowContent, allDayContent, timeContent) {
+ var _a = this, context = _a.context, props = _a.props;
+ var sections = [];
+ var stickyHeaderDates = getStickyHeaderDates(context.options);
+ if (headerRowContent) {
+ sections.push({
+ type: 'header',
+ key: 'header',
+ isSticky: stickyHeaderDates,
+ chunk: {
+ elRef: this.headerElRef,
+ tableClassName: 'fc-col-header',
+ rowContent: headerRowContent,
+ },
+ });
+ }
+ if (allDayContent) {
+ sections.push({
+ type: 'body',
+ key: 'all-day',
+ chunk: { content: allDayContent },
+ });
+ sections.push({
+ type: 'body',
+ key: 'all-day-divider',
+ outerContent: ( // TODO: rename to cellContent so don't need to define