UNPKG

445 kBJavaScriptView Raw
1/*!
2FullCalendar Core v6.1.15
3Docs & License: https://fullcalendar.io
4(c) 2024 Adam Shaw
5*/
6var FullCalendar = (function (exports) {
7 'use strict';
8
9 const styleTexts = [];
10 const styleEls = new Map();
11 function injectStyles(styleText) {
12 styleTexts.push(styleText);
13 styleEls.forEach((styleEl) => {
14 appendStylesTo(styleEl, styleText);
15 });
16 }
17 function ensureElHasStyles(el) {
18 if (el.isConnected && // sometimes true if SSR system simulates DOM
19 el.getRootNode // sometimes undefined if SSR system simulates DOM
20 ) {
21 registerStylesRoot(el.getRootNode());
22 }
23 }
24 function registerStylesRoot(rootNode) {
25 let styleEl = styleEls.get(rootNode);
26 if (!styleEl || !styleEl.isConnected) {
27 styleEl = rootNode.querySelector('style[data-fullcalendar]');
28 if (!styleEl) {
29 styleEl = document.createElement('style');
30 styleEl.setAttribute('data-fullcalendar', '');
31 const nonce = getNonceValue();
32 if (nonce) {
33 styleEl.nonce = nonce;
34 }
35 const parentEl = rootNode === document ? document.head : rootNode;
36 const insertBefore = rootNode === document
37 ? parentEl.querySelector('script,link[rel=stylesheet],link[as=style],style')
38 : parentEl.firstChild;
39 parentEl.insertBefore(styleEl, insertBefore);
40 }
41 styleEls.set(rootNode, styleEl);
42 hydrateStylesRoot(styleEl);
43 }
44 }
45 function hydrateStylesRoot(styleEl) {
46 for (const styleText of styleTexts) {
47 appendStylesTo(styleEl, styleText);
48 }
49 }
50 function appendStylesTo(styleEl, styleText) {
51 const { sheet } = styleEl;
52 const ruleCnt = sheet.cssRules.length;
53 styleText.split('}').forEach((styleStr, i) => {
54 styleStr = styleStr.trim();
55 if (styleStr) {
56 sheet.insertRule(styleStr + '}', ruleCnt + i);
57 }
58 });
59 }
60 // nonce
61 // -------------------------------------------------------------------------------------------------
62 let queriedNonceValue;
63 function getNonceValue() {
64 if (queriedNonceValue === undefined) {
65 queriedNonceValue = queryNonceValue();
66 }
67 return queriedNonceValue;
68 }
69 /*
70 TODO: discourage meta tag and instead put nonce attribute on placeholder <style> tag
71 */
72 function queryNonceValue() {
73 const metaWithNonce = document.querySelector('meta[name="csp-nonce"]');
74 if (metaWithNonce && metaWithNonce.hasAttribute('content')) {
75 return metaWithNonce.getAttribute('content');
76 }
77 const elWithNonce = document.querySelector('script[nonce]');
78 if (elWithNonce) {
79 return elWithNonce.nonce || '';
80 }
81 return '';
82 }
83 // main
84 // -------------------------------------------------------------------------------------------------
85 if (typeof document !== 'undefined') {
86 registerStylesRoot(document);
87 }
88
89 var css_248z = ":root{--fc-small-font-size:.85em;--fc-page-bg-color:#fff;--fc-neutral-bg-color:hsla(0,0%,82%,.3);--fc-neutral-text-color:grey;--fc-border-color:#ddd;--fc-button-text-color:#fff;--fc-button-bg-color:#2c3e50;--fc-button-border-color:#2c3e50;--fc-button-hover-bg-color:#1e2b37;--fc-button-hover-border-color:#1a252f;--fc-button-active-bg-color:#1a252f;--fc-button-active-border-color:#151e27;--fc-event-bg-color:#3788d8;--fc-event-border-color:#3788d8;--fc-event-text-color:#fff;--fc-event-selected-overlay-color:rgba(0,0,0,.25);--fc-more-link-bg-color:#d0d0d0;--fc-more-link-text-color:inherit;--fc-event-resizer-thickness:8px;--fc-event-resizer-dot-total-width:8px;--fc-event-resizer-dot-border-width:1px;--fc-non-business-color:hsla(0,0%,84%,.3);--fc-bg-event-color:#8fdf82;--fc-bg-event-opacity:0.3;--fc-highlight-color:rgba(188,232,241,.3);--fc-today-bg-color:rgba(255,220,40,.15);--fc-now-indicator-color:red}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc{display:flex;flex-direction:column;font-size:1em}.fc,.fc *,.fc :after,.fc :before{box-sizing:border-box}.fc table{border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{padding:0;vertical-align:top}.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 var(--fc-border-color)}.fc-liquid-hack td,.fc-liquid-hack th{position:relative}@font-face{font-family:fcicons;font-style:normal;font-weight:400;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\")}.fc-icon{speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;font-family:fcicons!important;font-style:normal;font-variant:normal;font-weight:400;height:1em;line-height:1;text-align:center;text-transform:none;-moz-user-select:none;user-select:none;width:1em}.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\"}.fc .fc-button{border-radius:0;font-family:inherit;font-size:inherit;line-height:inherit;margin:0;overflow:visible;text-transform:none}.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{background-color:transparent;border:1px solid transparent;border-radius:.25em;display:inline-block;font-size:1em;font-weight:400;line-height:1.5;padding:.4em .65em;text-align:center;-moz-user-select:none;user-select:none;vertical-align:middle}.fc .fc-button:hover{text-decoration:none}.fc .fc-button:focus{box-shadow:0 0 0 .2rem rgba(44,62,80,.25);outline:0}.fc .fc-button:disabled{opacity:.65}.fc .fc-button-primary{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:hover{background-color:var(--fc-button-hover-bg-color);border-color:var(--fc-button-hover-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:disabled{background-color:var(--fc-button-bg-color);border-color:var(--fc-button-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc .fc-button-primary:not(:disabled).fc-button-active,.fc .fc-button-primary:not(:disabled):active{background-color:var(--fc-button-active-bg-color);border-color:var(--fc-button-active-border-color);color:var(--fc-button-text-color)}.fc .fc-button-primary:not(:disabled).fc-button-active:focus,.fc .fc-button-primary:not(:disabled):active:focus{box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc .fc-button .fc-icon{font-size:1.5em;vertical-align:middle}.fc .fc-button-group{display:inline-flex;position:relative;vertical-align:middle}.fc .fc-button-group>.fc-button{flex:1 1 auto;position:relative}.fc .fc-button-group>.fc-button.fc-button-active,.fc .fc-button-group>.fc-button:active,.fc .fc-button-group>.fc-button:focus,.fc .fc-button-group>.fc-button:hover{z-index:1}.fc-direction-ltr .fc-button-group>.fc-button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:-1px}.fc-direction-ltr .fc-button-group>.fc-button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.fc-direction-rtl .fc-button-group>.fc-button:not(:first-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.fc-direction-rtl .fc-button-group>.fc-button:not(:last-child){border-bottom-left-radius:0;border-top-left-radius:0}.fc .fc-toolbar{align-items:center;display:flex;justify-content:space-between}.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}.fc-direction-rtl .fc-toolbar>*>:not(:first-child){margin-right:.75em}.fc-direction-rtl .fc-toolbar-ltr{flex-direction:row-reverse}.fc .fc-scroller{-webkit-overflow-scrolling:touch;position:relative}.fc .fc-scroller-liquid{height:100%}.fc .fc-scroller-liquid-absolute{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-scroller-harness{direction:ltr;overflow:hidden;position:relative}.fc .fc-scroller-harness-liquid{height:100%}.fc-direction-rtl .fc-scroller-harness>.fc-scroller{direction:rtl}.fc-theme-standard .fc-scrollgrid{border:1px solid var(--fc-border-color)}.fc .fc-scrollgrid,.fc .fc-scrollgrid table{table-layout:fixed;width:100%}.fc .fc-scrollgrid table{border-left-style:hidden;border-right-style:hidden;border-top-style:hidden}.fc .fc-scrollgrid{border-bottom-width:0;border-collapse:separate;border-right-width:0}.fc .fc-scrollgrid-liquid{height:100%}.fc .fc-scrollgrid-section,.fc .fc-scrollgrid-section table,.fc .fc-scrollgrid-section>td{height:1px}.fc .fc-scrollgrid-section-liquid>td{height:100%}.fc .fc-scrollgrid-section>*{border-left-width:0;border-top-width:0}.fc .fc-scrollgrid-section-footer>*,.fc .fc-scrollgrid-section-header>*{border-bottom-width:0}.fc .fc-scrollgrid-section-body table,.fc .fc-scrollgrid-section-footer table{border-bottom-style:hidden}.fc .fc-scrollgrid-section-sticky>*{background:var(--fc-page-bg-color);position:sticky;z-index:3}.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky>*{top:0}.fc .fc-scrollgrid-section-footer.fc-scrollgrid-section-sticky>*{bottom:0}.fc .fc-scrollgrid-sticky-shim{height:1px;margin-bottom:-1px}.fc-sticky{position:sticky}.fc .fc-view-harness{flex-grow:1;position:relative}.fc .fc-view-harness-active>.fc-view{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-col-header-cell-cushion{display:inline-block;padding:2px 4px}.fc .fc-bg-event,.fc .fc-highlight,.fc .fc-non-business{bottom:0;left:0;position:absolute;right:0;top:0}.fc .fc-non-business{background:var(--fc-non-business-color)}.fc .fc-bg-event{background:var(--fc-bg-event-color);opacity:var(--fc-bg-event-opacity)}.fc .fc-bg-event .fc-event-title{font-size:var(--fc-small-font-size);font-style:italic;margin:.5em}.fc .fc-highlight{background:var(--fc-highlight-color)}.fc .fc-cell-shaded,.fc .fc-day-disabled{background:var(--fc-neutral-bg-color)}a.fc-event,a.fc-event:hover{text-decoration:none}.fc-event.fc-event-draggable,.fc-event[href]{cursor:pointer}.fc-event .fc-event-main{position:relative;z-index:2}.fc-event-dragging:not(.fc-event-selected){opacity:.75}.fc-event-dragging.fc-event-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event .fc-event-resizer{display:none;position:absolute;z-index:4}.fc-event-selected .fc-event-resizer,.fc-event:hover .fc-event-resizer{display:block}.fc-event-selected .fc-event-resizer{background:var(--fc-page-bg-color);border-color:inherit;border-radius:calc(var(--fc-event-resizer-dot-total-width)/2);border-style:solid;border-width:var(--fc-event-resizer-dot-border-width);height:var(--fc-event-resizer-dot-total-width);width:var(--fc-event-resizer-dot-total-width)}.fc-event-selected .fc-event-resizer:before{bottom:-20px;content:\"\";left:-20px;position:absolute;right:-20px;top:-20px}.fc-event-selected,.fc-event:focus{box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event-selected:before,.fc-event:focus:before{bottom:0;content:\"\";left:0;position:absolute;right:0;top:0;z-index:3}.fc-event-selected:after,.fc-event:focus:after{background:var(--fc-event-selected-overlay-color);bottom:-1px;content:\"\";left:-1px;position:absolute;right:-1px;top:-1px;z-index:1}.fc-h-event{background-color:var(--fc-event-bg-color);border:1px solid var(--fc-event-border-color);display:block}.fc-h-event .fc-event-main{color:var(--fc-event-text-color)}.fc-h-event .fc-event-main-frame{display:flex}.fc-h-event .fc-event-time{max-width:100%;overflow:hidden}.fc-h-event .fc-event-title-container{flex-grow:1;flex-shrink:1;min-width:0}.fc-h-event .fc-event-title{display:inline-block;left:0;max-width:100%;overflow:hidden;right:0;vertical-align:top}.fc-h-event.fc-event-selected:before{bottom:-10px;top:-10px}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-start),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-end){border-bottom-left-radius:0;border-left-width:0;border-top-left-radius:0}.fc-direction-ltr .fc-daygrid-block-event:not(.fc-event-end),.fc-direction-rtl .fc-daygrid-block-event:not(.fc-event-start){border-bottom-right-radius:0;border-right-width:0;border-top-right-radius:0}.fc-h-event:not(.fc-event-selected) .fc-event-resizer{bottom:0;top:0;width:var(--fc-event-resizer-thickness)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end{cursor:w-resize;left:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-direction-ltr .fc-h-event:not(.fc-event-selected) .fc-event-resizer-end,.fc-direction-rtl .fc-h-event:not(.fc-event-selected) .fc-event-resizer-start{cursor:e-resize;right:calc(var(--fc-event-resizer-thickness)*-.5)}.fc-h-event.fc-event-selected .fc-event-resizer{margin-top:calc(var(--fc-event-resizer-dot-total-width)*-.5);top:50%}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-start,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-end{left:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc-direction-ltr .fc-h-event.fc-event-selected .fc-event-resizer-end,.fc-direction-rtl .fc-h-event.fc-event-selected .fc-event-resizer-start{right:calc(var(--fc-event-resizer-dot-total-width)*-.5)}.fc .fc-popover{box-shadow:0 2px 6px rgba(0,0,0,.15);position:absolute;z-index:9999}.fc .fc-popover-header{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:3px 4px}.fc .fc-popover-title{margin:0 2px}.fc .fc-popover-close{cursor:pointer;font-size:1.1em;opacity:.65}.fc-theme-standard .fc-popover{background:var(--fc-page-bg-color);border:1px solid var(--fc-border-color)}.fc-theme-standard .fc-popover-header{background:var(--fc-neutral-bg-color)}";
90 injectStyles(css_248z);
91
92 function removeElement(el) {
93 if (el.parentNode) {
94 el.parentNode.removeChild(el);
95 }
96 }
97 // Querying
98 // ----------------------------------------------------------------------------------------------------------------
99 function elementClosest(el, selector) {
100 if (el.closest) {
101 return el.closest(selector);
102 // really bad fallback for IE
103 // from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
104 }
105 if (!document.documentElement.contains(el)) {
106 return null;
107 }
108 do {
109 if (elementMatches(el, selector)) {
110 return el;
111 }
112 el = (el.parentElement || el.parentNode);
113 } while (el !== null && el.nodeType === 1);
114 return null;
115 }
116 function elementMatches(el, selector) {
117 let method = el.matches || el.matchesSelector || el.msMatchesSelector;
118 return method.call(el, selector);
119 }
120 // accepts multiple subject els
121 // returns a real array. good for methods like forEach
122 // TODO: accept the document
123 function findElements(container, selector) {
124 let containers = container instanceof HTMLElement ? [container] : container;
125 let allMatches = [];
126 for (let i = 0; i < containers.length; i += 1) {
127 let matches = containers[i].querySelectorAll(selector);
128 for (let j = 0; j < matches.length; j += 1) {
129 allMatches.push(matches[j]);
130 }
131 }
132 return allMatches;
133 }
134 // accepts multiple subject els
135 // only queries direct child elements // TODO: rename to findDirectChildren!
136 function findDirectChildren(parent, selector) {
137 let parents = parent instanceof HTMLElement ? [parent] : parent;
138 let allMatches = [];
139 for (let i = 0; i < parents.length; i += 1) {
140 let childNodes = parents[i].children; // only ever elements
141 for (let j = 0; j < childNodes.length; j += 1) {
142 let childNode = childNodes[j];
143 if (!selector || elementMatches(childNode, selector)) {
144 allMatches.push(childNode);
145 }
146 }
147 }
148 return allMatches;
149 }
150 // Style
151 // ----------------------------------------------------------------------------------------------------------------
152 const PIXEL_PROP_RE = /(top|left|right|bottom|width|height)$/i;
153 function applyStyle(el, props) {
154 for (let propName in props) {
155 applyStyleProp(el, propName, props[propName]);
156 }
157 }
158 function applyStyleProp(el, name, val) {
159 if (val == null) {
160 el.style[name] = '';
161 }
162 else if (typeof val === 'number' && PIXEL_PROP_RE.test(name)) {
163 el.style[name] = `${val}px`;
164 }
165 else {
166 el.style[name] = val;
167 }
168 }
169 // Event Handling
170 // ----------------------------------------------------------------------------------------------------------------
171 // if intercepting bubbled events at the document/window/body level,
172 // and want to see originating element (the 'target'), use this util instead
173 // of `ev.target` because it goes within web-component boundaries.
174 function getEventTargetViaRoot(ev) {
175 var _a, _b;
176 return (_b = (_a = ev.composedPath) === null || _a === void 0 ? void 0 : _a.call(ev)[0]) !== null && _b !== void 0 ? _b : ev.target;
177 }
178 // Unique ID for DOM attribute
179 let guid$1 = 0;
180 function getUniqueDomId() {
181 guid$1 += 1;
182 return 'fc-dom-' + guid$1;
183 }
184
185 // Stops a mouse/touch event from doing it's native browser action
186 function preventDefault(ev) {
187 ev.preventDefault();
188 }
189 // Event Delegation
190 // ----------------------------------------------------------------------------------------------------------------
191 function buildDelegationHandler(selector, handler) {
192 return (ev) => {
193 let matchedChild = elementClosest(ev.target, selector);
194 if (matchedChild) {
195 handler.call(matchedChild, ev, matchedChild);
196 }
197 };
198 }
199 function listenBySelector(container, eventType, selector, handler) {
200 let attachedHandler = buildDelegationHandler(selector, handler);
201 container.addEventListener(eventType, attachedHandler);
202 return () => {
203 container.removeEventListener(eventType, attachedHandler);
204 };
205 }
206 function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) {
207 let currentMatchedChild;
208 return listenBySelector(container, 'mouseover', selector, (mouseOverEv, matchedChild) => {
209 if (matchedChild !== currentMatchedChild) {
210 currentMatchedChild = matchedChild;
211 onMouseEnter(mouseOverEv, matchedChild);
212 let realOnMouseLeave = (mouseLeaveEv) => {
213 currentMatchedChild = null;
214 onMouseLeave(mouseLeaveEv, matchedChild);
215 matchedChild.removeEventListener('mouseleave', realOnMouseLeave);
216 };
217 // listen to the next mouseleave, and then unattach
218 matchedChild.addEventListener('mouseleave', realOnMouseLeave);
219 }
220 });
221 }
222 // Animation
223 // ----------------------------------------------------------------------------------------------------------------
224 const transitionEventNames = [
225 'webkitTransitionEnd',
226 'otransitionend',
227 'oTransitionEnd',
228 'msTransitionEnd',
229 'transitionend',
230 ];
231 // triggered only when the next single subsequent transition finishes
232 function whenTransitionDone(el, callback) {
233 let realCallback = (ev) => {
234 callback(ev);
235 transitionEventNames.forEach((eventName) => {
236 el.removeEventListener(eventName, realCallback);
237 });
238 };
239 transitionEventNames.forEach((eventName) => {
240 el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes
241 });
242 }
243 // ARIA workarounds
244 // ----------------------------------------------------------------------------------------------------------------
245 function createAriaClickAttrs(handler) {
246 return Object.assign({ onClick: handler }, createAriaKeyboardAttrs(handler));
247 }
248 function createAriaKeyboardAttrs(handler) {
249 return {
250 tabIndex: 0,
251 onKeyDown(ev) {
252 if (ev.key === 'Enter' || ev.key === ' ') {
253 handler(ev);
254 ev.preventDefault(); // if space, don't scroll down page
255 }
256 },
257 };
258 }
259
260 let guidNumber = 0;
261 function guid() {
262 guidNumber += 1;
263 return String(guidNumber);
264 }
265 /* FullCalendar-specific DOM Utilities
266 ----------------------------------------------------------------------------------------------------------------------*/
267 // Make the mouse cursor express that an event is not allowed in the current area
268 function disableCursor() {
269 document.body.classList.add('fc-not-allowed');
270 }
271 // Returns the mouse cursor to its original look
272 function enableCursor() {
273 document.body.classList.remove('fc-not-allowed');
274 }
275 /* Selection
276 ----------------------------------------------------------------------------------------------------------------------*/
277 function preventSelection(el) {
278 el.style.userSelect = 'none';
279 el.style.webkitUserSelect = 'none';
280 el.addEventListener('selectstart', preventDefault);
281 }
282 function allowSelection(el) {
283 el.style.userSelect = '';
284 el.style.webkitUserSelect = '';
285 el.removeEventListener('selectstart', preventDefault);
286 }
287 /* Context Menu
288 ----------------------------------------------------------------------------------------------------------------------*/
289 function preventContextMenu(el) {
290 el.addEventListener('contextmenu', preventDefault);
291 }
292 function allowContextMenu(el) {
293 el.removeEventListener('contextmenu', preventDefault);
294 }
295 function parseFieldSpecs(input) {
296 let specs = [];
297 let tokens = [];
298 let i;
299 let token;
300 if (typeof input === 'string') {
301 tokens = input.split(/\s*,\s*/);
302 }
303 else if (typeof input === 'function') {
304 tokens = [input];
305 }
306 else if (Array.isArray(input)) {
307 tokens = input;
308 }
309 for (i = 0; i < tokens.length; i += 1) {
310 token = tokens[i];
311 if (typeof token === 'string') {
312 specs.push(token.charAt(0) === '-' ?
313 { field: token.substring(1), order: -1 } :
314 { field: token, order: 1 });
315 }
316 else if (typeof token === 'function') {
317 specs.push({ func: token });
318 }
319 }
320 return specs;
321 }
322 function compareByFieldSpecs(obj0, obj1, fieldSpecs) {
323 let i;
324 let cmp;
325 for (i = 0; i < fieldSpecs.length; i += 1) {
326 cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]);
327 if (cmp) {
328 return cmp;
329 }
330 }
331 return 0;
332 }
333 function compareByFieldSpec(obj0, obj1, fieldSpec) {
334 if (fieldSpec.func) {
335 return fieldSpec.func(obj0, obj1);
336 }
337 return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field])
338 * (fieldSpec.order || 1);
339 }
340 function flexibleCompare(a, b) {
341 if (!a && !b) {
342 return 0;
343 }
344 if (b == null) {
345 return -1;
346 }
347 if (a == null) {
348 return 1;
349 }
350 if (typeof a === 'string' || typeof b === 'string') {
351 return String(a).localeCompare(String(b));
352 }
353 return a - b;
354 }
355 /* String Utilities
356 ----------------------------------------------------------------------------------------------------------------------*/
357 function padStart(val, len) {
358 let s = String(val);
359 return '000'.substr(0, len - s.length) + s;
360 }
361 function formatWithOrdinals(formatter, args, fallbackText) {
362 if (typeof formatter === 'function') {
363 return formatter(...args);
364 }
365 if (typeof formatter === 'string') { // non-blank string
366 return args.reduce((str, arg, index) => (str.replace('$' + index, arg || '')), formatter);
367 }
368 return fallbackText;
369 }
370 /* Number Utilities
371 ----------------------------------------------------------------------------------------------------------------------*/
372 function compareNumbers(a, b) {
373 return a - b;
374 }
375 function isInt(n) {
376 return n % 1 === 0;
377 }
378 /* FC-specific DOM dimension stuff
379 ----------------------------------------------------------------------------------------------------------------------*/
380 function computeSmallestCellWidth(cellEl) {
381 let allWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-frame');
382 let contentWidthEl = cellEl.querySelector('.fc-scrollgrid-shrink-cushion');
383 if (!allWidthEl) {
384 throw new Error('needs fc-scrollgrid-shrink-frame className'); // TODO: use const
385 }
386 if (!contentWidthEl) {
387 throw new Error('needs fc-scrollgrid-shrink-cushion className');
388 }
389 return cellEl.getBoundingClientRect().width - allWidthEl.getBoundingClientRect().width + // the cell padding+border
390 contentWidthEl.getBoundingClientRect().width;
391 }
392
393 const INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
394 const PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
395 // Parsing and Creation
396 function createDuration(input, unit) {
397 if (typeof input === 'string') {
398 return parseString(input);
399 }
400 if (typeof input === 'object' && input) { // non-null object
401 return parseObject(input);
402 }
403 if (typeof input === 'number') {
404 return parseObject({ [unit || 'milliseconds']: input });
405 }
406 return null;
407 }
408 function parseString(s) {
409 let m = PARSE_RE.exec(s);
410 if (m) {
411 let sign = m[1] ? -1 : 1;
412 return {
413 years: 0,
414 months: 0,
415 days: sign * (m[2] ? parseInt(m[2], 10) : 0),
416 milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours
417 (m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes
418 (m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds
419 (m[6] ? parseInt(m[6], 10) : 0) // ms
420 ),
421 };
422 }
423 return null;
424 }
425 function parseObject(obj) {
426 let duration = {
427 years: obj.years || obj.year || 0,
428 months: obj.months || obj.month || 0,
429 days: obj.days || obj.day || 0,
430 milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours
431 (obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes
432 (obj.seconds || obj.second || 0) * 1000 + // seconds
433 (obj.milliseconds || obj.millisecond || obj.ms || 0), // ms
434 };
435 let weeks = obj.weeks || obj.week;
436 if (weeks) {
437 duration.days += weeks * 7;
438 duration.specifiedWeeks = true;
439 }
440 return duration;
441 }
442 // Equality
443 function durationsEqual(d0, d1) {
444 return d0.years === d1.years &&
445 d0.months === d1.months &&
446 d0.days === d1.days &&
447 d0.milliseconds === d1.milliseconds;
448 }
449 function asCleanDays(dur) {
450 if (!dur.years && !dur.months && !dur.milliseconds) {
451 return dur.days;
452 }
453 return 0;
454 }
455 // Simple Math
456 function addDurations(d0, d1) {
457 return {
458 years: d0.years + d1.years,
459 months: d0.months + d1.months,
460 days: d0.days + d1.days,
461 milliseconds: d0.milliseconds + d1.milliseconds,
462 };
463 }
464 function subtractDurations(d1, d0) {
465 return {
466 years: d1.years - d0.years,
467 months: d1.months - d0.months,
468 days: d1.days - d0.days,
469 milliseconds: d1.milliseconds - d0.milliseconds,
470 };
471 }
472 function multiplyDuration(d, n) {
473 return {
474 years: d.years * n,
475 months: d.months * n,
476 days: d.days * n,
477 milliseconds: d.milliseconds * n,
478 };
479 }
480 // Conversions
481 // "Rough" because they are based on average-case Gregorian months/years
482 function asRoughYears(dur) {
483 return asRoughDays(dur) / 365;
484 }
485 function asRoughMonths(dur) {
486 return asRoughDays(dur) / 30;
487 }
488 function asRoughDays(dur) {
489 return asRoughMs(dur) / 864e5;
490 }
491 function asRoughMinutes(dur) {
492 return asRoughMs(dur) / (1000 * 60);
493 }
494 function asRoughSeconds(dur) {
495 return asRoughMs(dur) / 1000;
496 }
497 function asRoughMs(dur) {
498 return dur.years * (365 * 864e5) +
499 dur.months * (30 * 864e5) +
500 dur.days * 864e5 +
501 dur.milliseconds;
502 }
503 // Advanced Math
504 function wholeDivideDurations(numerator, denominator) {
505 let res = null;
506 for (let i = 0; i < INTERNAL_UNITS.length; i += 1) {
507 let unit = INTERNAL_UNITS[i];
508 if (denominator[unit]) {
509 let localRes = numerator[unit] / denominator[unit];
510 if (!isInt(localRes) || (res !== null && res !== localRes)) {
511 return null;
512 }
513 res = localRes;
514 }
515 else if (numerator[unit]) {
516 // needs to divide by something but can't!
517 return null;
518 }
519 }
520 return res;
521 }
522 function greatestDurationDenominator(dur) {
523 let ms = dur.milliseconds;
524 if (ms) {
525 if (ms % 1000 !== 0) {
526 return { unit: 'millisecond', value: ms };
527 }
528 if (ms % (1000 * 60) !== 0) {
529 return { unit: 'second', value: ms / 1000 };
530 }
531 if (ms % (1000 * 60 * 60) !== 0) {
532 return { unit: 'minute', value: ms / (1000 * 60) };
533 }
534 if (ms) {
535 return { unit: 'hour', value: ms / (1000 * 60 * 60) };
536 }
537 }
538 if (dur.days) {
539 if (dur.specifiedWeeks && dur.days % 7 === 0) {
540 return { unit: 'week', value: dur.days / 7 };
541 }
542 return { unit: 'day', value: dur.days };
543 }
544 if (dur.months) {
545 return { unit: 'month', value: dur.months };
546 }
547 if (dur.years) {
548 return { unit: 'year', value: dur.years };
549 }
550 return { unit: 'millisecond', value: 0 };
551 }
552
553 const { hasOwnProperty } = Object.prototype;
554 // Merges an array of objects into a single object.
555 // The second argument allows for an array of property names who's object values will be merged together.
556 function mergeProps(propObjs, complexPropsMap) {
557 let dest = {};
558 if (complexPropsMap) {
559 for (let name in complexPropsMap) {
560 if (complexPropsMap[name] === isMaybeObjectsEqual) { // implies that it's object-mergeable
561 let complexObjs = [];
562 // collect the trailing object values, stopping when a non-object is discovered
563 for (let i = propObjs.length - 1; i >= 0; i -= 1) {
564 let val = propObjs[i][name];
565 if (typeof val === 'object' && val) { // non-null object
566 complexObjs.unshift(val);
567 }
568 else if (val !== undefined) {
569 dest[name] = val; // if there were no objects, this value will be used
570 break;
571 }
572 }
573 // if the trailing values were objects, use the merged value
574 if (complexObjs.length) {
575 dest[name] = mergeProps(complexObjs);
576 }
577 }
578 }
579 }
580 // copy values into the destination, going from last to first
581 for (let i = propObjs.length - 1; i >= 0; i -= 1) {
582 let props = propObjs[i];
583 for (let name in props) {
584 if (!(name in dest)) { // if already assigned by previous props or complex props, don't reassign
585 dest[name] = props[name];
586 }
587 }
588 }
589 return dest;
590 }
591 function filterHash(hash, func) {
592 let filtered = {};
593 for (let key in hash) {
594 if (func(hash[key], key)) {
595 filtered[key] = hash[key];
596 }
597 }
598 return filtered;
599 }
600 function mapHash(hash, func) {
601 let newHash = {};
602 for (let key in hash) {
603 newHash[key] = func(hash[key], key);
604 }
605 return newHash;
606 }
607 function arrayToHash(a) {
608 let hash = {};
609 for (let item of a) {
610 hash[item] = true;
611 }
612 return hash;
613 }
614 // TODO: reassess browser support
615 // https://caniuse.com/?search=object.values
616 function hashValuesToArray(obj) {
617 let a = [];
618 for (let key in obj) {
619 a.push(obj[key]);
620 }
621 return a;
622 }
623 function isPropsEqual(obj0, obj1) {
624 if (obj0 === obj1) {
625 return true;
626 }
627 for (let key in obj0) {
628 if (hasOwnProperty.call(obj0, key)) {
629 if (!(key in obj1)) {
630 return false;
631 }
632 }
633 }
634 for (let key in obj1) {
635 if (hasOwnProperty.call(obj1, key)) {
636 if (obj0[key] !== obj1[key]) {
637 return false;
638 }
639 }
640 }
641 return true;
642 }
643 const HANDLER_RE = /^on[A-Z]/;
644 function isNonHandlerPropsEqual(obj0, obj1) {
645 const keys = getUnequalProps(obj0, obj1);
646 for (let key of keys) {
647 if (!HANDLER_RE.test(key)) {
648 return false;
649 }
650 }
651 return true;
652 }
653 function getUnequalProps(obj0, obj1) {
654 let keys = [];
655 for (let key in obj0) {
656 if (hasOwnProperty.call(obj0, key)) {
657 if (!(key in obj1)) {
658 keys.push(key);
659 }
660 }
661 }
662 for (let key in obj1) {
663 if (hasOwnProperty.call(obj1, key)) {
664 if (obj0[key] !== obj1[key]) {
665 keys.push(key);
666 }
667 }
668 }
669 return keys;
670 }
671 function compareObjs(oldProps, newProps, equalityFuncs = {}) {
672 if (oldProps === newProps) {
673 return true;
674 }
675 for (let key in newProps) {
676 if (key in oldProps && isObjValsEqual(oldProps[key], newProps[key], equalityFuncs[key])) ;
677 else {
678 return false;
679 }
680 }
681 // check for props that were omitted in the new
682 for (let key in oldProps) {
683 if (!(key in newProps)) {
684 return false;
685 }
686 }
687 return true;
688 }
689 /*
690 assumed "true" equality for handler names like "onReceiveSomething"
691 */
692 function isObjValsEqual(val0, val1, comparator) {
693 if (val0 === val1 || comparator === true) {
694 return true;
695 }
696 if (comparator) {
697 return comparator(val0, val1);
698 }
699 return false;
700 }
701 function collectFromHash(hash, startIndex = 0, endIndex, step = 1) {
702 let res = [];
703 if (endIndex == null) {
704 endIndex = Object.keys(hash).length;
705 }
706 for (let i = startIndex; i < endIndex; i += step) {
707 let val = hash[i];
708 if (val !== undefined) { // will disregard undefined for sparse arrays
709 res.push(val);
710 }
711 }
712 return res;
713 }
714
715 // TODO: new util arrayify?
716 function removeExact(array, exactVal) {
717 let removeCnt = 0;
718 let i = 0;
719 while (i < array.length) {
720 if (array[i] === exactVal) {
721 array.splice(i, 1);
722 removeCnt += 1;
723 }
724 else {
725 i += 1;
726 }
727 }
728 return removeCnt;
729 }
730 function isArraysEqual(a0, a1, equalityFunc) {
731 if (a0 === a1) {
732 return true;
733 }
734 let len = a0.length;
735 let i;
736 if (len !== a1.length) { // not array? or not same length?
737 return false;
738 }
739 for (i = 0; i < len; i += 1) {
740 if (!(equalityFunc ? equalityFunc(a0[i], a1[i]) : a0[i] === a1[i])) {
741 return false;
742 }
743 }
744 return true;
745 }
746
747 const DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
748 // Adding
749 function addWeeks(m, n) {
750 let a = dateToUtcArray(m);
751 a[2] += n * 7;
752 return arrayToUtcDate(a);
753 }
754 function addDays(m, n) {
755 let a = dateToUtcArray(m);
756 a[2] += n;
757 return arrayToUtcDate(a);
758 }
759 function addMs(m, n) {
760 let a = dateToUtcArray(m);
761 a[6] += n;
762 return arrayToUtcDate(a);
763 }
764 // Diffing (all return floats)
765 // TODO: why not use ranges?
766 function diffWeeks(m0, m1) {
767 return diffDays(m0, m1) / 7;
768 }
769 function diffDays(m0, m1) {
770 return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24);
771 }
772 function diffHours(m0, m1) {
773 return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60);
774 }
775 function diffMinutes(m0, m1) {
776 return (m1.valueOf() - m0.valueOf()) / (1000 * 60);
777 }
778 function diffSeconds(m0, m1) {
779 return (m1.valueOf() - m0.valueOf()) / 1000;
780 }
781 function diffDayAndTime(m0, m1) {
782 let m0day = startOfDay(m0);
783 let m1day = startOfDay(m1);
784 return {
785 years: 0,
786 months: 0,
787 days: Math.round(diffDays(m0day, m1day)),
788 milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf()),
789 };
790 }
791 // Diffing Whole Units
792 function diffWholeWeeks(m0, m1) {
793 let d = diffWholeDays(m0, m1);
794 if (d !== null && d % 7 === 0) {
795 return d / 7;
796 }
797 return null;
798 }
799 function diffWholeDays(m0, m1) {
800 if (timeAsMs(m0) === timeAsMs(m1)) {
801 return Math.round(diffDays(m0, m1));
802 }
803 return null;
804 }
805 // Start-Of
806 function startOfDay(m) {
807 return arrayToUtcDate([
808 m.getUTCFullYear(),
809 m.getUTCMonth(),
810 m.getUTCDate(),
811 ]);
812 }
813 function startOfHour(m) {
814 return arrayToUtcDate([
815 m.getUTCFullYear(),
816 m.getUTCMonth(),
817 m.getUTCDate(),
818 m.getUTCHours(),
819 ]);
820 }
821 function startOfMinute(m) {
822 return arrayToUtcDate([
823 m.getUTCFullYear(),
824 m.getUTCMonth(),
825 m.getUTCDate(),
826 m.getUTCHours(),
827 m.getUTCMinutes(),
828 ]);
829 }
830 function startOfSecond(m) {
831 return arrayToUtcDate([
832 m.getUTCFullYear(),
833 m.getUTCMonth(),
834 m.getUTCDate(),
835 m.getUTCHours(),
836 m.getUTCMinutes(),
837 m.getUTCSeconds(),
838 ]);
839 }
840 // Week Computation
841 function weekOfYear(marker, dow, doy) {
842 let y = marker.getUTCFullYear();
843 let w = weekOfGivenYear(marker, y, dow, doy);
844 if (w < 1) {
845 return weekOfGivenYear(marker, y - 1, dow, doy);
846 }
847 let nextW = weekOfGivenYear(marker, y + 1, dow, doy);
848 if (nextW >= 1) {
849 return Math.min(w, nextW);
850 }
851 return w;
852 }
853 function weekOfGivenYear(marker, year, dow, doy) {
854 let firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]);
855 let dayStart = startOfDay(marker);
856 let days = Math.round(diffDays(firstWeekStart, dayStart));
857 return Math.floor(days / 7) + 1; // zero-indexed
858 }
859 // start-of-first-week - start-of-year
860 function firstWeekOffset(year, dow, doy) {
861 // first-week day -- which january is always in the first week (4 for iso, 1 for other)
862 let fwd = 7 + dow - doy;
863 // first-week day local weekday -- which local weekday is fwd
864 let fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7;
865 return -fwdlw + fwd - 1;
866 }
867 // Array Conversion
868 function dateToLocalArray(date) {
869 return [
870 date.getFullYear(),
871 date.getMonth(),
872 date.getDate(),
873 date.getHours(),
874 date.getMinutes(),
875 date.getSeconds(),
876 date.getMilliseconds(),
877 ];
878 }
879 function arrayToLocalDate(a) {
880 return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month
881 a[3] || 0, a[4] || 0, a[5] || 0);
882 }
883 function dateToUtcArray(date) {
884 return [
885 date.getUTCFullYear(),
886 date.getUTCMonth(),
887 date.getUTCDate(),
888 date.getUTCHours(),
889 date.getUTCMinutes(),
890 date.getUTCSeconds(),
891 date.getUTCMilliseconds(),
892 ];
893 }
894 function arrayToUtcDate(a) {
895 // according to web standards (and Safari), a month index is required.
896 // massage if only given a year.
897 if (a.length === 1) {
898 a = a.concat([0]);
899 }
900 return new Date(Date.UTC(...a));
901 }
902 // Other Utils
903 function isValidDate(m) {
904 return !isNaN(m.valueOf());
905 }
906 function timeAsMs(m) {
907 return m.getUTCHours() * 1000 * 60 * 60 +
908 m.getUTCMinutes() * 1000 * 60 +
909 m.getUTCSeconds() * 1000 +
910 m.getUTCMilliseconds();
911 }
912
913 // timeZoneOffset is in minutes
914 function buildIsoString(marker, timeZoneOffset, stripZeroTime = false) {
915 let s = marker.toISOString();
916 s = s.replace('.000', '');
917 if (stripZeroTime) {
918 s = s.replace('T00:00:00Z', '');
919 }
920 if (s.length > 10) { // time part wasn't stripped, can add timezone info
921 if (timeZoneOffset == null) {
922 s = s.replace('Z', '');
923 }
924 else if (timeZoneOffset !== 0) {
925 s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true));
926 }
927 // otherwise, its UTC-0 and we want to keep the Z
928 }
929 return s;
930 }
931 // formats the date, but with no time part
932 // TODO: somehow merge with buildIsoString and stripZeroTime
933 // TODO: rename. omit "string"
934 function formatDayString(marker) {
935 return marker.toISOString().replace(/T.*$/, '');
936 }
937 function formatIsoMonthStr(marker) {
938 return marker.toISOString().match(/^\d{4}-\d{2}/)[0];
939 }
940 // TODO: use Date::toISOString and use everything after the T?
941 function formatIsoTimeString(marker) {
942 return padStart(marker.getUTCHours(), 2) + ':' +
943 padStart(marker.getUTCMinutes(), 2) + ':' +
944 padStart(marker.getUTCSeconds(), 2);
945 }
946 function formatTimeZoneOffset(minutes, doIso = false) {
947 let sign = minutes < 0 ? '-' : '+';
948 let abs = Math.abs(minutes);
949 let hours = Math.floor(abs / 60);
950 let mins = Math.round(abs % 60);
951 if (doIso) {
952 return `${sign + padStart(hours, 2)}:${padStart(mins, 2)}`;
953 }
954 return `GMT${sign}${hours}${mins ? `:${padStart(mins, 2)}` : ''}`;
955 }
956
957 function memoize(workerFunc, resEquality, teardownFunc) {
958 let currentArgs;
959 let currentRes;
960 return function (...newArgs) {
961 if (!currentArgs) {
962 currentRes = workerFunc.apply(this, newArgs);
963 }
964 else if (!isArraysEqual(currentArgs, newArgs)) {
965 if (teardownFunc) {
966 teardownFunc(currentRes);
967 }
968 let res = workerFunc.apply(this, newArgs);
969 if (!resEquality || !resEquality(res, currentRes)) {
970 currentRes = res;
971 }
972 }
973 currentArgs = newArgs;
974 return currentRes;
975 };
976 }
977 function memoizeObjArg(workerFunc, resEquality, teardownFunc) {
978 let currentArg;
979 let currentRes;
980 return (newArg) => {
981 if (!currentArg) {
982 currentRes = workerFunc.call(this, newArg);
983 }
984 else if (!isPropsEqual(currentArg, newArg)) {
985 if (teardownFunc) {
986 teardownFunc(currentRes);
987 }
988 let res = workerFunc.call(this, newArg);
989 if (!resEquality || !resEquality(res, currentRes)) {
990 currentRes = res;
991 }
992 }
993 currentArg = newArg;
994 return currentRes;
995 };
996 }
997 function memoizeArraylike(// used at all?
998 workerFunc, resEquality, teardownFunc) {
999 let currentArgSets = [];
1000 let currentResults = [];
1001 return (newArgSets) => {
1002 let currentLen = currentArgSets.length;
1003 let newLen = newArgSets.length;
1004 let i = 0;
1005 for (; i < currentLen; i += 1) {
1006 if (!newArgSets[i]) { // one of the old sets no longer exists
1007 if (teardownFunc) {
1008 teardownFunc(currentResults[i]);
1009 }
1010 }
1011 else if (!isArraysEqual(currentArgSets[i], newArgSets[i])) {
1012 if (teardownFunc) {
1013 teardownFunc(currentResults[i]);
1014 }
1015 let res = workerFunc.apply(this, newArgSets[i]);
1016 if (!resEquality || !resEquality(res, currentResults[i])) {
1017 currentResults[i] = res;
1018 }
1019 }
1020 }
1021 for (; i < newLen; i += 1) {
1022 currentResults[i] = workerFunc.apply(this, newArgSets[i]);
1023 }
1024 currentArgSets = newArgSets;
1025 currentResults.splice(newLen); // remove excess
1026 return currentResults;
1027 };
1028 }
1029 function memoizeHashlike(workerFunc, resEquality, teardownFunc) {
1030 let currentArgHash = {};
1031 let currentResHash = {};
1032 return (newArgHash) => {
1033 let newResHash = {};
1034 for (let key in newArgHash) {
1035 if (!currentResHash[key]) {
1036 newResHash[key] = workerFunc.apply(this, newArgHash[key]);
1037 }
1038 else if (!isArraysEqual(currentArgHash[key], newArgHash[key])) {
1039 if (teardownFunc) {
1040 teardownFunc(currentResHash[key]);
1041 }
1042 let res = workerFunc.apply(this, newArgHash[key]);
1043 newResHash[key] = (resEquality && resEquality(res, currentResHash[key]))
1044 ? currentResHash[key]
1045 : res;
1046 }
1047 else {
1048 newResHash[key] = currentResHash[key];
1049 }
1050 }
1051 currentArgHash = newArgHash;
1052 currentResHash = newResHash;
1053 return newResHash;
1054 };
1055 }
1056
1057 const EXTENDED_SETTINGS_AND_SEVERITIES = {
1058 week: 3,
1059 separator: 0,
1060 omitZeroMinute: 0,
1061 meridiem: 0,
1062 omitCommas: 0,
1063 };
1064 const STANDARD_DATE_PROP_SEVERITIES = {
1065 timeZoneName: 7,
1066 era: 6,
1067 year: 5,
1068 month: 4,
1069 day: 2,
1070 weekday: 2,
1071 hour: 1,
1072 minute: 1,
1073 second: 1,
1074 };
1075 const MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too
1076 const COMMA_RE = /,/g; // we need re for globalness
1077 const MULTI_SPACE_RE = /\s+/g;
1078 const LTR_RE = /\u200e/g; // control character
1079 const UTC_RE = /UTC|GMT/;
1080 class NativeFormatter {
1081 constructor(formatSettings) {
1082 let standardDateProps = {};
1083 let extendedSettings = {};
1084 let severity = 0;
1085 for (let name in formatSettings) {
1086 if (name in EXTENDED_SETTINGS_AND_SEVERITIES) {
1087 extendedSettings[name] = formatSettings[name];
1088 severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name], severity);
1089 }
1090 else {
1091 standardDateProps[name] = formatSettings[name];
1092 if (name in STANDARD_DATE_PROP_SEVERITIES) { // TODO: what about hour12? no severity
1093 severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name], severity);
1094 }
1095 }
1096 }
1097 this.standardDateProps = standardDateProps;
1098 this.extendedSettings = extendedSettings;
1099 this.severity = severity;
1100 this.buildFormattingFunc = memoize(buildFormattingFunc);
1101 }
1102 format(date, context) {
1103 return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date);
1104 }
1105 formatRange(start, end, context, betterDefaultSeparator) {
1106 let { standardDateProps, extendedSettings } = this;
1107 let diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem);
1108 if (!diffSeverity) {
1109 return this.format(start, context);
1110 }
1111 let biggestUnitForPartial = diffSeverity;
1112 if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time
1113 (standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') &&
1114 (standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') &&
1115 (standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) {
1116 biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time
1117 }
1118 let full0 = this.format(start, context);
1119 let full1 = this.format(end, context);
1120 if (full0 === full1) {
1121 return full0;
1122 }
1123 let partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial);
1124 let partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context);
1125 let partial0 = partialFormattingFunc(start);
1126 let partial1 = partialFormattingFunc(end);
1127 let insertion = findCommonInsertion(full0, partial0, full1, partial1);
1128 let separator = extendedSettings.separator || betterDefaultSeparator || context.defaultSeparator || '';
1129 if (insertion) {
1130 return insertion.before + partial0 + separator + partial1 + insertion.after;
1131 }
1132 return full0 + separator + full1;
1133 }
1134 getLargestUnit() {
1135 switch (this.severity) {
1136 case 7:
1137 case 6:
1138 case 5:
1139 return 'year';
1140 case 4:
1141 return 'month';
1142 case 3:
1143 return 'week';
1144 case 2:
1145 return 'day';
1146 default:
1147 return 'time'; // really?
1148 }
1149 }
1150 }
1151 function buildFormattingFunc(standardDateProps, extendedSettings, context) {
1152 let standardDatePropCnt = Object.keys(standardDateProps).length;
1153 if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') {
1154 return (date) => (formatTimeZoneOffset(date.timeZoneOffset));
1155 }
1156 if (standardDatePropCnt === 0 && extendedSettings.week) {
1157 return (date) => (formatWeekNumber(context.computeWeekNumber(date.marker), context.weekText, context.weekTextLong, context.locale, extendedSettings.week));
1158 }
1159 return buildNativeFormattingFunc(standardDateProps, extendedSettings, context);
1160 }
1161 function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) {
1162 standardDateProps = Object.assign({}, standardDateProps); // copy
1163 extendedSettings = Object.assign({}, extendedSettings); // copy
1164 sanitizeSettings(standardDateProps, extendedSettings);
1165 standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers
1166 let normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps);
1167 let zeroFormat; // needed?
1168 if (extendedSettings.omitZeroMinute) {
1169 let zeroProps = Object.assign({}, standardDateProps);
1170 delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings
1171 zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps);
1172 }
1173 return (date) => {
1174 let { marker } = date;
1175 let format;
1176 if (zeroFormat && !marker.getUTCMinutes()) {
1177 format = zeroFormat;
1178 }
1179 else {
1180 format = normalFormat;
1181 }
1182 let s = format.format(marker);
1183 return postProcess(s, date, standardDateProps, extendedSettings, context);
1184 };
1185 }
1186 function sanitizeSettings(standardDateProps, extendedSettings) {
1187 // deal with a browser inconsistency where formatting the timezone
1188 // requires that the hour/minute be present.
1189 if (standardDateProps.timeZoneName) {
1190 if (!standardDateProps.hour) {
1191 standardDateProps.hour = '2-digit';
1192 }
1193 if (!standardDateProps.minute) {
1194 standardDateProps.minute = '2-digit';
1195 }
1196 }
1197 // only support short timezone names
1198 if (standardDateProps.timeZoneName === 'long') {
1199 standardDateProps.timeZoneName = 'short';
1200 }
1201 // if requesting to display seconds, MUST display minutes
1202 if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) {
1203 delete extendedSettings.omitZeroMinute;
1204 }
1205 }
1206 function postProcess(s, date, standardDateProps, extendedSettings, context) {
1207 s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes
1208 if (standardDateProps.timeZoneName === 'short') {
1209 s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ?
1210 'UTC' : // important to normalize for IE, which does "GMT"
1211 formatTimeZoneOffset(date.timeZoneOffset));
1212 }
1213 if (extendedSettings.omitCommas) {
1214 s = s.replace(COMMA_RE, '').trim();
1215 }
1216 if (extendedSettings.omitZeroMinute) {
1217 s = s.replace(':00', ''); // zeroFormat doesn't always achieve this
1218 }
1219 // ^ do anything that might create adjacent spaces before this point,
1220 // because MERIDIEM_RE likes to eat up loading spaces
1221 if (extendedSettings.meridiem === false) {
1222 s = s.replace(MERIDIEM_RE, '').trim();
1223 }
1224 else if (extendedSettings.meridiem === 'narrow') { // a/p
1225 s = s.replace(MERIDIEM_RE, (m0, m1) => m1.toLocaleLowerCase());
1226 }
1227 else if (extendedSettings.meridiem === 'short') { // am/pm
1228 s = s.replace(MERIDIEM_RE, (m0, m1) => `${m1.toLocaleLowerCase()}m`);
1229 }
1230 else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase
1231 s = s.replace(MERIDIEM_RE, (m0) => m0.toLocaleLowerCase());
1232 }
1233 s = s.replace(MULTI_SPACE_RE, ' ');
1234 s = s.trim();
1235 return s;
1236 }
1237 function injectTzoStr(s, tzoStr) {
1238 let replaced = false;
1239 s = s.replace(UTC_RE, () => {
1240 replaced = true;
1241 return tzoStr;
1242 });
1243 // IE11 doesn't include UTC/GMT in the original string, so append to end
1244 if (!replaced) {
1245 s += ` ${tzoStr}`;
1246 }
1247 return s;
1248 }
1249 function formatWeekNumber(num, weekText, weekTextLong, locale, display) {
1250 let parts = [];
1251 if (display === 'long') {
1252 parts.push(weekTextLong);
1253 }
1254 else if (display === 'short' || display === 'narrow') {
1255 parts.push(weekText);
1256 }
1257 if (display === 'long' || display === 'short') {
1258 parts.push(' ');
1259 }
1260 parts.push(locale.simpleNumberFormat.format(num));
1261 if (locale.options.direction === 'rtl') { // TODO: use control characters instead?
1262 parts.reverse();
1263 }
1264 return parts.join('');
1265 }
1266 // Range Formatting Utils
1267 // 0 = exactly the same
1268 // 1 = different by time
1269 // and bigger
1270 function computeMarkerDiffSeverity(d0, d1, ca) {
1271 if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) {
1272 return 5;
1273 }
1274 if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) {
1275 return 4;
1276 }
1277 if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) {
1278 return 2;
1279 }
1280 if (timeAsMs(d0) !== timeAsMs(d1)) {
1281 return 1;
1282 }
1283 return 0;
1284 }
1285 function computePartialFormattingOptions(options, biggestUnit) {
1286 let partialOptions = {};
1287 for (let name in options) {
1288 if (!(name in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone)
1289 STANDARD_DATE_PROP_SEVERITIES[name] <= biggestUnit) {
1290 partialOptions[name] = options[name];
1291 }
1292 }
1293 return partialOptions;
1294 }
1295 function findCommonInsertion(full0, partial0, full1, partial1) {
1296 let i0 = 0;
1297 while (i0 < full0.length) {
1298 let found0 = full0.indexOf(partial0, i0);
1299 if (found0 === -1) {
1300 break;
1301 }
1302 let before0 = full0.substr(0, found0);
1303 i0 = found0 + partial0.length;
1304 let after0 = full0.substr(i0);
1305 let i1 = 0;
1306 while (i1 < full1.length) {
1307 let found1 = full1.indexOf(partial1, i1);
1308 if (found1 === -1) {
1309 break;
1310 }
1311 let before1 = full1.substr(0, found1);
1312 i1 = found1 + partial1.length;
1313 let after1 = full1.substr(i1);
1314 if (before0 === before1 && after0 === after1) {
1315 return {
1316 before: before0,
1317 after: after0,
1318 };
1319 }
1320 }
1321 }
1322 return null;
1323 }
1324
1325 function expandZonedMarker(dateInfo, calendarSystem) {
1326 let a = calendarSystem.markerToArray(dateInfo.marker);
1327 return {
1328 marker: dateInfo.marker,
1329 timeZoneOffset: dateInfo.timeZoneOffset,
1330 array: a,
1331 year: a[0],
1332 month: a[1],
1333 day: a[2],
1334 hour: a[3],
1335 minute: a[4],
1336 second: a[5],
1337 millisecond: a[6],
1338 };
1339 }
1340
1341 function createVerboseFormattingArg(start, end, context, betterDefaultSeparator) {
1342 let startInfo = expandZonedMarker(start, context.calendarSystem);
1343 let endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null;
1344 return {
1345 date: startInfo,
1346 start: startInfo,
1347 end: endInfo,
1348 timeZone: context.timeZone,
1349 localeCodes: context.locale.codes,
1350 defaultSeparator: betterDefaultSeparator || context.defaultSeparator,
1351 };
1352 }
1353
1354 /*
1355 TODO: fix the terminology of "formatter" vs "formatting func"
1356 */
1357 /*
1358 At the time of instantiation, this object does not know which cmd-formatting system it will use.
1359 It receives this at the time of formatting, as a setting.
1360 */
1361 class CmdFormatter {
1362 constructor(cmdStr) {
1363 this.cmdStr = cmdStr;
1364 }
1365 format(date, context, betterDefaultSeparator) {
1366 return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, betterDefaultSeparator));
1367 }
1368 formatRange(start, end, context, betterDefaultSeparator) {
1369 return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, betterDefaultSeparator));
1370 }
1371 }
1372
1373 class FuncFormatter {
1374 constructor(func) {
1375 this.func = func;
1376 }
1377 format(date, context, betterDefaultSeparator) {
1378 return this.func(createVerboseFormattingArg(date, null, context, betterDefaultSeparator));
1379 }
1380 formatRange(start, end, context, betterDefaultSeparator) {
1381 return this.func(createVerboseFormattingArg(start, end, context, betterDefaultSeparator));
1382 }
1383 }
1384
1385 function createFormatter(input) {
1386 if (typeof input === 'object' && input) { // non-null object
1387 return new NativeFormatter(input);
1388 }
1389 if (typeof input === 'string') {
1390 return new CmdFormatter(input);
1391 }
1392 if (typeof input === 'function') {
1393 return new FuncFormatter(input);
1394 }
1395 return null;
1396 }
1397
1398 // base options
1399 // ------------
1400 const BASE_OPTION_REFINERS = {
1401 navLinkDayClick: identity,
1402 navLinkWeekClick: identity,
1403 duration: createDuration,
1404 bootstrapFontAwesome: identity,
1405 buttonIcons: identity,
1406 customButtons: identity,
1407 defaultAllDayEventDuration: createDuration,
1408 defaultTimedEventDuration: createDuration,
1409 nextDayThreshold: createDuration,
1410 scrollTime: createDuration,
1411 scrollTimeReset: Boolean,
1412 slotMinTime: createDuration,
1413 slotMaxTime: createDuration,
1414 dayPopoverFormat: createFormatter,
1415 slotDuration: createDuration,
1416 snapDuration: createDuration,
1417 headerToolbar: identity,
1418 footerToolbar: identity,
1419 defaultRangeSeparator: String,
1420 titleRangeSeparator: String,
1421 forceEventDuration: Boolean,
1422 dayHeaders: Boolean,
1423 dayHeaderFormat: createFormatter,
1424 dayHeaderClassNames: identity,
1425 dayHeaderContent: identity,
1426 dayHeaderDidMount: identity,
1427 dayHeaderWillUnmount: identity,
1428 dayCellClassNames: identity,
1429 dayCellContent: identity,
1430 dayCellDidMount: identity,
1431 dayCellWillUnmount: identity,
1432 initialView: String,
1433 aspectRatio: Number,
1434 weekends: Boolean,
1435 weekNumberCalculation: identity,
1436 weekNumbers: Boolean,
1437 weekNumberClassNames: identity,
1438 weekNumberContent: identity,
1439 weekNumberDidMount: identity,
1440 weekNumberWillUnmount: identity,
1441 editable: Boolean,
1442 viewClassNames: identity,
1443 viewDidMount: identity,
1444 viewWillUnmount: identity,
1445 nowIndicator: Boolean,
1446 nowIndicatorClassNames: identity,
1447 nowIndicatorContent: identity,
1448 nowIndicatorDidMount: identity,
1449 nowIndicatorWillUnmount: identity,
1450 showNonCurrentDates: Boolean,
1451 lazyFetching: Boolean,
1452 startParam: String,
1453 endParam: String,
1454 timeZoneParam: String,
1455 timeZone: String,
1456 locales: identity,
1457 locale: identity,
1458 themeSystem: String,
1459 dragRevertDuration: Number,
1460 dragScroll: Boolean,
1461 allDayMaintainDuration: Boolean,
1462 unselectAuto: Boolean,
1463 dropAccept: identity,
1464 eventOrder: parseFieldSpecs,
1465 eventOrderStrict: Boolean,
1466 handleWindowResize: Boolean,
1467 windowResizeDelay: Number,
1468 longPressDelay: Number,
1469 eventDragMinDistance: Number,
1470 expandRows: Boolean,
1471 height: identity,
1472 contentHeight: identity,
1473 direction: String,
1474 weekNumberFormat: createFormatter,
1475 eventResizableFromStart: Boolean,
1476 displayEventTime: Boolean,
1477 displayEventEnd: Boolean,
1478 weekText: String,
1479 weekTextLong: String,
1480 progressiveEventRendering: Boolean,
1481 businessHours: identity,
1482 initialDate: identity,
1483 now: identity,
1484 eventDataTransform: identity,
1485 stickyHeaderDates: identity,
1486 stickyFooterScrollbar: identity,
1487 viewHeight: identity,
1488 defaultAllDay: Boolean,
1489 eventSourceFailure: identity,
1490 eventSourceSuccess: identity,
1491 eventDisplay: String,
1492 eventStartEditable: Boolean,
1493 eventDurationEditable: Boolean,
1494 eventOverlap: identity,
1495 eventConstraint: identity,
1496 eventAllow: identity,
1497 eventBackgroundColor: String,
1498 eventBorderColor: String,
1499 eventTextColor: String,
1500 eventColor: String,
1501 eventClassNames: identity,
1502 eventContent: identity,
1503 eventDidMount: identity,
1504 eventWillUnmount: identity,
1505 selectConstraint: identity,
1506 selectOverlap: identity,
1507 selectAllow: identity,
1508 droppable: Boolean,
1509 unselectCancel: String,
1510 slotLabelFormat: identity,
1511 slotLaneClassNames: identity,
1512 slotLaneContent: identity,
1513 slotLaneDidMount: identity,
1514 slotLaneWillUnmount: identity,
1515 slotLabelClassNames: identity,
1516 slotLabelContent: identity,
1517 slotLabelDidMount: identity,
1518 slotLabelWillUnmount: identity,
1519 dayMaxEvents: identity,
1520 dayMaxEventRows: identity,
1521 dayMinWidth: Number,
1522 slotLabelInterval: createDuration,
1523 allDayText: String,
1524 allDayClassNames: identity,
1525 allDayContent: identity,
1526 allDayDidMount: identity,
1527 allDayWillUnmount: identity,
1528 slotMinWidth: Number,
1529 navLinks: Boolean,
1530 eventTimeFormat: createFormatter,
1531 rerenderDelay: Number,
1532 moreLinkText: identity,
1533 moreLinkHint: identity,
1534 selectMinDistance: Number,
1535 selectable: Boolean,
1536 selectLongPressDelay: Number,
1537 eventLongPressDelay: Number,
1538 selectMirror: Boolean,
1539 eventMaxStack: Number,
1540 eventMinHeight: Number,
1541 eventMinWidth: Number,
1542 eventShortHeight: Number,
1543 slotEventOverlap: Boolean,
1544 plugins: identity,
1545 firstDay: Number,
1546 dayCount: Number,
1547 dateAlignment: String,
1548 dateIncrement: createDuration,
1549 hiddenDays: identity,
1550 fixedWeekCount: Boolean,
1551 validRange: identity,
1552 visibleRange: identity,
1553 titleFormat: identity,
1554 eventInteractive: Boolean,
1555 // only used by list-view, but languages define the value, so we need it in base options
1556 noEventsText: String,
1557 viewHint: identity,
1558 navLinkHint: identity,
1559 closeHint: String,
1560 timeHint: String,
1561 eventHint: String,
1562 moreLinkClick: identity,
1563 moreLinkClassNames: identity,
1564 moreLinkContent: identity,
1565 moreLinkDidMount: identity,
1566 moreLinkWillUnmount: identity,
1567 monthStartFormat: createFormatter,
1568 // for connectors
1569 // (can't be part of plugin system b/c must be provided at runtime)
1570 handleCustomRendering: identity,
1571 customRenderingMetaMap: identity,
1572 customRenderingReplaces: Boolean,
1573 };
1574 // do NOT give a type here. need `typeof BASE_OPTION_DEFAULTS` to give real results.
1575 // raw values.
1576 const BASE_OPTION_DEFAULTS = {
1577 eventDisplay: 'auto',
1578 defaultRangeSeparator: ' - ',
1579 titleRangeSeparator: ' \u2013 ',
1580 defaultTimedEventDuration: '01:00:00',
1581 defaultAllDayEventDuration: { day: 1 },
1582 forceEventDuration: false,
1583 nextDayThreshold: '00:00:00',
1584 dayHeaders: true,
1585 initialView: '',
1586 aspectRatio: 1.35,
1587 headerToolbar: {
1588 start: 'title',
1589 center: '',
1590 end: 'today prev,next',
1591 },
1592 weekends: true,
1593 weekNumbers: false,
1594 weekNumberCalculation: 'local',
1595 editable: false,
1596 nowIndicator: false,
1597 scrollTime: '06:00:00',
1598 scrollTimeReset: true,
1599 slotMinTime: '00:00:00',
1600 slotMaxTime: '24:00:00',
1601 showNonCurrentDates: true,
1602 lazyFetching: true,
1603 startParam: 'start',
1604 endParam: 'end',
1605 timeZoneParam: 'timeZone',
1606 timeZone: 'local',
1607 locales: [],
1608 locale: '',
1609 themeSystem: 'standard',
1610 dragRevertDuration: 500,
1611 dragScroll: true,
1612 allDayMaintainDuration: false,
1613 unselectAuto: true,
1614 dropAccept: '*',
1615 eventOrder: 'start,-duration,allDay,title',
1616 dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' },
1617 handleWindowResize: true,
1618 windowResizeDelay: 100,
1619 longPressDelay: 1000,
1620 eventDragMinDistance: 5,
1621 expandRows: false,
1622 navLinks: false,
1623 selectable: false,
1624 eventMinHeight: 15,
1625 eventMinWidth: 30,
1626 eventShortHeight: 30,
1627 monthStartFormat: { month: 'long', day: 'numeric' },
1628 };
1629 // calendar listeners
1630 // ------------------
1631 const CALENDAR_LISTENER_REFINERS = {
1632 datesSet: identity,
1633 eventsSet: identity,
1634 eventAdd: identity,
1635 eventChange: identity,
1636 eventRemove: identity,
1637 windowResize: identity,
1638 eventClick: identity,
1639 eventMouseEnter: identity,
1640 eventMouseLeave: identity,
1641 select: identity,
1642 unselect: identity,
1643 loading: identity,
1644 // internal
1645 _unmount: identity,
1646 _beforeprint: identity,
1647 _afterprint: identity,
1648 _noEventDrop: identity,
1649 _noEventResize: identity,
1650 _resize: identity,
1651 _scrollRequest: identity,
1652 };
1653 // calendar-specific options
1654 // -------------------------
1655 const CALENDAR_OPTION_REFINERS = {
1656 buttonText: identity,
1657 buttonHints: identity,
1658 views: identity,
1659 plugins: identity,
1660 initialEvents: identity,
1661 events: identity,
1662 eventSources: identity,
1663 };
1664 const COMPLEX_OPTION_COMPARATORS = {
1665 headerToolbar: isMaybeObjectsEqual,
1666 footerToolbar: isMaybeObjectsEqual,
1667 buttonText: isMaybeObjectsEqual,
1668 buttonHints: isMaybeObjectsEqual,
1669 buttonIcons: isMaybeObjectsEqual,
1670 dateIncrement: isMaybeObjectsEqual,
1671 plugins: isMaybeArraysEqual,
1672 events: isMaybeArraysEqual,
1673 eventSources: isMaybeArraysEqual,
1674 ['resources']: isMaybeArraysEqual,
1675 };
1676 function isMaybeObjectsEqual(a, b) {
1677 if (typeof a === 'object' && typeof b === 'object' && a && b) { // both non-null objects
1678 return isPropsEqual(a, b);
1679 }
1680 return a === b;
1681 }
1682 function isMaybeArraysEqual(a, b) {
1683 if (Array.isArray(a) && Array.isArray(b)) {
1684 return isArraysEqual(a, b);
1685 }
1686 return a === b;
1687 }
1688 // view-specific options
1689 // ---------------------
1690 const VIEW_OPTION_REFINERS = {
1691 type: String,
1692 component: identity,
1693 buttonText: String,
1694 buttonTextKey: String,
1695 dateProfileGeneratorClass: identity,
1696 usesMinMaxTime: Boolean,
1697 classNames: identity,
1698 content: identity,
1699 didMount: identity,
1700 willUnmount: identity,
1701 };
1702 // util funcs
1703 // ----------------------------------------------------------------------------------------------------
1704 function mergeRawOptions(optionSets) {
1705 return mergeProps(optionSets, COMPLEX_OPTION_COMPARATORS);
1706 }
1707 function refineProps(input, refiners) {
1708 let refined = {};
1709 let extra = {};
1710 for (let propName in refiners) {
1711 if (propName in input) {
1712 refined[propName] = refiners[propName](input[propName]);
1713 }
1714 }
1715 for (let propName in input) {
1716 if (!(propName in refiners)) {
1717 extra[propName] = input[propName];
1718 }
1719 }
1720 return { refined, extra };
1721 }
1722 function identity(raw) {
1723 return raw;
1724 }
1725
1726 function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) {
1727 return {
1728 instanceId: guid(),
1729 defId,
1730 range,
1731 forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo,
1732 forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo,
1733 };
1734 }
1735
1736 function parseRecurring(refined, defaultAllDay, dateEnv, recurringTypes) {
1737 for (let i = 0; i < recurringTypes.length; i += 1) {
1738 let parsed = recurringTypes[i].parse(refined, dateEnv);
1739 if (parsed) {
1740 let { allDay } = refined;
1741 if (allDay == null) {
1742 allDay = defaultAllDay;
1743 if (allDay == null) {
1744 allDay = parsed.allDayGuess;
1745 if (allDay == null) {
1746 allDay = false;
1747 }
1748 }
1749 }
1750 return {
1751 allDay,
1752 duration: parsed.duration,
1753 typeData: parsed.typeData,
1754 typeId: i,
1755 };
1756 }
1757 }
1758 return null;
1759 }
1760 function expandRecurring(eventStore, framingRange, context) {
1761 let { dateEnv, pluginHooks, options } = context;
1762 let { defs, instances } = eventStore;
1763 // remove existing recurring instances
1764 // TODO: bad. always expand events as a second step
1765 instances = filterHash(instances, (instance) => !defs[instance.defId].recurringDef);
1766 for (let defId in defs) {
1767 let def = defs[defId];
1768 if (def.recurringDef) {
1769 let { duration } = def.recurringDef;
1770 if (!duration) {
1771 duration = def.allDay ?
1772 options.defaultAllDayEventDuration :
1773 options.defaultTimedEventDuration;
1774 }
1775 let starts = expandRecurringRanges(def, duration, framingRange, dateEnv, pluginHooks.recurringTypes);
1776 for (let start of starts) {
1777 let instance = createEventInstance(defId, {
1778 start,
1779 end: dateEnv.add(start, duration),
1780 });
1781 instances[instance.instanceId] = instance;
1782 }
1783 }
1784 }
1785 return { defs, instances };
1786 }
1787 /*
1788 Event MUST have a recurringDef
1789 */
1790 function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) {
1791 let typeDef = recurringTypes[eventDef.recurringDef.typeId];
1792 let markers = typeDef.expand(eventDef.recurringDef.typeData, {
1793 start: dateEnv.subtract(framingRange.start, duration),
1794 end: framingRange.end,
1795 }, dateEnv);
1796 // the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to
1797 if (eventDef.allDay) {
1798 markers = markers.map(startOfDay);
1799 }
1800 return markers;
1801 }
1802
1803 function parseEvents(rawEvents, eventSource, context, allowOpenRange, defIdMap, instanceIdMap) {
1804 let eventStore = createEmptyEventStore();
1805 let eventRefiners = buildEventRefiners(context);
1806 for (let rawEvent of rawEvents) {
1807 let tuple = parseEvent(rawEvent, eventSource, context, allowOpenRange, eventRefiners, defIdMap, instanceIdMap);
1808 if (tuple) {
1809 eventTupleToStore(tuple, eventStore);
1810 }
1811 }
1812 return eventStore;
1813 }
1814 function eventTupleToStore(tuple, eventStore = createEmptyEventStore()) {
1815 eventStore.defs[tuple.def.defId] = tuple.def;
1816 if (tuple.instance) {
1817 eventStore.instances[tuple.instance.instanceId] = tuple.instance;
1818 }
1819 return eventStore;
1820 }
1821 // retrieves events that have the same groupId as the instance specified by `instanceId`
1822 // or they are the same as the instance.
1823 // why might instanceId not be in the store? an event from another calendar?
1824 function getRelevantEvents(eventStore, instanceId) {
1825 let instance = eventStore.instances[instanceId];
1826 if (instance) {
1827 let def = eventStore.defs[instance.defId];
1828 // get events/instances with same group
1829 let newStore = filterEventStoreDefs(eventStore, (lookDef) => isEventDefsGrouped(def, lookDef));
1830 // add the original
1831 // TODO: wish we could use eventTupleToStore or something like it
1832 newStore.defs[def.defId] = def;
1833 newStore.instances[instance.instanceId] = instance;
1834 return newStore;
1835 }
1836 return createEmptyEventStore();
1837 }
1838 function isEventDefsGrouped(def0, def1) {
1839 return Boolean(def0.groupId && def0.groupId === def1.groupId);
1840 }
1841 function createEmptyEventStore() {
1842 return { defs: {}, instances: {} };
1843 }
1844 function mergeEventStores(store0, store1) {
1845 return {
1846 defs: Object.assign(Object.assign({}, store0.defs), store1.defs),
1847 instances: Object.assign(Object.assign({}, store0.instances), store1.instances),
1848 };
1849 }
1850 function filterEventStoreDefs(eventStore, filterFunc) {
1851 let defs = filterHash(eventStore.defs, filterFunc);
1852 let instances = filterHash(eventStore.instances, (instance) => (defs[instance.defId] // still exists?
1853 ));
1854 return { defs, instances };
1855 }
1856 function excludeSubEventStore(master, sub) {
1857 let { defs, instances } = master;
1858 let filteredDefs = {};
1859 let filteredInstances = {};
1860 for (let defId in defs) {
1861 if (!sub.defs[defId]) { // not explicitly excluded
1862 filteredDefs[defId] = defs[defId];
1863 }
1864 }
1865 for (let instanceId in instances) {
1866 if (!sub.instances[instanceId] && // not explicitly excluded
1867 filteredDefs[instances[instanceId].defId] // def wasn't filtered away
1868 ) {
1869 filteredInstances[instanceId] = instances[instanceId];
1870 }
1871 }
1872 return {
1873 defs: filteredDefs,
1874 instances: filteredInstances,
1875 };
1876 }
1877
1878 function normalizeConstraint(input, context) {
1879 if (Array.isArray(input)) {
1880 return parseEvents(input, null, context, true); // allowOpenRange=true
1881 }
1882 if (typeof input === 'object' && input) { // non-null object
1883 return parseEvents([input], null, context, true); // allowOpenRange=true
1884 }
1885 if (input != null) {
1886 return String(input);
1887 }
1888 return null;
1889 }
1890
1891 function parseClassNames(raw) {
1892 if (Array.isArray(raw)) {
1893 return raw;
1894 }
1895 if (typeof raw === 'string') {
1896 return raw.split(/\s+/);
1897 }
1898 return [];
1899 }
1900
1901 // TODO: better called "EventSettings" or "EventConfig"
1902 // TODO: move this file into structs
1903 // TODO: separate constraint/overlap/allow, because selection uses only that, not other props
1904 const EVENT_UI_REFINERS = {
1905 display: String,
1906 editable: Boolean,
1907 startEditable: Boolean,
1908 durationEditable: Boolean,
1909 constraint: identity,
1910 overlap: identity,
1911 allow: identity,
1912 className: parseClassNames,
1913 classNames: parseClassNames,
1914 color: String,
1915 backgroundColor: String,
1916 borderColor: String,
1917 textColor: String,
1918 };
1919 const EMPTY_EVENT_UI = {
1920 display: null,
1921 startEditable: null,
1922 durationEditable: null,
1923 constraints: [],
1924 overlap: null,
1925 allows: [],
1926 backgroundColor: '',
1927 borderColor: '',
1928 textColor: '',
1929 classNames: [],
1930 };
1931 function createEventUi(refined, context) {
1932 let constraint = normalizeConstraint(refined.constraint, context);
1933 return {
1934 display: refined.display || null,
1935 startEditable: refined.startEditable != null ? refined.startEditable : refined.editable,
1936 durationEditable: refined.durationEditable != null ? refined.durationEditable : refined.editable,
1937 constraints: constraint != null ? [constraint] : [],
1938 overlap: refined.overlap != null ? refined.overlap : null,
1939 allows: refined.allow != null ? [refined.allow] : [],
1940 backgroundColor: refined.backgroundColor || refined.color || '',
1941 borderColor: refined.borderColor || refined.color || '',
1942 textColor: refined.textColor || '',
1943 classNames: (refined.className || []).concat(refined.classNames || []), // join singular and plural
1944 };
1945 }
1946 // TODO: prevent against problems with <2 args!
1947 function combineEventUis(uis) {
1948 return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI);
1949 }
1950 function combineTwoEventUis(item0, item1) {
1951 return {
1952 display: item1.display != null ? item1.display : item0.display,
1953 startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable,
1954 durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable,
1955 constraints: item0.constraints.concat(item1.constraints),
1956 overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap,
1957 allows: item0.allows.concat(item1.allows),
1958 backgroundColor: item1.backgroundColor || item0.backgroundColor,
1959 borderColor: item1.borderColor || item0.borderColor,
1960 textColor: item1.textColor || item0.textColor,
1961 classNames: item0.classNames.concat(item1.classNames),
1962 };
1963 }
1964
1965 const EVENT_NON_DATE_REFINERS = {
1966 id: String,
1967 groupId: String,
1968 title: String,
1969 url: String,
1970 interactive: Boolean,
1971 };
1972 const EVENT_DATE_REFINERS = {
1973 start: identity,
1974 end: identity,
1975 date: identity,
1976 allDay: Boolean,
1977 };
1978 const EVENT_REFINERS = Object.assign(Object.assign(Object.assign({}, EVENT_NON_DATE_REFINERS), EVENT_DATE_REFINERS), { extendedProps: identity });
1979 function parseEvent(raw, eventSource, context, allowOpenRange, refiners = buildEventRefiners(context), defIdMap, instanceIdMap) {
1980 let { refined, extra } = refineEventDef(raw, context, refiners);
1981 let defaultAllDay = computeIsDefaultAllDay(eventSource, context);
1982 let recurringRes = parseRecurring(refined, defaultAllDay, context.dateEnv, context.pluginHooks.recurringTypes);
1983 if (recurringRes) {
1984 let def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', recurringRes.allDay, Boolean(recurringRes.duration), context, defIdMap);
1985 def.recurringDef = {
1986 typeId: recurringRes.typeId,
1987 typeData: recurringRes.typeData,
1988 duration: recurringRes.duration,
1989 };
1990 return { def, instance: null };
1991 }
1992 let singleRes = parseSingle(refined, defaultAllDay, context, allowOpenRange);
1993 if (singleRes) {
1994 let def = parseEventDef(refined, extra, eventSource ? eventSource.sourceId : '', singleRes.allDay, singleRes.hasEnd, context, defIdMap);
1995 let instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo);
1996 if (instanceIdMap && def.publicId && instanceIdMap[def.publicId]) {
1997 instance.instanceId = instanceIdMap[def.publicId];
1998 }
1999 return { def, instance };
2000 }
2001 return null;
2002 }
2003 function refineEventDef(raw, context, refiners = buildEventRefiners(context)) {
2004 return refineProps(raw, refiners);
2005 }
2006 function buildEventRefiners(context) {
2007 return Object.assign(Object.assign(Object.assign({}, EVENT_UI_REFINERS), EVENT_REFINERS), context.pluginHooks.eventRefiners);
2008 }
2009 /*
2010 Will NOT populate extendedProps with the leftover properties.
2011 Will NOT populate date-related props.
2012 */
2013 function parseEventDef(refined, extra, sourceId, allDay, hasEnd, context, defIdMap) {
2014 let def = {
2015 title: refined.title || '',
2016 groupId: refined.groupId || '',
2017 publicId: refined.id || '',
2018 url: refined.url || '',
2019 recurringDef: null,
2020 defId: ((defIdMap && refined.id) ? defIdMap[refined.id] : '') || guid(),
2021 sourceId,
2022 allDay,
2023 hasEnd,
2024 interactive: refined.interactive,
2025 ui: createEventUi(refined, context),
2026 extendedProps: Object.assign(Object.assign({}, (refined.extendedProps || {})), extra),
2027 };
2028 for (let memberAdder of context.pluginHooks.eventDefMemberAdders) {
2029 Object.assign(def, memberAdder(refined));
2030 }
2031 // help out EventImpl from having user modify props
2032 Object.freeze(def.ui.classNames);
2033 Object.freeze(def.extendedProps);
2034 return def;
2035 }
2036 function parseSingle(refined, defaultAllDay, context, allowOpenRange) {
2037 let { allDay } = refined;
2038 let startMeta;
2039 let startMarker = null;
2040 let hasEnd = false;
2041 let endMeta;
2042 let endMarker = null;
2043 let startInput = refined.start != null ? refined.start : refined.date;
2044 startMeta = context.dateEnv.createMarkerMeta(startInput);
2045 if (startMeta) {
2046 startMarker = startMeta.marker;
2047 }
2048 else if (!allowOpenRange) {
2049 return null;
2050 }
2051 if (refined.end != null) {
2052 endMeta = context.dateEnv.createMarkerMeta(refined.end);
2053 }
2054 if (allDay == null) {
2055 if (defaultAllDay != null) {
2056 allDay = defaultAllDay;
2057 }
2058 else {
2059 // fall back to the date props LAST
2060 allDay = (!startMeta || startMeta.isTimeUnspecified) &&
2061 (!endMeta || endMeta.isTimeUnspecified);
2062 }
2063 }
2064 if (allDay && startMarker) {
2065 startMarker = startOfDay(startMarker);
2066 }
2067 if (endMeta) {
2068 endMarker = endMeta.marker;
2069 if (allDay) {
2070 endMarker = startOfDay(endMarker);
2071 }
2072 if (startMarker && endMarker <= startMarker) {
2073 endMarker = null;
2074 }
2075 }
2076 if (endMarker) {
2077 hasEnd = true;
2078 }
2079 else if (!allowOpenRange) {
2080 hasEnd = context.options.forceEventDuration || false;
2081 endMarker = context.dateEnv.add(startMarker, allDay ?
2082 context.options.defaultAllDayEventDuration :
2083 context.options.defaultTimedEventDuration);
2084 }
2085 return {
2086 allDay,
2087 hasEnd,
2088 range: { start: startMarker, end: endMarker },
2089 forcedStartTzo: startMeta ? startMeta.forcedTzo : null,
2090 forcedEndTzo: endMeta ? endMeta.forcedTzo : null,
2091 };
2092 }
2093 function computeIsDefaultAllDay(eventSource, context) {
2094 let res = null;
2095 if (eventSource) {
2096 res = eventSource.defaultAllDay;
2097 }
2098 if (res == null) {
2099 res = context.options.defaultAllDay;
2100 }
2101 return res;
2102 }
2103
2104 const DEF_DEFAULTS = {
2105 startTime: '09:00',
2106 endTime: '17:00',
2107 daysOfWeek: [1, 2, 3, 4, 5],
2108 display: 'inverse-background',
2109 classNames: 'fc-non-business',
2110 groupId: '_businessHours', // so multiple defs get grouped
2111 };
2112 /*
2113 TODO: pass around as EventDefHash!!!
2114 */
2115 function parseBusinessHours(input, context) {
2116 return parseEvents(refineInputs(input), null, context);
2117 }
2118 function refineInputs(input) {
2119 let rawDefs;
2120 if (input === true) {
2121 rawDefs = [{}]; // will get DEF_DEFAULTS verbatim
2122 }
2123 else if (Array.isArray(input)) {
2124 // if specifying an array, every sub-definition NEEDS a day-of-week
2125 rawDefs = input.filter((rawDef) => rawDef.daysOfWeek);
2126 }
2127 else if (typeof input === 'object' && input) { // non-null object
2128 rawDefs = [input];
2129 }
2130 else { // is probably false
2131 rawDefs = [];
2132 }
2133 rawDefs = rawDefs.map((rawDef) => (Object.assign(Object.assign({}, DEF_DEFAULTS), rawDef)));
2134 return rawDefs;
2135 }
2136
2137 /* Date stuff that doesn't belong in datelib core
2138 ----------------------------------------------------------------------------------------------------------------------*/
2139 // given a timed range, computes an all-day range that has the same exact duration,
2140 // but whose start time is aligned with the start of the day.
2141 function computeAlignedDayRange(timedRange) {
2142 let dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1;
2143 let start = startOfDay(timedRange.start);
2144 let end = addDays(start, dayCnt);
2145 return { start, end };
2146 }
2147 // given a timed range, computes an all-day range based on how for the end date bleeds into the next day
2148 // TODO: give nextDayThreshold a default arg
2149 function computeVisibleDayRange(timedRange, nextDayThreshold = createDuration(0)) {
2150 let startDay = null;
2151 let endDay = null;
2152 if (timedRange.end) {
2153 endDay = startOfDay(timedRange.end);
2154 let endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay`
2155 // If the end time is actually inclusively part of the next day and is equal to or
2156 // beyond the next day threshold, adjust the end to be the exclusive end of `endDay`.
2157 // Otherwise, leaving it as inclusive will cause it to exclude `endDay`.
2158 if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) {
2159 endDay = addDays(endDay, 1);
2160 }
2161 }
2162 if (timedRange.start) {
2163 startDay = startOfDay(timedRange.start); // the beginning of the day the range starts
2164 // If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day.
2165 if (endDay && endDay <= startDay) {
2166 endDay = addDays(startDay, 1);
2167 }
2168 }
2169 return { start: startDay, end: endDay };
2170 }
2171 // spans from one day into another?
2172 function isMultiDayRange(range) {
2173 let visibleRange = computeVisibleDayRange(range);
2174 return diffDays(visibleRange.start, visibleRange.end) > 1;
2175 }
2176 function diffDates(date0, date1, dateEnv, largeUnit) {
2177 if (largeUnit === 'year') {
2178 return createDuration(dateEnv.diffWholeYears(date0, date1), 'year');
2179 }
2180 if (largeUnit === 'month') {
2181 return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month');
2182 }
2183 return diffDayAndTime(date0, date1); // returns a duration
2184 }
2185
2186 function pointInsideRect(point, rect) {
2187 return point.left >= rect.left &&
2188 point.left < rect.right &&
2189 point.top >= rect.top &&
2190 point.top < rect.bottom;
2191 }
2192 // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
2193 function intersectRects(rect1, rect2) {
2194 let res = {
2195 left: Math.max(rect1.left, rect2.left),
2196 right: Math.min(rect1.right, rect2.right),
2197 top: Math.max(rect1.top, rect2.top),
2198 bottom: Math.min(rect1.bottom, rect2.bottom),
2199 };
2200 if (res.left < res.right && res.top < res.bottom) {
2201 return res;
2202 }
2203 return false;
2204 }
2205 function translateRect(rect, deltaX, deltaY) {
2206 return {
2207 left: rect.left + deltaX,
2208 right: rect.right + deltaX,
2209 top: rect.top + deltaY,
2210 bottom: rect.bottom + deltaY,
2211 };
2212 }
2213 // Returns a new point that will have been moved to reside within the given rectangle
2214 function constrainPoint(point, rect) {
2215 return {
2216 left: Math.min(Math.max(point.left, rect.left), rect.right),
2217 top: Math.min(Math.max(point.top, rect.top), rect.bottom),
2218 };
2219 }
2220 // Returns a point that is the center of the given rectangle
2221 function getRectCenter(rect) {
2222 return {
2223 left: (rect.left + rect.right) / 2,
2224 top: (rect.top + rect.bottom) / 2,
2225 };
2226 }
2227 // Subtracts point2's coordinates from point1's coordinates, returning a delta
2228 function diffPoints(point1, point2) {
2229 return {
2230 left: point1.left - point2.left,
2231 top: point1.top - point2.top,
2232 };
2233 }
2234
2235 let canVGrowWithinCell;
2236 function getCanVGrowWithinCell() {
2237 if (canVGrowWithinCell == null) {
2238 canVGrowWithinCell = computeCanVGrowWithinCell();
2239 }
2240 return canVGrowWithinCell;
2241 }
2242 function computeCanVGrowWithinCell() {
2243 // for SSR, because this function is call immediately at top-level
2244 // TODO: just make this logic execute top-level, immediately, instead of doing lazily
2245 if (typeof document === 'undefined') {
2246 return true;
2247 }
2248 let el = document.createElement('div');
2249 el.style.position = 'absolute';
2250 el.style.top = '0px';
2251 el.style.left = '0px';
2252 el.innerHTML = '<table><tr><td><div></div></td></tr></table>';
2253 el.querySelector('table').style.height = '100px';
2254 el.querySelector('div').style.height = '100%';
2255 document.body.appendChild(el);
2256 let div = el.querySelector('div');
2257 let possible = div.offsetHeight > 0;
2258 document.body.removeChild(el);
2259 return possible;
2260 }
2261
2262 const EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
2263 class Splitter {
2264 constructor() {
2265 this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
2266 this.splitDateSelection = memoize(this._splitDateSpan);
2267 this.splitEventStore = memoize(this._splitEventStore);
2268 this.splitIndividualUi = memoize(this._splitIndividualUi);
2269 this.splitEventDrag = memoize(this._splitInteraction);
2270 this.splitEventResize = memoize(this._splitInteraction);
2271 this.eventUiBuilders = {}; // TODO: typescript protection
2272 }
2273 splitProps(props) {
2274 let keyInfos = this.getKeyInfo(props);
2275 let defKeys = this.getKeysForEventDefs(props.eventStore);
2276 let dateSelections = this.splitDateSelection(props.dateSelection);
2277 let individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
2278 let eventStores = this.splitEventStore(props.eventStore, defKeys);
2279 let eventDrags = this.splitEventDrag(props.eventDrag);
2280 let eventResizes = this.splitEventResize(props.eventResize);
2281 let splitProps = {};
2282 this.eventUiBuilders = mapHash(keyInfos, (info, key) => this.eventUiBuilders[key] || memoize(buildEventUiForKey));
2283 for (let key in keyInfos) {
2284 let keyInfo = keyInfos[key];
2285 let eventStore = eventStores[key] || EMPTY_EVENT_STORE;
2286 let buildEventUi = this.eventUiBuilders[key];
2287 splitProps[key] = {
2288 businessHours: keyInfo.businessHours || props.businessHours,
2289 dateSelection: dateSelections[key] || null,
2290 eventStore,
2291 eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
2292 eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
2293 eventDrag: eventDrags[key] || null,
2294 eventResize: eventResizes[key] || null,
2295 };
2296 }
2297 return splitProps;
2298 }
2299 _splitDateSpan(dateSpan) {
2300 let dateSpans = {};
2301 if (dateSpan) {
2302 let keys = this.getKeysForDateSpan(dateSpan);
2303 for (let key of keys) {
2304 dateSpans[key] = dateSpan;
2305 }
2306 }
2307 return dateSpans;
2308 }
2309 _getKeysForEventDefs(eventStore) {
2310 return mapHash(eventStore.defs, (eventDef) => this.getKeysForEventDef(eventDef));
2311 }
2312 _splitEventStore(eventStore, defKeys) {
2313 let { defs, instances } = eventStore;
2314 let splitStores = {};
2315 for (let defId in defs) {
2316 for (let key of defKeys[defId]) {
2317 if (!splitStores[key]) {
2318 splitStores[key] = createEmptyEventStore();
2319 }
2320 splitStores[key].defs[defId] = defs[defId];
2321 }
2322 }
2323 for (let instanceId in instances) {
2324 let instance = instances[instanceId];
2325 for (let key of defKeys[instance.defId]) {
2326 if (splitStores[key]) { // must have already been created
2327 splitStores[key].instances[instanceId] = instance;
2328 }
2329 }
2330 }
2331 return splitStores;
2332 }
2333 _splitIndividualUi(eventUiBases, defKeys) {
2334 let splitHashes = {};
2335 for (let defId in eventUiBases) {
2336 if (defId) { // not the '' key
2337 for (let key of defKeys[defId]) {
2338 if (!splitHashes[key]) {
2339 splitHashes[key] = {};
2340 }
2341 splitHashes[key][defId] = eventUiBases[defId];
2342 }
2343 }
2344 }
2345 return splitHashes;
2346 }
2347 _splitInteraction(interaction) {
2348 let splitStates = {};
2349 if (interaction) {
2350 let affectedStores = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents));
2351 // can't rely on defKeys because event data is mutated
2352 let mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
2353 let mutatedStores = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
2354 let populate = (key) => {
2355 if (!splitStates[key]) {
2356 splitStates[key] = {
2357 affectedEvents: affectedStores[key] || EMPTY_EVENT_STORE,
2358 mutatedEvents: mutatedStores[key] || EMPTY_EVENT_STORE,
2359 isEvent: interaction.isEvent,
2360 };
2361 }
2362 };
2363 for (let key in affectedStores) {
2364 populate(key);
2365 }
2366 for (let key in mutatedStores) {
2367 populate(key);
2368 }
2369 }
2370 return splitStates;
2371 }
2372 }
2373 function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
2374 let baseParts = [];
2375 if (allUi) {
2376 baseParts.push(allUi);
2377 }
2378 if (eventUiForKey) {
2379 baseParts.push(eventUiForKey);
2380 }
2381 let stuff = {
2382 '': combineEventUis(baseParts),
2383 };
2384 if (individualUi) {
2385 Object.assign(stuff, individualUi);
2386 }
2387 return stuff;
2388 }
2389
2390 function parseRange(input, dateEnv) {
2391 let start = null;
2392 let end = null;
2393 if (input.start) {
2394 start = dateEnv.createMarker(input.start);
2395 }
2396 if (input.end) {
2397 end = dateEnv.createMarker(input.end);
2398 }
2399 if (!start && !end) {
2400 return null;
2401 }
2402 if (start && end && end < start) {
2403 return null;
2404 }
2405 return { start, end };
2406 }
2407 // SIDE-EFFECT: will mutate ranges.
2408 // Will return a new array result.
2409 function invertRanges(ranges, constraintRange) {
2410 let invertedRanges = [];
2411 let { start } = constraintRange; // the end of the previous range. the start of the new range
2412 let i;
2413 let dateRange;
2414 // ranges need to be in order. required for our date-walking algorithm
2415 ranges.sort(compareRanges);
2416 for (i = 0; i < ranges.length; i += 1) {
2417 dateRange = ranges[i];
2418 // add the span of time before the event (if there is any)
2419 if (dateRange.start > start) { // compare millisecond time (skip any ambig logic)
2420 invertedRanges.push({ start, end: dateRange.start });
2421 }
2422 if (dateRange.end > start) {
2423 start = dateRange.end;
2424 }
2425 }
2426 // add the span of time after the last event (if there is any)
2427 if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic)
2428 invertedRanges.push({ start, end: constraintRange.end });
2429 }
2430 return invertedRanges;
2431 }
2432 function compareRanges(range0, range1) {
2433 return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first
2434 }
2435 function intersectRanges(range0, range1) {
2436 let { start, end } = range0;
2437 let newRange = null;
2438 if (range1.start !== null) {
2439 if (start === null) {
2440 start = range1.start;
2441 }
2442 else {
2443 start = new Date(Math.max(start.valueOf(), range1.start.valueOf()));
2444 }
2445 }
2446 if (range1.end != null) {
2447 if (end === null) {
2448 end = range1.end;
2449 }
2450 else {
2451 end = new Date(Math.min(end.valueOf(), range1.end.valueOf()));
2452 }
2453 }
2454 if (start === null || end === null || start < end) {
2455 newRange = { start, end };
2456 }
2457 return newRange;
2458 }
2459 function rangesEqual(range0, range1) {
2460 return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) &&
2461 (range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf());
2462 }
2463 function rangesIntersect(range0, range1) {
2464 return (range0.end === null || range1.start === null || range0.end > range1.start) &&
2465 (range0.start === null || range1.end === null || range0.start < range1.end);
2466 }
2467 function rangeContainsRange(outerRange, innerRange) {
2468 return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) &&
2469 (outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end));
2470 }
2471 function rangeContainsMarker(range, date) {
2472 return (range.start === null || date >= range.start) &&
2473 (range.end === null || date < range.end);
2474 }
2475 // If the given date is not within the given range, move it inside.
2476 // (If it's past the end, make it one millisecond before the end).
2477 function constrainMarkerToRange(date, range) {
2478 if (range.start != null && date < range.start) {
2479 return range.start;
2480 }
2481 if (range.end != null && date >= range.end) {
2482 return new Date(range.end.valueOf() - 1);
2483 }
2484 return date;
2485 }
2486
2487 function getDateMeta(date, todayRange, nowDate, dateProfile) {
2488 return {
2489 dow: date.getUTCDay(),
2490 isDisabled: Boolean(dateProfile && !rangeContainsMarker(dateProfile.activeRange, date)),
2491 isOther: Boolean(dateProfile && !rangeContainsMarker(dateProfile.currentRange, date)),
2492 isToday: Boolean(todayRange && rangeContainsMarker(todayRange, date)),
2493 isPast: Boolean(nowDate ? (date < nowDate) : todayRange ? (date < todayRange.start) : false),
2494 isFuture: Boolean(nowDate ? (date > nowDate) : todayRange ? (date >= todayRange.end) : false),
2495 };
2496 }
2497 function getDayClassNames(meta, theme) {
2498 let classNames = [
2499 'fc-day',
2500 `fc-day-${DAY_IDS[meta.dow]}`,
2501 ];
2502 if (meta.isDisabled) {
2503 classNames.push('fc-day-disabled');
2504 }
2505 else {
2506 if (meta.isToday) {
2507 classNames.push('fc-day-today');
2508 classNames.push(theme.getClass('today'));
2509 }
2510 if (meta.isPast) {
2511 classNames.push('fc-day-past');
2512 }
2513 if (meta.isFuture) {
2514 classNames.push('fc-day-future');
2515 }
2516 if (meta.isOther) {
2517 classNames.push('fc-day-other');
2518 }
2519 }
2520 return classNames;
2521 }
2522 function getSlotClassNames(meta, theme) {
2523 let classNames = [
2524 'fc-slot',
2525 `fc-slot-${DAY_IDS[meta.dow]}`,
2526 ];
2527 if (meta.isDisabled) {
2528 classNames.push('fc-slot-disabled');
2529 }
2530 else {
2531 if (meta.isToday) {
2532 classNames.push('fc-slot-today');
2533 classNames.push(theme.getClass('today'));
2534 }
2535 if (meta.isPast) {
2536 classNames.push('fc-slot-past');
2537 }
2538 if (meta.isFuture) {
2539 classNames.push('fc-slot-future');
2540 }
2541 }
2542 return classNames;
2543 }
2544
2545 const DAY_FORMAT = createFormatter({ year: 'numeric', month: 'long', day: 'numeric' });
2546 const WEEK_FORMAT = createFormatter({ week: 'long' });
2547 function buildNavLinkAttrs(context, dateMarker, viewType = 'day', isTabbable = true) {
2548 const { dateEnv, options, calendarApi } = context;
2549 let dateStr = dateEnv.format(dateMarker, viewType === 'week' ? WEEK_FORMAT : DAY_FORMAT);
2550 if (options.navLinks) {
2551 let zonedDate = dateEnv.toDate(dateMarker);
2552 const handleInteraction = (ev) => {
2553 let customAction = viewType === 'day' ? options.navLinkDayClick :
2554 viewType === 'week' ? options.navLinkWeekClick : null;
2555 if (typeof customAction === 'function') {
2556 customAction.call(calendarApi, dateEnv.toDate(dateMarker), ev);
2557 }
2558 else {
2559 if (typeof customAction === 'string') {
2560 viewType = customAction;
2561 }
2562 calendarApi.zoomTo(dateMarker, viewType);
2563 }
2564 };
2565 return Object.assign({ title: formatWithOrdinals(options.navLinkHint, [dateStr, zonedDate], dateStr), 'data-navlink': '' }, (isTabbable
2566 ? createAriaClickAttrs(handleInteraction)
2567 : { onClick: handleInteraction }));
2568 }
2569 return { 'aria-label': dateStr };
2570 }
2571
2572 let _isRtlScrollbarOnLeft = null;
2573 function getIsRtlScrollbarOnLeft() {
2574 if (_isRtlScrollbarOnLeft === null) {
2575 _isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
2576 }
2577 return _isRtlScrollbarOnLeft;
2578 }
2579 function computeIsRtlScrollbarOnLeft() {
2580 let outerEl = document.createElement('div');
2581 applyStyle(outerEl, {
2582 position: 'absolute',
2583 top: -1000,
2584 left: 0,
2585 border: 0,
2586 padding: 0,
2587 overflow: 'scroll',
2588 direction: 'rtl',
2589 });
2590 outerEl.innerHTML = '<div></div>';
2591 document.body.appendChild(outerEl);
2592 let innerEl = outerEl.firstChild;
2593 let res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
2594 removeElement(outerEl);
2595 return res;
2596 }
2597
2598 let _scrollbarWidths;
2599 function getScrollbarWidths() {
2600 if (!_scrollbarWidths) {
2601 _scrollbarWidths = computeScrollbarWidths();
2602 }
2603 return _scrollbarWidths;
2604 }
2605 function computeScrollbarWidths() {
2606 let el = document.createElement('div');
2607 el.style.overflow = 'scroll';
2608 el.style.position = 'absolute';
2609 el.style.top = '-9999px';
2610 el.style.left = '-9999px';
2611 document.body.appendChild(el);
2612 let res = computeScrollbarWidthsForEl(el);
2613 document.body.removeChild(el);
2614 return res;
2615 }
2616 // WARNING: will include border
2617 function computeScrollbarWidthsForEl(el) {
2618 return {
2619 x: el.offsetHeight - el.clientHeight,
2620 y: el.offsetWidth - el.clientWidth,
2621 };
2622 }
2623
2624 function computeEdges(el, getPadding = false) {
2625 let computedStyle = window.getComputedStyle(el);
2626 let borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
2627 let borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
2628 let borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
2629 let borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
2630 let badScrollbarWidths = computeScrollbarWidthsForEl(el); // includes border!
2631 let scrollbarLeftRight = badScrollbarWidths.y - borderLeft - borderRight;
2632 let scrollbarBottom = badScrollbarWidths.x - borderTop - borderBottom;
2633 let res = {
2634 borderLeft,
2635 borderRight,
2636 borderTop,
2637 borderBottom,
2638 scrollbarBottom,
2639 scrollbarLeft: 0,
2640 scrollbarRight: 0,
2641 };
2642 if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
2643 res.scrollbarLeft = scrollbarLeftRight;
2644 }
2645 else {
2646 res.scrollbarRight = scrollbarLeftRight;
2647 }
2648 if (getPadding) {
2649 res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
2650 res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
2651 res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
2652 res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
2653 }
2654 return res;
2655 }
2656 function computeInnerRect(el, goWithinPadding = false, doFromWindowViewport) {
2657 let outerRect = doFromWindowViewport ? el.getBoundingClientRect() : computeRect(el);
2658 let edges = computeEdges(el, goWithinPadding);
2659 let res = {
2660 left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
2661 right: outerRect.right - edges.borderRight - edges.scrollbarRight,
2662 top: outerRect.top + edges.borderTop,
2663 bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom,
2664 };
2665 if (goWithinPadding) {
2666 res.left += edges.paddingLeft;
2667 res.right -= edges.paddingRight;
2668 res.top += edges.paddingTop;
2669 res.bottom -= edges.paddingBottom;
2670 }
2671 return res;
2672 }
2673 function computeRect(el) {
2674 let rect = el.getBoundingClientRect();
2675 return {
2676 left: rect.left + window.scrollX,
2677 top: rect.top + window.scrollY,
2678 right: rect.right + window.scrollX,
2679 bottom: rect.bottom + window.scrollY,
2680 };
2681 }
2682 function computeClippedClientRect(el) {
2683 let clippingParents = getClippingParents(el);
2684 let rect = el.getBoundingClientRect();
2685 for (let clippingParent of clippingParents) {
2686 let intersection = intersectRects(rect, clippingParent.getBoundingClientRect());
2687 if (intersection) {
2688 rect = intersection;
2689 }
2690 else {
2691 return null;
2692 }
2693 }
2694 return rect;
2695 }
2696 // does not return window
2697 function getClippingParents(el) {
2698 let parents = [];
2699 while (el instanceof HTMLElement) { // will stop when gets to document or null
2700 let computedStyle = window.getComputedStyle(el);
2701 if (computedStyle.position === 'fixed') {
2702 break;
2703 }
2704 if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
2705 parents.push(el);
2706 }
2707 el = el.parentNode;
2708 }
2709 return parents;
2710 }
2711
2712 /*
2713 given a function that resolves a result asynchronously.
2714 the function can either call passed-in success and failure callbacks,
2715 or it can return a promise.
2716 if you need to pass additional params to func, bind them first.
2717 */
2718 function unpromisify(func, normalizedSuccessCallback, normalizedFailureCallback) {
2719 // guard against success/failure callbacks being called more than once
2720 // and guard against a promise AND callback being used together.
2721 let isResolved = false;
2722 let wrappedSuccess = function (res) {
2723 if (!isResolved) {
2724 isResolved = true;
2725 normalizedSuccessCallback(res);
2726 }
2727 };
2728 let wrappedFailure = function (error) {
2729 if (!isResolved) {
2730 isResolved = true;
2731 normalizedFailureCallback(error);
2732 }
2733 };
2734 let res = func(wrappedSuccess, wrappedFailure);
2735 if (res && typeof res.then === 'function') {
2736 res.then(wrappedSuccess, wrappedFailure);
2737 }
2738 }
2739
2740 class Emitter {
2741 constructor() {
2742 this.handlers = {};
2743 this.thisContext = null;
2744 }
2745 setThisContext(thisContext) {
2746 this.thisContext = thisContext;
2747 }
2748 setOptions(options) {
2749 this.options = options;
2750 }
2751 on(type, handler) {
2752 addToHash(this.handlers, type, handler);
2753 }
2754 off(type, handler) {
2755 removeFromHash(this.handlers, type, handler);
2756 }
2757 trigger(type, ...args) {
2758 let attachedHandlers = this.handlers[type] || [];
2759 let optionHandler = this.options && this.options[type];
2760 let handlers = [].concat(optionHandler || [], attachedHandlers);
2761 for (let handler of handlers) {
2762 handler.apply(this.thisContext, args);
2763 }
2764 }
2765 hasHandlers(type) {
2766 return Boolean((this.handlers[type] && this.handlers[type].length) ||
2767 (this.options && this.options[type]));
2768 }
2769 }
2770 function addToHash(hash, type, handler) {
2771 (hash[type] || (hash[type] = []))
2772 .push(handler);
2773 }
2774 function removeFromHash(hash, type, handler) {
2775 if (handler) {
2776 if (hash[type]) {
2777 hash[type] = hash[type].filter((func) => func !== handler);
2778 }
2779 }
2780 else {
2781 delete hash[type]; // remove all handler funcs for this type
2782 }
2783 }
2784
2785 /*
2786 Records offset information for a set of elements, relative to an origin element.
2787 Can record the left/right OR the top/bottom OR both.
2788 Provides methods for querying the cache by position.
2789 */
2790 class PositionCache {
2791 constructor(originEl, els, isHorizontal, isVertical) {
2792 this.els = els;
2793 let originClientRect = this.originClientRect = originEl.getBoundingClientRect(); // relative to viewport top-left
2794 if (isHorizontal) {
2795 this.buildElHorizontals(originClientRect.left);
2796 }
2797 if (isVertical) {
2798 this.buildElVerticals(originClientRect.top);
2799 }
2800 }
2801 // Populates the left/right internal coordinate arrays
2802 buildElHorizontals(originClientLeft) {
2803 let lefts = [];
2804 let rights = [];
2805 for (let el of this.els) {
2806 let rect = el.getBoundingClientRect();
2807 lefts.push(rect.left - originClientLeft);
2808 rights.push(rect.right - originClientLeft);
2809 }
2810 this.lefts = lefts;
2811 this.rights = rights;
2812 }
2813 // Populates the top/bottom internal coordinate arrays
2814 buildElVerticals(originClientTop) {
2815 let tops = [];
2816 let bottoms = [];
2817 for (let el of this.els) {
2818 let rect = el.getBoundingClientRect();
2819 tops.push(rect.top - originClientTop);
2820 bottoms.push(rect.bottom - originClientTop);
2821 }
2822 this.tops = tops;
2823 this.bottoms = bottoms;
2824 }
2825 // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
2826 // If no intersection is made, returns undefined.
2827 leftToIndex(leftPosition) {
2828 let { lefts, rights } = this;
2829 let len = lefts.length;
2830 let i;
2831 for (i = 0; i < len; i += 1) {
2832 if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
2833 return i;
2834 }
2835 }
2836 return undefined; // TODO: better
2837 }
2838 // Given a top offset (from document top), returns the index of the el that it vertically intersects.
2839 // If no intersection is made, returns undefined.
2840 topToIndex(topPosition) {
2841 let { tops, bottoms } = this;
2842 let len = tops.length;
2843 let i;
2844 for (i = 0; i < len; i += 1) {
2845 if (topPosition >= tops[i] && topPosition < bottoms[i]) {
2846 return i;
2847 }
2848 }
2849 return undefined; // TODO: better
2850 }
2851 // Gets the width of the element at the given index
2852 getWidth(leftIndex) {
2853 return this.rights[leftIndex] - this.lefts[leftIndex];
2854 }
2855 // Gets the height of the element at the given index
2856 getHeight(topIndex) {
2857 return this.bottoms[topIndex] - this.tops[topIndex];
2858 }
2859 similarTo(otherCache) {
2860 return similarNumArrays(this.tops || [], otherCache.tops || []) &&
2861 similarNumArrays(this.bottoms || [], otherCache.bottoms || []) &&
2862 similarNumArrays(this.lefts || [], otherCache.lefts || []) &&
2863 similarNumArrays(this.rights || [], otherCache.rights || []);
2864 }
2865 }
2866 function similarNumArrays(a, b) {
2867 const len = a.length;
2868 if (len !== b.length) {
2869 return false;
2870 }
2871 for (let i = 0; i < len; i++) {
2872 if (Math.round(a[i]) !== Math.round(b[i])) {
2873 return false;
2874 }
2875 }
2876 return true;
2877 }
2878
2879 /* eslint max-classes-per-file: "off" */
2880 /*
2881 An object for getting/setting scroll-related information for an element.
2882 Internally, this is done very differently for window versus DOM element,
2883 so this object serves as a common interface.
2884 */
2885 class ScrollController {
2886 getMaxScrollTop() {
2887 return this.getScrollHeight() - this.getClientHeight();
2888 }
2889 getMaxScrollLeft() {
2890 return this.getScrollWidth() - this.getClientWidth();
2891 }
2892 canScrollVertically() {
2893 return this.getMaxScrollTop() > 0;
2894 }
2895 canScrollHorizontally() {
2896 return this.getMaxScrollLeft() > 0;
2897 }
2898 canScrollUp() {
2899 return this.getScrollTop() > 0;
2900 }
2901 canScrollDown() {
2902 return this.getScrollTop() < this.getMaxScrollTop();
2903 }
2904 canScrollLeft() {
2905 return this.getScrollLeft() > 0;
2906 }
2907 canScrollRight() {
2908 return this.getScrollLeft() < this.getMaxScrollLeft();
2909 }
2910 }
2911 class ElementScrollController extends ScrollController {
2912 constructor(el) {
2913 super();
2914 this.el = el;
2915 }
2916 getScrollTop() {
2917 return this.el.scrollTop;
2918 }
2919 getScrollLeft() {
2920 return this.el.scrollLeft;
2921 }
2922 setScrollTop(top) {
2923 this.el.scrollTop = top;
2924 }
2925 setScrollLeft(left) {
2926 this.el.scrollLeft = left;
2927 }
2928 getScrollWidth() {
2929 return this.el.scrollWidth;
2930 }
2931 getScrollHeight() {
2932 return this.el.scrollHeight;
2933 }
2934 getClientHeight() {
2935 return this.el.clientHeight;
2936 }
2937 getClientWidth() {
2938 return this.el.clientWidth;
2939 }
2940 }
2941 class WindowScrollController extends ScrollController {
2942 getScrollTop() {
2943 return window.scrollY;
2944 }
2945 getScrollLeft() {
2946 return window.scrollX;
2947 }
2948 setScrollTop(n) {
2949 window.scroll(window.scrollX, n);
2950 }
2951 setScrollLeft(n) {
2952 window.scroll(n, window.scrollY);
2953 }
2954 getScrollWidth() {
2955 return document.documentElement.scrollWidth;
2956 }
2957 getScrollHeight() {
2958 return document.documentElement.scrollHeight;
2959 }
2960 getClientHeight() {
2961 return document.documentElement.clientHeight;
2962 }
2963 getClientWidth() {
2964 return document.documentElement.clientWidth;
2965 }
2966 }
2967
2968 class Theme {
2969 constructor(calendarOptions) {
2970 if (this.iconOverrideOption) {
2971 this.setIconOverride(calendarOptions[this.iconOverrideOption]);
2972 }
2973 }
2974 setIconOverride(iconOverrideHash) {
2975 let iconClassesCopy;
2976 let buttonName;
2977 if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object
2978 iconClassesCopy = Object.assign({}, this.iconClasses);
2979 for (buttonName in iconOverrideHash) {
2980 iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]);
2981 }
2982 this.iconClasses = iconClassesCopy;
2983 }
2984 else if (iconOverrideHash === false) {
2985 this.iconClasses = {};
2986 }
2987 }
2988 applyIconOverridePrefix(className) {
2989 let prefix = this.iconOverridePrefix;
2990 if (prefix && className.indexOf(prefix) !== 0) { // if not already present
2991 className = prefix + className;
2992 }
2993 return className;
2994 }
2995 getClass(key) {
2996 return this.classes[key] || '';
2997 }
2998 getIconClass(buttonName, isRtl) {
2999 let className;
3000 if (isRtl && this.rtlIconClasses) {
3001 className = this.rtlIconClasses[buttonName] || this.iconClasses[buttonName];
3002 }
3003 else {
3004 className = this.iconClasses[buttonName];
3005 }
3006 if (className) {
3007 return `${this.baseIconClass} ${className}`;
3008 }
3009 return '';
3010 }
3011 getCustomButtonIconClass(customButtonProps) {
3012 let className;
3013 if (this.iconOverrideCustomButtonOption) {
3014 className = customButtonProps[this.iconOverrideCustomButtonOption];
3015 if (className) {
3016 return `${this.baseIconClass} ${this.applyIconOverridePrefix(className)}`;
3017 }
3018 }
3019 return '';
3020 }
3021 }
3022 Theme.prototype.classes = {};
3023 Theme.prototype.iconClasses = {};
3024 Theme.prototype.baseIconClass = '';
3025 Theme.prototype.iconOverridePrefix = '';
3026
3027 var n,l$1,u$1,i$1,t,r$1,o,f$1,e$1,c$1={},s=[],a$1=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function h(n,l){for(var u in l)n[u]=l[u];return n}function v$1(n){var l=n.parentNode;l&&l.removeChild(n);}function y(l,u,i){var t,r,o,f={};for(o in u)"key"==o?t=u[o]:"ref"==o?r=u[o]:f[o]=u[o];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),"function"==typeof l&&null!=l.defaultProps)for(o in l.defaultProps)void 0===f[o]&&(f[o]=l.defaultProps[o]);return p(l,f,t,r,null)}function p(n,i,t,r,o){var f={type:n,props:i,key:t,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==o?++u$1:o};return null==o&&null!=l$1.vnode&&l$1.vnode(f),f}function d(){return {current:null}}function _(n){return n.children}function k$1(n,l,u,i,t){var r;for(r in u)"children"===r||"key"===r||r in l||g$2(n,r,null,u[r],i);for(r in l)t&&"function"!=typeof l[r]||"children"===r||"key"===r||"value"===r||"checked"===r||u[r]===l[r]||g$2(n,r,l[r],u[r],i);}function b$1(n,l,u){"-"===l[0]?n.setProperty(l,null==u?"":u):n[l]=null==u?"":"number"!=typeof u||a$1.test(l)?u:u+"px";}function g$2(n,l,u,i,t){var r;n:if("style"===l)if("string"==typeof u)n.style.cssText=u;else {if("string"==typeof i&&(n.style.cssText=i=""),i)for(l in i)u&&l in u||b$1(n.style,l,"");if(u)for(l in u)i&&u[l]===i[l]||b$1(n.style,l,u[l]);}else if("o"===l[0]&&"n"===l[1])r=l!==(l=l.replace(/Capture$/,"")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+r]=u,u?i||n.addEventListener(l,r?w$2:m$1,r):n.removeEventListener(l,r?w$2:m$1,r);else if("dangerouslySetInnerHTML"!==l){if(t)l=l.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==l&&"height"!==l&&"href"!==l&&"list"!==l&&"form"!==l&&"tabIndex"!==l&&"download"!==l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null==u||!1===u&&-1==l.indexOf("-")?n.removeAttribute(l):n.setAttribute(l,u));}}function m$1(n){t=!0;try{return this.l[n.type+!1](l$1.event?l$1.event(n):n)}finally{t=!1;}}function w$2(n){t=!0;try{return this.l[n.type+!0](l$1.event?l$1.event(n):n)}finally{t=!1;}}function x$1(n,l){this.props=n,this.context=l;}function A(n,l){if(null==l)return n.__?A(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return "function"==typeof n.type?A(n):null}function P$1(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return P$1(n)}}function C$1(n){t?setTimeout(n):f$1(n);}function T$1(n){(!n.__d&&(n.__d=!0)&&r$1.push(n)&&!$$1.__r++||o!==l$1.debounceRendering)&&((o=l$1.debounceRendering)||C$1)($$1);}function $$1(){var n,l,u,i,t,o,f,e;for(r$1.sort(function(n,l){return n.__v.__b-l.__v.__b});n=r$1.shift();)n.__d&&(l=r$1.length,i=void 0,t=void 0,f=(o=(u=n).__v).__e,(e=u.__P)&&(i=[],(t=h({},o)).__v=o.__v+1,M(e,o,t,u.__n,void 0!==e.ownerSVGElement,null!=o.__h?[f]:null,i,null==f?A(o):f,o.__h),N(i,o),o.__e!=f&&P$1(o)),r$1.length>l&&r$1.sort(function(n,l){return n.__v.__b-l.__v.__b}));$$1.__r=0;}function H$1(n,l,u,i,t,r,o,f,e,a){var h,v,y,d,k,b,g,m=i&&i.__k||s,w=m.length;for(u.__k=[],h=0;h<l.length;h++)if(null!=(d=u.__k[h]=null==(d=l[h])||"boolean"==typeof d?null:"string"==typeof d||"number"==typeof d||"bigint"==typeof d?p(null,d,null,null,d):Array.isArray(d)?p(_,{children:d},null,null,null):d.__b>0?p(d.type,d.props,d.key,d.ref?d.ref:null,d.__v):d)){if(d.__=u,d.__b=u.__b+1,null===(y=m[h])||y&&d.key==y.key&&d.type===y.type)m[h]=void 0;else for(v=0;v<w;v++){if((y=m[v])&&d.key==y.key&&d.type===y.type){m[v]=void 0;break}y=null;}M(n,d,y=y||c$1,t,r,o,f,e,a),k=d.__e,(v=d.ref)&&y.ref!=v&&(g||(g=[]),y.ref&&g.push(y.ref,null,d),g.push(v,d.__c||k,d)),null!=k?(null==b&&(b=k),"function"==typeof d.type&&d.__k===y.__k?d.__d=e=I$1(d,e,n):e=z$1(n,d,y,m,k,e),"function"==typeof u.type&&(u.__d=e)):e&&y.__e==e&&e.parentNode!=n&&(e=A(y));}for(u.__e=b,h=w;h--;)null!=m[h]&&("function"==typeof u.type&&null!=m[h].__e&&m[h].__e==u.__d&&(u.__d=L$1(i).nextSibling),q(m[h],m[h]));if(g)for(h=0;h<g.length;h++)S(g[h],g[++h],g[++h]);}function I$1(n,l,u){for(var i,t=n.__k,r=0;t&&r<t.length;r++)(i=t[r])&&(i.__=n,l="function"==typeof i.type?I$1(i,l,u):z$1(u,i,i,t,i.__e,l));return l}function j$2(n,l){return l=l||[],null==n||"boolean"==typeof n||(Array.isArray(n)?n.some(function(n){j$2(n,l);}):l.push(n)),l}function z$1(n,l,u,i,t,r){var o,f,e;if(void 0!==l.__d)o=l.__d,l.__d=void 0;else if(null==u||t!=r||null==t.parentNode)n:if(null==r||r.parentNode!==n)n.appendChild(t),o=null;else {for(f=r,e=0;(f=f.nextSibling)&&e<i.length;e+=1)if(f==t)break n;n.insertBefore(t,r),o=r;}return void 0!==o?o:t.nextSibling}function L$1(n){var l,u,i;if(null==n.type||"string"==typeof n.type)return n.__e;if(n.__k)for(l=n.__k.length-1;l>=0;l--)if((u=n.__k[l])&&(i=L$1(u)))return i;return null}function M(n,u,i,t,r,o,f,e,c){var s,a,v,y,p,d,k,b,g,m,w,A,P,C,T,$=u.type;if(void 0!==u.constructor)return null;null!=i.__h&&(c=i.__h,e=u.__e=i.__e,u.__h=null,o=[e]),(s=l$1.__b)&&s(u);try{n:if("function"==typeof $){if(b=u.props,g=(s=$.contextType)&&t[s.__c],m=s?g?g.props.value:s.__:t,i.__c?k=(a=u.__c=i.__c).__=a.__E:("prototype"in $&&$.prototype.render?u.__c=a=new $(b,m):(u.__c=a=new x$1(b,m),a.constructor=$,a.render=B$1),g&&g.sub(a),a.props=b,a.state||(a.state={}),a.context=m,a.__n=t,v=a.__d=!0,a.__h=[],a._sb=[]),null==a.__s&&(a.__s=a.state),null!=$.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=h({},a.__s)),h(a.__s,$.getDerivedStateFromProps(b,a.__s))),y=a.props,p=a.state,a.__v=u,v)null==$.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else {if(null==$.getDerivedStateFromProps&&b!==y&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,m),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldComponentUpdate(b,a.__s,m)||u.__v===i.__v){for(u.__v!==i.__v&&(a.props=b,a.state=a.__s,a.__d=!1),u.__e=i.__e,u.__k=i.__k,u.__k.forEach(function(n){n&&(n.__=u);}),w=0;w<a._sb.length;w++)a.__h.push(a._sb[w]);a._sb=[],a.__h.length&&f.push(a);break n}null!=a.componentWillUpdate&&a.componentWillUpdate(b,a.__s,m),null!=a.componentDidUpdate&&a.__h.push(function(){a.componentDidUpdate(y,p,d);});}if(a.context=m,a.props=b,a.__P=n,A=l$1.__r,P=0,"prototype"in $&&$.prototype.render){for(a.state=a.__s,a.__d=!1,A&&A(u),s=a.render(a.props,a.state,a.context),C=0;C<a._sb.length;C++)a.__h.push(a._sb[C]);a._sb=[];}else do{a.__d=!1,A&&A(u),s=a.render(a.props,a.state,a.context),a.state=a.__s;}while(a.__d&&++P<25);a.state=a.__s,null!=a.getChildContext&&(t=h(h({},t),a.getChildContext())),v||null==a.getSnapshotBeforeUpdate||(d=a.getSnapshotBeforeUpdate(y,p)),T=null!=s&&s.type===_&&null==s.key?s.props.children:s,H$1(n,Array.isArray(T)?T:[T],u,i,t,r,o,f,e,c),a.base=u.__e,u.__h=null,a.__h.length&&f.push(a),k&&(a.__E=a.__=null),a.__e=!1;}else null==o&&u.__v===i.__v?(u.__k=i.__k,u.__e=i.__e):u.__e=O(i.__e,u,i,t,r,o,f,c);(s=l$1.diffed)&&s(u);}catch(n){u.__v=null,(c||null!=o)&&(u.__e=e,u.__h=!!c,o[o.indexOf(e)]=null),l$1.__e(n,u,i);}}function N(n,u){l$1.__c&&l$1.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u);});}catch(n){l$1.__e(n,u.__v);}});}function O(l,u,i,t,r,o,f,e){var s,a,h,y=i.props,p=u.props,d=u.type,_=0;if("svg"===d&&(r=!0),null!=o)for(;_<o.length;_++)if((s=o[_])&&"setAttribute"in s==!!d&&(d?s.localName===d:3===s.nodeType)){l=s,o[_]=null;break}if(null==l){if(null===d)return document.createTextNode(p);l=r?document.createElementNS("http://www.w3.org/2000/svg",d):document.createElement(d,p.is&&p),o=null,e=!1;}if(null===d)y===p||e&&l.data===p||(l.data=p);else {if(o=o&&n.call(l.childNodes),a=(y=i.props||c$1).dangerouslySetInnerHTML,h=p.dangerouslySetInnerHTML,!e){if(null!=o)for(y={},_=0;_<l.attributes.length;_++)y[l.attributes[_].name]=l.attributes[_].value;(h||a)&&(h&&(a&&h.__html==a.__html||h.__html===l.innerHTML)||(l.innerHTML=h&&h.__html||""));}if(k$1(l,p,y,r,e),h)u.__k=[];else if(_=u.props.children,H$1(l,Array.isArray(_)?_:[_],u,i,t,r&&"foreignObject"!==d,o,f,o?o[0]:i.__k&&A(i,0),e),null!=o)for(_=o.length;_--;)null!=o[_]&&v$1(o[_]);e||("value"in p&&void 0!==(_=p.value)&&(_!==l.value||"progress"===d&&!_||"option"===d&&_!==y.value)&&g$2(l,"value",_,y.value,!1),"checked"in p&&void 0!==(_=p.checked)&&_!==l.checked&&g$2(l,"checked",_,y.checked,!1));}return l}function S(n,u,i){try{"function"==typeof n?n(u):n.current=u;}catch(n){l$1.__e(n,i);}}function q(n,u,i){var t,r;if(l$1.unmount&&l$1.unmount(n),(t=n.ref)&&(t.current&&t.current!==n.__e||S(t,null,u)),null!=(t=n.__c)){if(t.componentWillUnmount)try{t.componentWillUnmount();}catch(n){l$1.__e(n,u);}t.base=t.__P=null,n.__c=void 0;}if(t=n.__k)for(r=0;r<t.length;r++)t[r]&&q(t[r],u,i||"function"!=typeof n.type);i||null==n.__e||v$1(n.__e),n.__=n.__e=n.__d=void 0;}function B$1(n,l,u){return this.constructor(n,u)}function D$1(u,i,t){var r,o,f;l$1.__&&l$1.__(u,i),o=(r="function"==typeof t)?null:t&&t.__k||i.__k,f=[],M(i,u=(!r&&t||i).__k=y(_,null,[u]),o||c$1,c$1,void 0!==i.ownerSVGElement,!r&&t?[t]:o?null:i.firstChild?n.call(i.childNodes):null,f,!r&&t?t:o?o.__e:i.firstChild,r),N(f,u);}function E(n,l){D$1(n,l,E);}function F$1(l,u,i){var t,r,o,f=h({},l.props);for(o in u)"key"==o?t=u[o]:"ref"==o?r=u[o]:f[o]=u[o];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):i),p(l.type,f,t||l.key,r||l.ref,null)}function G$1(n,l){var u={__c:l="__cC"+e$1++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,i;return this.getChildContext||(u=[],(i={})[l]=this,this.getChildContext=function(){return i},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(function(n){n.__e=!0,T$1(n);});},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n);};}),n.children}};return u.Provider.__=u.Consumer.contextType=u}n=s.slice,l$1={__e:function(n,l,u,i){for(var t,r,o;l=l.__;)if((t=l.__c)&&!t.__)try{if((r=t.constructor)&&null!=r.getDerivedStateFromError&&(t.setState(r.getDerivedStateFromError(n)),o=t.__d),null!=t.componentDidCatch&&(t.componentDidCatch(n,i||{}),o=t.__d),o)return t.__E=t}catch(l){n=l;}throw n}},u$1=0,i$1=function(n){return null!=n&&void 0===n.constructor},t=!1,x$1.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=h({},this.state),"function"==typeof n&&(n=n(h({},u),this.props)),n&&h(u,n),null!=n&&this.__v&&(l&&this._sb.push(l),T$1(this));},x$1.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),T$1(this));},x$1.prototype.render=_,r$1=[],f$1="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,$$1.__r=0,e$1=0;
3028
3029 var r,u,i,f=[],c=[],e=l$1.__b,a=l$1.__r,v=l$1.diffed,l=l$1.__c,m=l$1.unmount;function b(){for(var t;t=f.shift();)if(t.__P&&t.__H)try{t.__H.__h.forEach(k),t.__H.__h.forEach(w$1),t.__H.__h=[];}catch(r){t.__H.__h=[],l$1.__e(r,t.__v);}}l$1.__b=function(n){r=null,e&&e(n);},l$1.__r=function(n){a&&a(n);var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.forEach(function(n){n.__N&&(n.__=n.__N),n.__V=c,n.__N=n.i=void 0;})):(i.__h.forEach(k),i.__h.forEach(w$1),i.__h=[])),u=r;},l$1.diffed=function(t){v&&v(t);var o=t.__c;o&&o.__H&&(o.__H.__h.length&&(1!==f.push(o)&&i===l$1.requestAnimationFrame||((i=l$1.requestAnimationFrame)||j$1)(b)),o.__H.__.forEach(function(n){n.i&&(n.__H=n.i),n.__V!==c&&(n.__=n.__V),n.i=void 0,n.__V=c;})),u=r=null;},l$1.__c=function(t,r){r.some(function(t){try{t.__h.forEach(k),t.__h=t.__h.filter(function(n){return !n.__||w$1(n)});}catch(u){r.some(function(n){n.__h&&(n.__h=[]);}),r=[],l$1.__e(u,t.__v);}}),l&&l(t,r);},l$1.unmount=function(t){m&&m(t);var r,u=t.__c;u&&u.__H&&(u.__H.__.forEach(function(n){try{k(n);}catch(n){r=n;}}),u.__H=void 0,r&&l$1.__e(r,u.__v));};var g$1="function"==typeof requestAnimationFrame;function j$1(n){var t,r=function(){clearTimeout(u),g$1&&cancelAnimationFrame(t),setTimeout(n);},u=setTimeout(r,100);g$1&&(t=requestAnimationFrame(r));}function k(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t;}function w$1(n){var t=r;n.__c=n.__(),r=t;}
3030
3031 function g(n,t){for(var e in t)n[e]=t[e];return n}function C(n,t){for(var e in n)if("__source"!==e&&!(e in t))return !0;for(var r in t)if("__source"!==r&&n[r]!==t[r])return !0;return !1}function w(n){this.props=n;}(w.prototype=new x$1).isPureReactComponent=!0,w.prototype.shouldComponentUpdate=function(n,t){return C(this.props,n)||C(this.state,t)};var x=l$1.__b;l$1.__b=function(n){n.type&&n.type.__f&&n.ref&&(n.props.ref=n.ref,n.ref=null),x&&x(n);};var T=l$1.__e;l$1.__e=function(n,t,e,r){if(n.then)for(var u,o=t;o=o.__;)if((u=o.__c)&&u.__c)return null==t.__e&&(t.__e=e.__e,t.__k=e.__k),u.__c(n,t);T(n,t,e,r);};var I=l$1.unmount;function L(n,t,e){return n&&(n.__c&&n.__c.__H&&(n.__c.__H.__.forEach(function(n){"function"==typeof n.__c&&n.__c();}),n.__c.__H=null),null!=(n=g({},n)).__c&&(n.__c.__P===e&&(n.__c.__P=t),n.__c=null),n.__k=n.__k&&n.__k.map(function(n){return L(n,t,e)})),n}function U(n,t,e){return n&&(n.__v=null,n.__k=n.__k&&n.__k.map(function(n){return U(n,t,e)}),n.__c&&n.__c.__P===t&&(n.__e&&e.insertBefore(n.__e,n.__d),n.__c.__e=!0,n.__c.__P=e)),n}function D(){this.__u=0,this.t=null,this.__b=null;}function F(n){var t=n.__.__c;return t&&t.__a&&t.__a(n)}function V(){this.u=null,this.o=null;}l$1.unmount=function(n){var t=n.__c;t&&t.__R&&t.__R(),t&&!0===n.__h&&(n.type=null),I&&I(n);},(D.prototype=new x$1).__c=function(n,t){var e=t.__c,r=this;null==r.t&&(r.t=[]),r.t.push(e);var u=F(r.__v),o=!1,i=function(){o||(o=!0,e.__R=null,u?u(l):l());};e.__R=i;var l=function(){if(!--r.__u){if(r.state.__a){var n=r.state.__a;r.__v.__k[0]=U(n,n.__c.__P,n.__c.__O);}var t;for(r.setState({__a:r.__b=null});t=r.t.pop();)t.forceUpdate();}},c=!0===t.__h;r.__u++||c||r.setState({__a:r.__b=r.__v.__k[0]}),n.then(i,i);},D.prototype.componentWillUnmount=function(){this.t=[];},D.prototype.render=function(n,e){if(this.__b){if(this.__v.__k){var r=document.createElement("div"),o=this.__v.__k[0].__c;this.__v.__k[0]=L(this.__b,r,o.__O=o.__P);}this.__b=null;}var i=e.__a&&y(_,null,n.fallback);return i&&(i.__h=null),[y(_,null,e.__a?null:n.children),i]};var W=function(n,t,e){if(++e[1]===e[0]&&n.o.delete(t),n.props.revealOrder&&("t"!==n.props.revealOrder[0]||!n.o.size))for(e=n.u;e;){for(;e.length>3;)e.pop()();if(e[1]<e[0])break;n.u=e=e[2];}};function P(n){return this.getChildContext=function(){return n.context},n.children}function $(n){var e=this,r=n.i;e.componentWillUnmount=function(){D$1(null,e.l),e.l=null,e.i=null;},e.i&&e.i!==r&&e.componentWillUnmount(),n.__v?(e.l||(e.i=r,e.l={nodeType:1,parentNode:r,childNodes:[],appendChild:function(n){this.childNodes.push(n),e.i.appendChild(n);},insertBefore:function(n,t){this.childNodes.push(n),e.i.appendChild(n);},removeChild:function(n){this.childNodes.splice(this.childNodes.indexOf(n)>>>1,1),e.i.removeChild(n);}}),D$1(y(P,{context:e.context},n.__v),e.l)):e.l&&e.componentWillUnmount();}function j(n,e){var r=y($,{__v:n,i:e});return r.containerInfo=e,r}(V.prototype=new x$1).__a=function(n){var t=this,e=F(t.__v),r=t.o.get(n);return r[0]++,function(u){var o=function(){t.props.revealOrder?(r.push(u),W(t,n,r)):u();};e?e(o):o();}},V.prototype.render=function(n){this.u=null,this.o=new Map;var t=j$2(n.children);n.revealOrder&&"b"===n.revealOrder[0]&&t.reverse();for(var e=t.length;e--;)this.o.set(t[e],this.u=[1,0,this.u]);return n.children},V.prototype.componentDidUpdate=V.prototype.componentDidMount=function(){var n=this;this.o.forEach(function(t,e){W(n,e,t);});};var z="undefined"!=typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103,B=/^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/,H="undefined"!=typeof document,Z=function(n){return ("undefined"!=typeof Symbol&&"symbol"==typeof Symbol()?/fil|che|rad/i:/fil|che|ra/i).test(n)};x$1.prototype.isReactComponent={},["componentWillMount","componentWillReceiveProps","componentWillUpdate"].forEach(function(t){Object.defineProperty(x$1.prototype,t,{configurable:!0,get:function(){return this["UNSAFE_"+t]},set:function(n){Object.defineProperty(this,t,{configurable:!0,writable:!0,value:n});}});});var G=l$1.event;function J(){}function K(){return this.cancelBubble}function Q(){return this.defaultPrevented}l$1.event=function(n){return G&&(n=G(n)),n.persist=J,n.isPropagationStopped=K,n.isDefaultPrevented=Q,n.nativeEvent=n};var nn={configurable:!0,get:function(){return this.class}},tn=l$1.vnode;l$1.vnode=function(n){var t=n.type,e=n.props,u=e;if("string"==typeof t){var o=-1===t.indexOf("-");for(var i in u={},e){var l=e[i];H&&"children"===i&&"noscript"===t||"value"===i&&"defaultValue"in e&&null==l||("defaultValue"===i&&"value"in e&&null==e.value?i="value":"download"===i&&!0===l?l="":/ondoubleclick/i.test(i)?i="ondblclick":/^onchange(textarea|input)/i.test(i+t)&&!Z(e.type)?i="oninput":/^onfocus$/i.test(i)?i="onfocusin":/^onblur$/i.test(i)?i="onfocusout":/^on(Ani|Tra|Tou|BeforeInp|Compo)/.test(i)?i=i.toLowerCase():o&&B.test(i)?i=i.replace(/[A-Z0-9]/g,"-$&").toLowerCase():null===l&&(l=void 0),/^oninput$/i.test(i)&&(i=i.toLowerCase(),u[i]&&(i="oninputCapture")),u[i]=l);}"select"==t&&u.multiple&&Array.isArray(u.value)&&(u.value=j$2(e.children).forEach(function(n){n.props.selected=-1!=u.value.indexOf(n.props.value);})),"select"==t&&null!=u.defaultValue&&(u.value=j$2(e.children).forEach(function(n){n.props.selected=u.multiple?-1!=u.defaultValue.indexOf(n.props.value):u.defaultValue==n.props.value;})),n.props=u,e.class!=e.className&&(nn.enumerable="className"in e,null!=e.className&&(u.class=e.className),Object.defineProperty(u,"className",nn));}n.$$typeof=z,tn&&tn(n);};var en=l$1.__r;l$1.__r=function(n){en&&en(n),n.__c;};
3032
3033 /*
3034 NOTE: this can be a public API, especially createElement for hooks.
3035 See examples/typescript-scheduler/src/index.ts
3036 */
3037 function flushSync(runBeforeFlush) {
3038 runBeforeFlush();
3039 let oldDebounceRendering = l$1.debounceRendering; // orig
3040 let callbackQ = [];
3041 function execCallbackSync(callback) {
3042 callbackQ.push(callback);
3043 }
3044 l$1.debounceRendering = execCallbackSync;
3045 D$1(y(FakeComponent, {}), document.createElement('div'));
3046 while (callbackQ.length) {
3047 callbackQ.shift()();
3048 }
3049 l$1.debounceRendering = oldDebounceRendering;
3050 }
3051 class FakeComponent extends x$1 {
3052 render() { return y('div', {}); }
3053 componentDidMount() { this.setState({}); }
3054 }
3055 // TODO: use preact/compat instead?
3056 function createContext(defaultValue) {
3057 let ContextType = G$1(defaultValue);
3058 let origProvider = ContextType.Provider;
3059 ContextType.Provider = function () {
3060 let isNew = !this.getChildContext;
3061 let children = origProvider.apply(this, arguments); // eslint-disable-line prefer-rest-params
3062 if (isNew) {
3063 let subs = [];
3064 this.shouldComponentUpdate = (_props) => {
3065 if (this.props.value !== _props.value) {
3066 subs.forEach((c) => {
3067 c.context = _props.value;
3068 c.forceUpdate();
3069 });
3070 }
3071 };
3072 this.sub = (c) => {
3073 subs.push(c);
3074 let old = c.componentWillUnmount;
3075 c.componentWillUnmount = () => {
3076 subs.splice(subs.indexOf(c), 1);
3077 old && old.call(c);
3078 };
3079 };
3080 }
3081 return children;
3082 };
3083 return ContextType;
3084 }
3085
3086 var preact = {
3087 __proto__: null,
3088 flushSync: flushSync,
3089 createContext: createContext,
3090 createPortal: j,
3091 Component: x$1,
3092 Fragment: _,
3093 cloneElement: F$1,
3094 createElement: y,
3095 createRef: d,
3096 h: y,
3097 hydrate: E,
3098 get isValidElement () { return i$1; },
3099 get options () { return l$1; },
3100 render: D$1,
3101 toChildArray: j$2
3102 };
3103
3104 class ScrollResponder {
3105 constructor(execFunc, emitter, scrollTime, scrollTimeReset) {
3106 this.execFunc = execFunc;
3107 this.emitter = emitter;
3108 this.scrollTime = scrollTime;
3109 this.scrollTimeReset = scrollTimeReset;
3110 this.handleScrollRequest = (request) => {
3111 this.queuedRequest = Object.assign({}, this.queuedRequest || {}, request);
3112 this.drain();
3113 };
3114 emitter.on('_scrollRequest', this.handleScrollRequest);
3115 this.fireInitialScroll();
3116 }
3117 detach() {
3118 this.emitter.off('_scrollRequest', this.handleScrollRequest);
3119 }
3120 update(isDatesNew) {
3121 if (isDatesNew && this.scrollTimeReset) {
3122 this.fireInitialScroll(); // will drain
3123 }
3124 else {
3125 this.drain();
3126 }
3127 }
3128 fireInitialScroll() {
3129 this.handleScrollRequest({
3130 time: this.scrollTime,
3131 });
3132 }
3133 drain() {
3134 if (this.queuedRequest && this.execFunc(this.queuedRequest)) {
3135 this.queuedRequest = null;
3136 }
3137 }
3138 }
3139
3140 const ViewContextType = createContext({}); // for Components
3141 function buildViewContext(viewSpec, viewApi, viewOptions, dateProfileGenerator, dateEnv, theme, pluginHooks, dispatch, getCurrentData, emitter, calendarApi, registerInteractiveComponent, unregisterInteractiveComponent) {
3142 return {
3143 dateEnv,
3144 options: viewOptions,
3145 pluginHooks,
3146 emitter,
3147 dispatch,
3148 getCurrentData,
3149 calendarApi,
3150 viewSpec,
3151 viewApi,
3152 dateProfileGenerator,
3153 theme,
3154 isRtl: viewOptions.direction === 'rtl',
3155 addResizeHandler(handler) {
3156 emitter.on('_resize', handler);
3157 },
3158 removeResizeHandler(handler) {
3159 emitter.off('_resize', handler);
3160 },
3161 createScrollResponder(execFunc) {
3162 return new ScrollResponder(execFunc, emitter, createDuration(viewOptions.scrollTime), viewOptions.scrollTimeReset);
3163 },
3164 registerInteractiveComponent,
3165 unregisterInteractiveComponent,
3166 };
3167 }
3168
3169 /* eslint max-classes-per-file: off */
3170 class PureComponent extends x$1 {
3171 shouldComponentUpdate(nextProps, nextState) {
3172 if (this.debug) {
3173 // eslint-disable-next-line no-console
3174 console.log(getUnequalProps(nextProps, this.props), getUnequalProps(nextState, this.state));
3175 }
3176 return !compareObjs(this.props, nextProps, this.propEquality) ||
3177 !compareObjs(this.state, nextState, this.stateEquality);
3178 }
3179 // HACK for freakin' React StrictMode
3180 safeSetState(newState) {
3181 if (!compareObjs(this.state, Object.assign(Object.assign({}, this.state), newState), this.stateEquality)) {
3182 this.setState(newState);
3183 }
3184 }
3185 }
3186 PureComponent.addPropsEquality = addPropsEquality;
3187 PureComponent.addStateEquality = addStateEquality;
3188 PureComponent.contextType = ViewContextType;
3189 PureComponent.prototype.propEquality = {};
3190 PureComponent.prototype.stateEquality = {};
3191 class BaseComponent extends PureComponent {
3192 }
3193 BaseComponent.contextType = ViewContextType;
3194 function addPropsEquality(propEquality) {
3195 let hash = Object.create(this.prototype.propEquality);
3196 Object.assign(hash, propEquality);
3197 this.prototype.propEquality = hash;
3198 }
3199 function addStateEquality(stateEquality) {
3200 let hash = Object.create(this.prototype.stateEquality);
3201 Object.assign(hash, stateEquality);
3202 this.prototype.stateEquality = hash;
3203 }
3204 // use other one
3205 function setRef(ref, current) {
3206 if (typeof ref === 'function') {
3207 ref(current);
3208 }
3209 else if (ref) {
3210 // see https://github.com/facebook/react/issues/13029
3211 ref.current = current;
3212 }
3213 }
3214
3215 /*
3216 an INTERACTABLE date component
3217
3218 PURPOSES:
3219 - hook up to fg, fill, and mirror renderers
3220 - interface for dragging and hits
3221 */
3222 class DateComponent extends BaseComponent {
3223 constructor() {
3224 super(...arguments);
3225 this.uid = guid();
3226 }
3227 // Hit System
3228 // -----------------------------------------------------------------------------------------------------------------
3229 prepareHits() {
3230 }
3231 queryHit(positionLeft, positionTop, elWidth, elHeight) {
3232 return null; // this should be abstract
3233 }
3234 // Pointer Interaction Utils
3235 // -----------------------------------------------------------------------------------------------------------------
3236 isValidSegDownEl(el) {
3237 return !this.props.eventDrag && // HACK
3238 !this.props.eventResize && // HACK
3239 !elementClosest(el, '.fc-event-mirror');
3240 }
3241 isValidDateDownEl(el) {
3242 return !elementClosest(el, '.fc-event:not(.fc-bg-event)') &&
3243 !elementClosest(el, '.fc-more-link') && // a "more.." link
3244 !elementClosest(el, 'a[data-navlink]') && // a clickable nav link
3245 !elementClosest(el, '.fc-popover'); // hack
3246 }
3247 }
3248
3249 function reduceCurrentDate(currentDate, action) {
3250 switch (action.type) {
3251 case 'CHANGE_DATE':
3252 return action.dateMarker;
3253 default:
3254 return currentDate;
3255 }
3256 }
3257 function getInitialDate(options, dateEnv) {
3258 let initialDateInput = options.initialDate;
3259 // compute the initial ambig-timezone date
3260 if (initialDateInput != null) {
3261 return dateEnv.createMarker(initialDateInput);
3262 }
3263 return getNow(options.now, dateEnv); // getNow already returns unzoned
3264 }
3265 function getNow(nowInput, dateEnv) {
3266 if (typeof nowInput === 'function') {
3267 nowInput = nowInput();
3268 }
3269 if (nowInput == null) {
3270 return dateEnv.createNowMarker();
3271 }
3272 return dateEnv.createMarker(nowInput);
3273 }
3274
3275 class DateProfileGenerator {
3276 constructor(props) {
3277 this.props = props;
3278 this.nowDate = getNow(props.nowInput, props.dateEnv);
3279 this.initHiddenDays();
3280 }
3281 /* Date Range Computation
3282 ------------------------------------------------------------------------------------------------------------------*/
3283 // Builds a structure with info about what the dates/ranges will be for the "prev" view.
3284 buildPrev(currentDateProfile, currentDate, forceToValid) {
3285 let { dateEnv } = this.props;
3286 let prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
3287 currentDateProfile.dateIncrement);
3288 return this.build(prevDate, -1, forceToValid);
3289 }
3290 // Builds a structure with info about what the dates/ranges will be for the "next" view.
3291 buildNext(currentDateProfile, currentDate, forceToValid) {
3292 let { dateEnv } = this.props;
3293 let nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
3294 currentDateProfile.dateIncrement);
3295 return this.build(nextDate, 1, forceToValid);
3296 }
3297 // Builds a structure holding dates/ranges for rendering around the given date.
3298 // Optional direction param indicates whether the date is being incremented/decremented
3299 // from its previous value. decremented = -1, incremented = 1 (default).
3300 build(currentDate, direction, forceToValid = true) {
3301 let { props } = this;
3302 let validRange;
3303 let currentInfo;
3304 let isRangeAllDay;
3305 let renderRange;
3306 let activeRange;
3307 let isValid;
3308 validRange = this.buildValidRange();
3309 validRange = this.trimHiddenDays(validRange);
3310 if (forceToValid) {
3311 currentDate = constrainMarkerToRange(currentDate, validRange);
3312 }
3313 currentInfo = this.buildCurrentRangeInfo(currentDate, direction);
3314 isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit);
3315 renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay);
3316 renderRange = this.trimHiddenDays(renderRange);
3317 activeRange = renderRange;
3318 if (!props.showNonCurrentDates) {
3319 activeRange = intersectRanges(activeRange, currentInfo.range);
3320 }
3321 activeRange = this.adjustActiveRange(activeRange);
3322 activeRange = intersectRanges(activeRange, validRange); // might return null
3323 // it's invalid if the originally requested date is not contained,
3324 // or if the range is completely outside of the valid range.
3325 isValid = rangesIntersect(currentInfo.range, validRange);
3326 // HACK: constrain to render-range so `currentDate` is more useful to view rendering
3327 if (!rangeContainsMarker(renderRange, currentDate)) {
3328 currentDate = renderRange.start;
3329 }
3330 return {
3331 currentDate,
3332 // constraint for where prev/next operations can go and where events can be dragged/resized to.
3333 // an object with optional start and end properties.
3334 validRange,
3335 // range the view is formally responsible for.
3336 // for example, a month view might have 1st-31st, excluding padded dates
3337 currentRange: currentInfo.range,
3338 // name of largest unit being displayed, like "month" or "week"
3339 currentRangeUnit: currentInfo.unit,
3340 isRangeAllDay,
3341 // dates that display events and accept drag-n-drop
3342 // will be `null` if no dates accept events
3343 activeRange,
3344 // date range with a rendered skeleton
3345 // includes not-active days that need some sort of DOM
3346 renderRange,
3347 // Duration object that denotes the first visible time of any given day
3348 slotMinTime: props.slotMinTime,
3349 // Duration object that denotes the exclusive visible end time of any given day
3350 slotMaxTime: props.slotMaxTime,
3351 isValid,
3352 // how far the current date will move for a prev/next operation
3353 dateIncrement: this.buildDateIncrement(currentInfo.duration),
3354 // pass a fallback (might be null) ^
3355 };
3356 }
3357 // Builds an object with optional start/end properties.
3358 // Indicates the minimum/maximum dates to display.
3359 // not responsible for trimming hidden days.
3360 buildValidRange() {
3361 let input = this.props.validRangeInput;
3362 let simpleInput = typeof input === 'function'
3363 ? input.call(this.props.calendarApi, this.nowDate)
3364 : input;
3365 return this.refineRange(simpleInput) ||
3366 { start: null, end: null }; // completely open-ended
3367 }
3368 // Builds a structure with info about the "current" range, the range that is
3369 // highlighted as being the current month for example.
3370 // See build() for a description of `direction`.
3371 // Guaranteed to have `range` and `unit` properties. `duration` is optional.
3372 buildCurrentRangeInfo(date, direction) {
3373 let { props } = this;
3374 let duration = null;
3375 let unit = null;
3376 let range = null;
3377 let dayCount;
3378 if (props.duration) {
3379 duration = props.duration;
3380 unit = props.durationUnit;
3381 range = this.buildRangeFromDuration(date, direction, duration, unit);
3382 }
3383 else if ((dayCount = this.props.dayCount)) {
3384 unit = 'day';
3385 range = this.buildRangeFromDayCount(date, direction, dayCount);
3386 }
3387 else if ((range = this.buildCustomVisibleRange(date))) {
3388 unit = props.dateEnv.greatestWholeUnit(range.start, range.end).unit;
3389 }
3390 else {
3391 duration = this.getFallbackDuration();
3392 unit = greatestDurationDenominator(duration).unit;
3393 range = this.buildRangeFromDuration(date, direction, duration, unit);
3394 }
3395 return { duration, unit, range };
3396 }
3397 getFallbackDuration() {
3398 return createDuration({ day: 1 });
3399 }
3400 // Returns a new activeRange to have time values (un-ambiguate)
3401 // slotMinTime or slotMaxTime causes the range to expand.
3402 adjustActiveRange(range) {
3403 let { dateEnv, usesMinMaxTime, slotMinTime, slotMaxTime } = this.props;
3404 let { start, end } = range;
3405 if (usesMinMaxTime) {
3406 // expand active range if slotMinTime is negative (why not when positive?)
3407 if (asRoughDays(slotMinTime) < 0) {
3408 start = startOfDay(start); // necessary?
3409 start = dateEnv.add(start, slotMinTime);
3410 }
3411 // expand active range if slotMaxTime is beyond one day (why not when negative?)
3412 if (asRoughDays(slotMaxTime) > 1) {
3413 end = startOfDay(end); // necessary?
3414 end = addDays(end, -1);
3415 end = dateEnv.add(end, slotMaxTime);
3416 }
3417 }
3418 return { start, end };
3419 }
3420 // Builds the "current" range when it is specified as an explicit duration.
3421 // `unit` is the already-computed greatestDurationDenominator unit of duration.
3422 buildRangeFromDuration(date, direction, duration, unit) {
3423 let { dateEnv, dateAlignment } = this.props;
3424 let start;
3425 let end;
3426 let res;
3427 // compute what the alignment should be
3428 if (!dateAlignment) {
3429 let { dateIncrement } = this.props;
3430 if (dateIncrement) {
3431 // use the smaller of the two units
3432 if (asRoughMs(dateIncrement) < asRoughMs(duration)) {
3433 dateAlignment = greatestDurationDenominator(dateIncrement).unit;
3434 }
3435 else {
3436 dateAlignment = unit;
3437 }
3438 }
3439 else {
3440 dateAlignment = unit;
3441 }
3442 }
3443 // if the view displays a single day or smaller
3444 if (asRoughDays(duration) <= 1) {
3445 if (this.isHiddenDay(start)) {
3446 start = this.skipHiddenDays(start, direction);
3447 start = startOfDay(start);
3448 }
3449 }
3450 function computeRes() {
3451 start = dateEnv.startOf(date, dateAlignment);
3452 end = dateEnv.add(start, duration);
3453 res = { start, end };
3454 }
3455 computeRes();
3456 // if range is completely enveloped by hidden days, go past the hidden days
3457 if (!this.trimHiddenDays(res)) {
3458 date = this.skipHiddenDays(date, direction);
3459 computeRes();
3460 }
3461 return res;
3462 }
3463 // Builds the "current" range when a dayCount is specified.
3464 buildRangeFromDayCount(date, direction, dayCount) {
3465 let { dateEnv, dateAlignment } = this.props;
3466 let runningCount = 0;
3467 let start = date;
3468 let end;
3469 if (dateAlignment) {
3470 start = dateEnv.startOf(start, dateAlignment);
3471 }
3472 start = startOfDay(start);
3473 start = this.skipHiddenDays(start, direction);
3474 end = start;
3475 do {
3476 end = addDays(end, 1);
3477 if (!this.isHiddenDay(end)) {
3478 runningCount += 1;
3479 }
3480 } while (runningCount < dayCount);
3481 return { start, end };
3482 }
3483 // Builds a normalized range object for the "visible" range,
3484 // which is a way to define the currentRange and activeRange at the same time.
3485 buildCustomVisibleRange(date) {
3486 let { props } = this;
3487 let input = props.visibleRangeInput;
3488 let simpleInput = typeof input === 'function'
3489 ? input.call(props.calendarApi, props.dateEnv.toDate(date))
3490 : input;
3491 let range = this.refineRange(simpleInput);
3492 if (range && (range.start == null || range.end == null)) {
3493 return null;
3494 }
3495 return range;
3496 }
3497 // Computes the range that will represent the element/cells for *rendering*,
3498 // but which may have voided days/times.
3499 // not responsible for trimming hidden days.
3500 buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
3501 return currentRange;
3502 }
3503 // Compute the duration value that should be added/substracted to the current date
3504 // when a prev/next operation happens.
3505 buildDateIncrement(fallback) {
3506 let { dateIncrement } = this.props;
3507 let customAlignment;
3508 if (dateIncrement) {
3509 return dateIncrement;
3510 }
3511 if ((customAlignment = this.props.dateAlignment)) {
3512 return createDuration(1, customAlignment);
3513 }
3514 if (fallback) {
3515 return fallback;
3516 }
3517 return createDuration({ days: 1 });
3518 }
3519 refineRange(rangeInput) {
3520 if (rangeInput) {
3521 let range = parseRange(rangeInput, this.props.dateEnv);
3522 if (range) {
3523 range = computeVisibleDayRange(range);
3524 }
3525 return range;
3526 }
3527 return null;
3528 }
3529 /* Hidden Days
3530 ------------------------------------------------------------------------------------------------------------------*/
3531 // Initializes internal variables related to calculating hidden days-of-week
3532 initHiddenDays() {
3533 let hiddenDays = this.props.hiddenDays || []; // array of day-of-week indices that are hidden
3534 let isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
3535 let dayCnt = 0;
3536 let i;
3537 if (this.props.weekends === false) {
3538 hiddenDays.push(0, 6); // 0=sunday, 6=saturday
3539 }
3540 for (i = 0; i < 7; i += 1) {
3541 if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) {
3542 dayCnt += 1;
3543 }
3544 }
3545 if (!dayCnt) {
3546 throw new Error('invalid hiddenDays'); // all days were hidden? bad.
3547 }
3548 this.isHiddenDayHash = isHiddenDayHash;
3549 }
3550 // Remove days from the beginning and end of the range that are computed as hidden.
3551 // If the whole range is trimmed off, returns null
3552 trimHiddenDays(range) {
3553 let { start, end } = range;
3554 if (start) {
3555 start = this.skipHiddenDays(start);
3556 }
3557 if (end) {
3558 end = this.skipHiddenDays(end, -1, true);
3559 }
3560 if (start == null || end == null || start < end) {
3561 return { start, end };
3562 }
3563 return null;
3564 }
3565 // Is the current day hidden?
3566 // `day` is a day-of-week index (0-6), or a Date (used for UTC)
3567 isHiddenDay(day) {
3568 if (day instanceof Date) {
3569 day = day.getUTCDay();
3570 }
3571 return this.isHiddenDayHash[day];
3572 }
3573 // Incrementing the current day until it is no longer a hidden day, returning a copy.
3574 // DOES NOT CONSIDER validRange!
3575 // If the initial value of `date` is not a hidden day, don't do anything.
3576 // Pass `isExclusive` as `true` if you are dealing with an end date.
3577 // `inc` defaults to `1` (increment one day forward each time)
3578 skipHiddenDays(date, inc = 1, isExclusive = false) {
3579 while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) {
3580 date = addDays(date, inc);
3581 }
3582 return date;
3583 }
3584 }
3585
3586 function triggerDateSelect(selection, pev, context) {
3587 context.emitter.trigger('select', Object.assign(Object.assign({}, buildDateSpanApiWithContext(selection, context)), { jsEvent: pev ? pev.origEvent : null, view: context.viewApi || context.calendarApi.view }));
3588 }
3589 function triggerDateUnselect(pev, context) {
3590 context.emitter.trigger('unselect', {
3591 jsEvent: pev ? pev.origEvent : null,
3592 view: context.viewApi || context.calendarApi.view,
3593 });
3594 }
3595 function buildDateSpanApiWithContext(dateSpan, context) {
3596 let props = {};
3597 for (let transform of context.pluginHooks.dateSpanTransforms) {
3598 Object.assign(props, transform(dateSpan, context));
3599 }
3600 Object.assign(props, buildDateSpanApi(dateSpan, context.dateEnv));
3601 return props;
3602 }
3603 // Given an event's allDay status and start date, return what its fallback end date should be.
3604 // TODO: rename to computeDefaultEventEnd
3605 function getDefaultEventEnd(allDay, marker, context) {
3606 let { dateEnv, options } = context;
3607 let end = marker;
3608 if (allDay) {
3609 end = startOfDay(end);
3610 end = dateEnv.add(end, options.defaultAllDayEventDuration);
3611 }
3612 else {
3613 end = dateEnv.add(end, options.defaultTimedEventDuration);
3614 }
3615 return end;
3616 }
3617
3618 // applies the mutation to ALL defs/instances within the event store
3619 function applyMutationToEventStore(eventStore, eventConfigBase, mutation, context) {
3620 let eventConfigs = compileEventUis(eventStore.defs, eventConfigBase);
3621 let dest = createEmptyEventStore();
3622 for (let defId in eventStore.defs) {
3623 let def = eventStore.defs[defId];
3624 dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, context);
3625 }
3626 for (let instanceId in eventStore.instances) {
3627 let instance = eventStore.instances[instanceId];
3628 let def = dest.defs[instance.defId]; // important to grab the newly modified def
3629 dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, context);
3630 }
3631 return dest;
3632 }
3633 function applyMutationToEventDef(eventDef, eventConfig, mutation, context) {
3634 let standardProps = mutation.standardProps || {};
3635 // if hasEnd has not been specified, guess a good value based on deltas.
3636 // if duration will change, there's no way the default duration will persist,
3637 // and thus, we need to mark the event as having a real end
3638 if (standardProps.hasEnd == null &&
3639 eventConfig.durationEditable &&
3640 (mutation.startDelta || mutation.endDelta)) {
3641 standardProps.hasEnd = true; // TODO: is this mutation okay?
3642 }
3643 let copy = Object.assign(Object.assign(Object.assign({}, eventDef), standardProps), { ui: Object.assign(Object.assign({}, eventDef.ui), standardProps.ui) });
3644 if (mutation.extendedProps) {
3645 copy.extendedProps = Object.assign(Object.assign({}, copy.extendedProps), mutation.extendedProps);
3646 }
3647 for (let applier of context.pluginHooks.eventDefMutationAppliers) {
3648 applier(copy, mutation, context);
3649 }
3650 if (!copy.hasEnd && context.options.forceEventDuration) {
3651 copy.hasEnd = true;
3652 }
3653 return copy;
3654 }
3655 function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef
3656 eventConfig, mutation, context) {
3657 let { dateEnv } = context;
3658 let forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true;
3659 let clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false;
3660 let copy = Object.assign({}, eventInstance);
3661 if (forceAllDay) {
3662 copy.range = computeAlignedDayRange(copy.range);
3663 }
3664 if (mutation.datesDelta && eventConfig.startEditable) {
3665 copy.range = {
3666 start: dateEnv.add(copy.range.start, mutation.datesDelta),
3667 end: dateEnv.add(copy.range.end, mutation.datesDelta),
3668 };
3669 }
3670 if (mutation.startDelta && eventConfig.durationEditable) {
3671 copy.range = {
3672 start: dateEnv.add(copy.range.start, mutation.startDelta),
3673 end: copy.range.end,
3674 };
3675 }
3676 if (mutation.endDelta && eventConfig.durationEditable) {
3677 copy.range = {
3678 start: copy.range.start,
3679 end: dateEnv.add(copy.range.end, mutation.endDelta),
3680 };
3681 }
3682 if (clearEnd) {
3683 copy.range = {
3684 start: copy.range.start,
3685 end: getDefaultEventEnd(eventDef.allDay, copy.range.start, context),
3686 };
3687 }
3688 // in case event was all-day but the supplied deltas were not
3689 // better util for this?
3690 if (eventDef.allDay) {
3691 copy.range = {
3692 start: startOfDay(copy.range.start),
3693 end: startOfDay(copy.range.end),
3694 };
3695 }
3696 // handle invalid durations
3697 if (copy.range.end < copy.range.start) {
3698 copy.range.end = getDefaultEventEnd(eventDef.allDay, copy.range.start, context);
3699 }
3700 return copy;
3701 }
3702
3703 class EventSourceImpl {
3704 constructor(context, internalEventSource) {
3705 this.context = context;
3706 this.internalEventSource = internalEventSource;
3707 }
3708 remove() {
3709 this.context.dispatch({
3710 type: 'REMOVE_EVENT_SOURCE',
3711 sourceId: this.internalEventSource.sourceId,
3712 });
3713 }
3714 refetch() {
3715 this.context.dispatch({
3716 type: 'FETCH_EVENT_SOURCES',
3717 sourceIds: [this.internalEventSource.sourceId],
3718 isRefetch: true,
3719 });
3720 }
3721 get id() {
3722 return this.internalEventSource.publicId;
3723 }
3724 get url() {
3725 return this.internalEventSource.meta.url;
3726 }
3727 get format() {
3728 return this.internalEventSource.meta.format; // TODO: bad. not guaranteed
3729 }
3730 }
3731
3732 class EventImpl {
3733 // instance will be null if expressing a recurring event that has no current instances,
3734 // OR if trying to validate an incoming external event that has no dates assigned
3735 constructor(context, def, instance) {
3736 this._context = context;
3737 this._def = def;
3738 this._instance = instance || null;
3739 }
3740 /*
3741 TODO: make event struct more responsible for this
3742 */
3743 setProp(name, val) {
3744 if (name in EVENT_DATE_REFINERS) {
3745 console.warn('Could not set date-related prop \'name\'. Use one of the date-related methods instead.');
3746 // TODO: make proper aliasing system?
3747 }
3748 else if (name === 'id') {
3749 val = EVENT_NON_DATE_REFINERS[name](val);
3750 this.mutate({
3751 standardProps: { publicId: val }, // hardcoded internal name
3752 });
3753 }
3754 else if (name in EVENT_NON_DATE_REFINERS) {
3755 val = EVENT_NON_DATE_REFINERS[name](val);
3756 this.mutate({
3757 standardProps: { [name]: val },
3758 });
3759 }
3760 else if (name in EVENT_UI_REFINERS) {
3761 let ui = EVENT_UI_REFINERS[name](val);
3762 if (name === 'color') {
3763 ui = { backgroundColor: val, borderColor: val };
3764 }
3765 else if (name === 'editable') {
3766 ui = { startEditable: val, durationEditable: val };
3767 }
3768 else {
3769 ui = { [name]: val };
3770 }
3771 this.mutate({
3772 standardProps: { ui },
3773 });
3774 }
3775 else {
3776 console.warn(`Could not set prop '${name}'. Use setExtendedProp instead.`);
3777 }
3778 }
3779 setExtendedProp(name, val) {
3780 this.mutate({
3781 extendedProps: { [name]: val },
3782 });
3783 }
3784 setStart(startInput, options = {}) {
3785 let { dateEnv } = this._context;
3786 let start = dateEnv.createMarker(startInput);
3787 if (start && this._instance) { // TODO: warning if parsed bad
3788 let instanceRange = this._instance.range;
3789 let startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!?
3790 if (options.maintainDuration) {
3791 this.mutate({ datesDelta: startDelta });
3792 }
3793 else {
3794 this.mutate({ startDelta });
3795 }
3796 }
3797 }
3798 setEnd(endInput, options = {}) {
3799 let { dateEnv } = this._context;
3800 let end;
3801 if (endInput != null) {
3802 end = dateEnv.createMarker(endInput);
3803 if (!end) {
3804 return; // TODO: warning if parsed bad
3805 }
3806 }
3807 if (this._instance) {
3808 if (end) {
3809 let endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity);
3810 this.mutate({ endDelta });
3811 }
3812 else {
3813 this.mutate({ standardProps: { hasEnd: false } });
3814 }
3815 }
3816 }
3817 setDates(startInput, endInput, options = {}) {
3818 let { dateEnv } = this._context;
3819 let standardProps = { allDay: options.allDay };
3820 let start = dateEnv.createMarker(startInput);
3821 let end;
3822 if (!start) {
3823 return; // TODO: warning if parsed bad
3824 }
3825 if (endInput != null) {
3826 end = dateEnv.createMarker(endInput);
3827 if (!end) { // TODO: warning if parsed bad
3828 return;
3829 }
3830 }
3831 if (this._instance) {
3832 let instanceRange = this._instance.range;
3833 // when computing the diff for an event being converted to all-day,
3834 // compute diff off of the all-day values the way event-mutation does.
3835 if (options.allDay === true) {
3836 instanceRange = computeAlignedDayRange(instanceRange);
3837 }
3838 let startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity);
3839 if (end) {
3840 let endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity);
3841 if (durationsEqual(startDelta, endDelta)) {
3842 this.mutate({ datesDelta: startDelta, standardProps });
3843 }
3844 else {
3845 this.mutate({ startDelta, endDelta, standardProps });
3846 }
3847 }
3848 else { // means "clear the end"
3849 standardProps.hasEnd = false;
3850 this.mutate({ datesDelta: startDelta, standardProps });
3851 }
3852 }
3853 }
3854 moveStart(deltaInput) {
3855 let delta = createDuration(deltaInput);
3856 if (delta) { // TODO: warning if parsed bad
3857 this.mutate({ startDelta: delta });
3858 }
3859 }
3860 moveEnd(deltaInput) {
3861 let delta = createDuration(deltaInput);
3862 if (delta) { // TODO: warning if parsed bad
3863 this.mutate({ endDelta: delta });
3864 }
3865 }
3866 moveDates(deltaInput) {
3867 let delta = createDuration(deltaInput);
3868 if (delta) { // TODO: warning if parsed bad
3869 this.mutate({ datesDelta: delta });
3870 }
3871 }
3872 setAllDay(allDay, options = {}) {
3873 let standardProps = { allDay };
3874 let { maintainDuration } = options;
3875 if (maintainDuration == null) {
3876 maintainDuration = this._context.options.allDayMaintainDuration;
3877 }
3878 if (this._def.allDay !== allDay) {
3879 standardProps.hasEnd = maintainDuration;
3880 }
3881 this.mutate({ standardProps });
3882 }
3883 formatRange(formatInput) {
3884 let { dateEnv } = this._context;
3885 let instance = this._instance;
3886 let formatter = createFormatter(formatInput);
3887 if (this._def.hasEnd) {
3888 return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, {
3889 forcedStartTzo: instance.forcedStartTzo,
3890 forcedEndTzo: instance.forcedEndTzo,
3891 });
3892 }
3893 return dateEnv.format(instance.range.start, formatter, {
3894 forcedTzo: instance.forcedStartTzo,
3895 });
3896 }
3897 mutate(mutation) {
3898 let instance = this._instance;
3899 if (instance) {
3900 let def = this._def;
3901 let context = this._context;
3902 let { eventStore } = context.getCurrentData();
3903 let relevantEvents = getRelevantEvents(eventStore, instance.instanceId);
3904 let eventConfigBase = {
3905 '': {
3906 display: '',
3907 startEditable: true,
3908 durationEditable: true,
3909 constraints: [],
3910 overlap: null,
3911 allows: [],
3912 backgroundColor: '',
3913 borderColor: '',
3914 textColor: '',
3915 classNames: [],
3916 },
3917 };
3918 relevantEvents = applyMutationToEventStore(relevantEvents, eventConfigBase, mutation, context);
3919 let oldEvent = new EventImpl(context, def, instance); // snapshot
3920 this._def = relevantEvents.defs[def.defId];
3921 this._instance = relevantEvents.instances[instance.instanceId];
3922 context.dispatch({
3923 type: 'MERGE_EVENTS',
3924 eventStore: relevantEvents,
3925 });
3926 context.emitter.trigger('eventChange', {
3927 oldEvent,
3928 event: this,
3929 relatedEvents: buildEventApis(relevantEvents, context, instance),
3930 revert() {
3931 context.dispatch({
3932 type: 'RESET_EVENTS',
3933 eventStore, // the ORIGINAL store
3934 });
3935 },
3936 });
3937 }
3938 }
3939 remove() {
3940 let context = this._context;
3941 let asStore = eventApiToStore(this);
3942 context.dispatch({
3943 type: 'REMOVE_EVENTS',
3944 eventStore: asStore,
3945 });
3946 context.emitter.trigger('eventRemove', {
3947 event: this,
3948 relatedEvents: [],
3949 revert() {
3950 context.dispatch({
3951 type: 'MERGE_EVENTS',
3952 eventStore: asStore,
3953 });
3954 },
3955 });
3956 }
3957 get source() {
3958 let { sourceId } = this._def;
3959 if (sourceId) {
3960 return new EventSourceImpl(this._context, this._context.getCurrentData().eventSources[sourceId]);
3961 }
3962 return null;
3963 }
3964 get start() {
3965 return this._instance ?
3966 this._context.dateEnv.toDate(this._instance.range.start) :
3967 null;
3968 }
3969 get end() {
3970 return (this._instance && this._def.hasEnd) ?
3971 this._context.dateEnv.toDate(this._instance.range.end) :
3972 null;
3973 }
3974 get startStr() {
3975 let instance = this._instance;
3976 if (instance) {
3977 return this._context.dateEnv.formatIso(instance.range.start, {
3978 omitTime: this._def.allDay,
3979 forcedTzo: instance.forcedStartTzo,
3980 });
3981 }
3982 return '';
3983 }
3984 get endStr() {
3985 let instance = this._instance;
3986 if (instance && this._def.hasEnd) {
3987 return this._context.dateEnv.formatIso(instance.range.end, {
3988 omitTime: this._def.allDay,
3989 forcedTzo: instance.forcedEndTzo,
3990 });
3991 }
3992 return '';
3993 }
3994 // computable props that all access the def
3995 // TODO: find a TypeScript-compatible way to do this at scale
3996 get id() { return this._def.publicId; }
3997 get groupId() { return this._def.groupId; }
3998 get allDay() { return this._def.allDay; }
3999 get title() { return this._def.title; }
4000 get url() { return this._def.url; }
4001 get display() { return this._def.ui.display || 'auto'; } // bad. just normalize the type earlier
4002 get startEditable() { return this._def.ui.startEditable; }
4003 get durationEditable() { return this._def.ui.durationEditable; }
4004 get constraint() { return this._def.ui.constraints[0] || null; }
4005 get overlap() { return this._def.ui.overlap; }
4006 get allow() { return this._def.ui.allows[0] || null; }
4007 get backgroundColor() { return this._def.ui.backgroundColor; }
4008 get borderColor() { return this._def.ui.borderColor; }
4009 get textColor() { return this._def.ui.textColor; }
4010 // NOTE: user can't modify these because Object.freeze was called in event-def parsing
4011 get classNames() { return this._def.ui.classNames; }
4012 get extendedProps() { return this._def.extendedProps; }
4013 toPlainObject(settings = {}) {
4014 let def = this._def;
4015 let { ui } = def;
4016 let { startStr, endStr } = this;
4017 let res = {
4018 allDay: def.allDay,
4019 };
4020 if (def.title) {
4021 res.title = def.title;
4022 }
4023 if (startStr) {
4024 res.start = startStr;
4025 }
4026 if (endStr) {
4027 res.end = endStr;
4028 }
4029 if (def.publicId) {
4030 res.id = def.publicId;
4031 }
4032 if (def.groupId) {
4033 res.groupId = def.groupId;
4034 }
4035 if (def.url) {
4036 res.url = def.url;
4037 }
4038 if (ui.display && ui.display !== 'auto') {
4039 res.display = ui.display;
4040 }
4041 // TODO: what about recurring-event properties???
4042 // TODO: include startEditable/durationEditable/constraint/overlap/allow
4043 if (settings.collapseColor && ui.backgroundColor && ui.backgroundColor === ui.borderColor) {
4044 res.color = ui.backgroundColor;
4045 }
4046 else {
4047 if (ui.backgroundColor) {
4048 res.backgroundColor = ui.backgroundColor;
4049 }
4050 if (ui.borderColor) {
4051 res.borderColor = ui.borderColor;
4052 }
4053 }
4054 if (ui.textColor) {
4055 res.textColor = ui.textColor;
4056 }
4057 if (ui.classNames.length) {
4058 res.classNames = ui.classNames;
4059 }
4060 if (Object.keys(def.extendedProps).length) {
4061 if (settings.collapseExtendedProps) {
4062 Object.assign(res, def.extendedProps);
4063 }
4064 else {
4065 res.extendedProps = def.extendedProps;
4066 }
4067 }
4068 return res;
4069 }
4070 toJSON() {
4071 return this.toPlainObject();
4072 }
4073 }
4074 function eventApiToStore(eventApi) {
4075 let def = eventApi._def;
4076 let instance = eventApi._instance;
4077 return {
4078 defs: { [def.defId]: def },
4079 instances: instance
4080 ? { [instance.instanceId]: instance }
4081 : {},
4082 };
4083 }
4084 function buildEventApis(eventStore, context, excludeInstance) {
4085 let { defs, instances } = eventStore;
4086 let eventApis = [];
4087 let excludeInstanceId = excludeInstance ? excludeInstance.instanceId : '';
4088 for (let id in instances) {
4089 let instance = instances[id];
4090 let def = defs[instance.defId];
4091 if (instance.instanceId !== excludeInstanceId) {
4092 eventApis.push(new EventImpl(context, def, instance));
4093 }
4094 }
4095 return eventApis;
4096 }
4097
4098 /*
4099 Specifying nextDayThreshold signals that all-day ranges should be sliced.
4100 */
4101 function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) {
4102 let inverseBgByGroupId = {};
4103 let inverseBgByDefId = {};
4104 let defByGroupId = {};
4105 let bgRanges = [];
4106 let fgRanges = [];
4107 let eventUis = compileEventUis(eventStore.defs, eventUiBases);
4108 for (let defId in eventStore.defs) {
4109 let def = eventStore.defs[defId];
4110 let ui = eventUis[def.defId];
4111 if (ui.display === 'inverse-background') {
4112 if (def.groupId) {
4113 inverseBgByGroupId[def.groupId] = [];
4114 if (!defByGroupId[def.groupId]) {
4115 defByGroupId[def.groupId] = def;
4116 }
4117 }
4118 else {
4119 inverseBgByDefId[defId] = [];
4120 }
4121 }
4122 }
4123 for (let instanceId in eventStore.instances) {
4124 let instance = eventStore.instances[instanceId];
4125 let def = eventStore.defs[instance.defId];
4126 let ui = eventUis[def.defId];
4127 let origRange = instance.range;
4128 let normalRange = (!def.allDay && nextDayThreshold) ?
4129 computeVisibleDayRange(origRange, nextDayThreshold) :
4130 origRange;
4131 let slicedRange = intersectRanges(normalRange, framingRange);
4132 if (slicedRange) {
4133 if (ui.display === 'inverse-background') {
4134 if (def.groupId) {
4135 inverseBgByGroupId[def.groupId].push(slicedRange);
4136 }
4137 else {
4138 inverseBgByDefId[instance.defId].push(slicedRange);
4139 }
4140 }
4141 else if (ui.display !== 'none') {
4142 (ui.display === 'background' ? bgRanges : fgRanges).push({
4143 def,
4144 ui,
4145 instance,
4146 range: slicedRange,
4147 isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(),
4148 isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf(),
4149 });
4150 }
4151 }
4152 }
4153 for (let groupId in inverseBgByGroupId) { // BY GROUP
4154 let ranges = inverseBgByGroupId[groupId];
4155 let invertedRanges = invertRanges(ranges, framingRange);
4156 for (let invertedRange of invertedRanges) {
4157 let def = defByGroupId[groupId];
4158 let ui = eventUis[def.defId];
4159 bgRanges.push({
4160 def,
4161 ui,
4162 instance: null,
4163 range: invertedRange,
4164 isStart: false,
4165 isEnd: false,
4166 });
4167 }
4168 }
4169 for (let defId in inverseBgByDefId) {
4170 let ranges = inverseBgByDefId[defId];
4171 let invertedRanges = invertRanges(ranges, framingRange);
4172 for (let invertedRange of invertedRanges) {
4173 bgRanges.push({
4174 def: eventStore.defs[defId],
4175 ui: eventUis[defId],
4176 instance: null,
4177 range: invertedRange,
4178 isStart: false,
4179 isEnd: false,
4180 });
4181 }
4182 }
4183 return { bg: bgRanges, fg: fgRanges };
4184 }
4185 function hasBgRendering(def) {
4186 return def.ui.display === 'background' || def.ui.display === 'inverse-background';
4187 }
4188 function setElSeg(el, seg) {
4189 el.fcSeg = seg;
4190 }
4191 function getElSeg(el) {
4192 return el.fcSeg ||
4193 el.parentNode.fcSeg || // for the harness
4194 null;
4195 }
4196 // event ui computation
4197 function compileEventUis(eventDefs, eventUiBases) {
4198 return mapHash(eventDefs, (eventDef) => compileEventUi(eventDef, eventUiBases));
4199 }
4200 function compileEventUi(eventDef, eventUiBases) {
4201 let uis = [];
4202 if (eventUiBases['']) {
4203 uis.push(eventUiBases['']);
4204 }
4205 if (eventUiBases[eventDef.defId]) {
4206 uis.push(eventUiBases[eventDef.defId]);
4207 }
4208 uis.push(eventDef.ui);
4209 return combineEventUis(uis);
4210 }
4211 function sortEventSegs(segs, eventOrderSpecs) {
4212 let objs = segs.map(buildSegCompareObj);
4213 objs.sort((obj0, obj1) => compareByFieldSpecs(obj0, obj1, eventOrderSpecs));
4214 return objs.map((c) => c._seg);
4215 }
4216 // returns a object with all primitive props that can be compared
4217 function buildSegCompareObj(seg) {
4218 let { eventRange } = seg;
4219 let eventDef = eventRange.def;
4220 let range = eventRange.instance ? eventRange.instance.range : eventRange.range;
4221 let start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events
4222 let end = range.end ? range.end.valueOf() : 0; // "
4223 return Object.assign(Object.assign(Object.assign({}, eventDef.extendedProps), eventDef), { id: eventDef.publicId, start,
4224 end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg });
4225 }
4226 function computeSegDraggable(seg, context) {
4227 let { pluginHooks } = context;
4228 let transformers = pluginHooks.isDraggableTransformers;
4229 let { def, ui } = seg.eventRange;
4230 let val = ui.startEditable;
4231 for (let transformer of transformers) {
4232 val = transformer(val, def, ui, context);
4233 }
4234 return val;
4235 }
4236 function computeSegStartResizable(seg, context) {
4237 return seg.isStart && seg.eventRange.ui.durationEditable && context.options.eventResizableFromStart;
4238 }
4239 function computeSegEndResizable(seg, context) {
4240 return seg.isEnd && seg.eventRange.ui.durationEditable;
4241 }
4242 function buildSegTimeText(seg, timeFormat, context, defaultDisplayEventTime, // defaults to true
4243 defaultDisplayEventEnd, // defaults to true
4244 startOverride, endOverride) {
4245 let { dateEnv, options } = context;
4246 let { displayEventTime, displayEventEnd } = options;
4247 let eventDef = seg.eventRange.def;
4248 let eventInstance = seg.eventRange.instance;
4249 if (displayEventTime == null) {
4250 displayEventTime = defaultDisplayEventTime !== false;
4251 }
4252 if (displayEventEnd == null) {
4253 displayEventEnd = defaultDisplayEventEnd !== false;
4254 }
4255 let wholeEventStart = eventInstance.range.start;
4256 let wholeEventEnd = eventInstance.range.end;
4257 let segStart = startOverride || seg.start || seg.eventRange.range.start;
4258 let segEnd = endOverride || seg.end || seg.eventRange.range.end;
4259 let isStartDay = startOfDay(wholeEventStart).valueOf() === startOfDay(segStart).valueOf();
4260 let isEndDay = startOfDay(addMs(wholeEventEnd, -1)).valueOf() === startOfDay(addMs(segEnd, -1)).valueOf();
4261 if (displayEventTime && !eventDef.allDay && (isStartDay || isEndDay)) {
4262 segStart = isStartDay ? wholeEventStart : segStart;
4263 segEnd = isEndDay ? wholeEventEnd : segEnd;
4264 if (displayEventEnd && eventDef.hasEnd) {
4265 return dateEnv.formatRange(segStart, segEnd, timeFormat, {
4266 forcedStartTzo: startOverride ? null : eventInstance.forcedStartTzo,
4267 forcedEndTzo: endOverride ? null : eventInstance.forcedEndTzo,
4268 });
4269 }
4270 return dateEnv.format(segStart, timeFormat, {
4271 forcedTzo: startOverride ? null : eventInstance.forcedStartTzo, // nooooo, same
4272 });
4273 }
4274 return '';
4275 }
4276 function getSegMeta(seg, todayRange, nowDate) {
4277 let segRange = seg.eventRange.range;
4278 return {
4279 isPast: segRange.end <= (nowDate || todayRange.start),
4280 isFuture: segRange.start >= (nowDate || todayRange.end),
4281 isToday: todayRange && rangeContainsMarker(todayRange, segRange.start),
4282 };
4283 }
4284 function getEventClassNames(props) {
4285 let classNames = ['fc-event'];
4286 if (props.isMirror) {
4287 classNames.push('fc-event-mirror');
4288 }
4289 if (props.isDraggable) {
4290 classNames.push('fc-event-draggable');
4291 }
4292 if (props.isStartResizable || props.isEndResizable) {
4293 classNames.push('fc-event-resizable');
4294 }
4295 if (props.isDragging) {
4296 classNames.push('fc-event-dragging');
4297 }
4298 if (props.isResizing) {
4299 classNames.push('fc-event-resizing');
4300 }
4301 if (props.isSelected) {
4302 classNames.push('fc-event-selected');
4303 }
4304 if (props.isStart) {
4305 classNames.push('fc-event-start');
4306 }
4307 if (props.isEnd) {
4308 classNames.push('fc-event-end');
4309 }
4310 if (props.isPast) {
4311 classNames.push('fc-event-past');
4312 }
4313 if (props.isToday) {
4314 classNames.push('fc-event-today');
4315 }
4316 if (props.isFuture) {
4317 classNames.push('fc-event-future');
4318 }
4319 return classNames;
4320 }
4321 function buildEventRangeKey(eventRange) {
4322 return eventRange.instance
4323 ? eventRange.instance.instanceId
4324 : `${eventRange.def.defId}:${eventRange.range.start.toISOString()}`;
4325 // inverse-background events don't have specific instances. TODO: better solution
4326 }
4327 function getSegAnchorAttrs(seg, context) {
4328 let { def, instance } = seg.eventRange;
4329 let { url } = def;
4330 if (url) {
4331 return { href: url };
4332 }
4333 let { emitter, options } = context;
4334 let { eventInteractive } = options;
4335 if (eventInteractive == null) {
4336 eventInteractive = def.interactive;
4337 if (eventInteractive == null) {
4338 eventInteractive = Boolean(emitter.hasHandlers('eventClick'));
4339 }
4340 }
4341 // mock what happens in EventClicking
4342 if (eventInteractive) {
4343 // only attach keyboard-related handlers because click handler is already done in EventClicking
4344 return createAriaKeyboardAttrs((ev) => {
4345 emitter.trigger('eventClick', {
4346 el: ev.target,
4347 event: new EventImpl(context, def, instance),
4348 jsEvent: ev,
4349 view: context.viewApi,
4350 });
4351 });
4352 }
4353 return {};
4354 }
4355
4356 const STANDARD_PROPS = {
4357 start: identity,
4358 end: identity,
4359 allDay: Boolean,
4360 };
4361 function parseDateSpan(raw, dateEnv, defaultDuration) {
4362 let span = parseOpenDateSpan(raw, dateEnv);
4363 let { range } = span;
4364 if (!range.start) {
4365 return null;
4366 }
4367 if (!range.end) {
4368 if (defaultDuration == null) {
4369 return null;
4370 }
4371 range.end = dateEnv.add(range.start, defaultDuration);
4372 }
4373 return span;
4374 }
4375 /*
4376 TODO: somehow combine with parseRange?
4377 Will return null if the start/end props were present but parsed invalidly.
4378 */
4379 function parseOpenDateSpan(raw, dateEnv) {
4380 let { refined: standardProps, extra } = refineProps(raw, STANDARD_PROPS);
4381 let startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null;
4382 let endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null;
4383 let { allDay } = standardProps;
4384 if (allDay == null) {
4385 allDay = (startMeta && startMeta.isTimeUnspecified) &&
4386 (!endMeta || endMeta.isTimeUnspecified);
4387 }
4388 return Object.assign({ range: {
4389 start: startMeta ? startMeta.marker : null,
4390 end: endMeta ? endMeta.marker : null,
4391 }, allDay }, extra);
4392 }
4393 function isDateSpansEqual(span0, span1) {
4394 return rangesEqual(span0.range, span1.range) &&
4395 span0.allDay === span1.allDay &&
4396 isSpanPropsEqual(span0, span1);
4397 }
4398 // the NON-DATE-RELATED props
4399 function isSpanPropsEqual(span0, span1) {
4400 for (let propName in span1) {
4401 if (propName !== 'range' && propName !== 'allDay') {
4402 if (span0[propName] !== span1[propName]) {
4403 return false;
4404 }
4405 }
4406 }
4407 // are there any props that span0 has that span1 DOESN'T have?
4408 // both have range/allDay, so no need to special-case.
4409 for (let propName in span0) {
4410 if (!(propName in span1)) {
4411 return false;
4412 }
4413 }
4414 return true;
4415 }
4416 function buildDateSpanApi(span, dateEnv) {
4417 return Object.assign(Object.assign({}, buildRangeApi(span.range, dateEnv, span.allDay)), { allDay: span.allDay });
4418 }
4419 function buildRangeApiWithTimeZone(range, dateEnv, omitTime) {
4420 return Object.assign(Object.assign({}, buildRangeApi(range, dateEnv, omitTime)), { timeZone: dateEnv.timeZone });
4421 }
4422 function buildRangeApi(range, dateEnv, omitTime) {
4423 return {
4424 start: dateEnv.toDate(range.start),
4425 end: dateEnv.toDate(range.end),
4426 startStr: dateEnv.formatIso(range.start, { omitTime }),
4427 endStr: dateEnv.formatIso(range.end, { omitTime }),
4428 };
4429 }
4430 function fabricateEventRange(dateSpan, eventUiBases, context) {
4431 let res = refineEventDef({ editable: false }, context);
4432 let def = parseEventDef(res.refined, res.extra, '', // sourceId
4433 dateSpan.allDay, true, // hasEnd
4434 context);
4435 return {
4436 def,
4437 ui: compileEventUi(def, eventUiBases),
4438 instance: createEventInstance(def.defId, dateSpan.range),
4439 range: dateSpan.range,
4440 isStart: true,
4441 isEnd: true,
4442 };
4443 }
4444
4445 let calendarSystemClassMap = {};
4446 function registerCalendarSystem(name, theClass) {
4447 calendarSystemClassMap[name] = theClass;
4448 }
4449 function createCalendarSystem(name) {
4450 return new calendarSystemClassMap[name]();
4451 }
4452 class GregorianCalendarSystem {
4453 getMarkerYear(d) {
4454 return d.getUTCFullYear();
4455 }
4456 getMarkerMonth(d) {
4457 return d.getUTCMonth();
4458 }
4459 getMarkerDay(d) {
4460 return d.getUTCDate();
4461 }
4462 arrayToMarker(arr) {
4463 return arrayToUtcDate(arr);
4464 }
4465 markerToArray(marker) {
4466 return dateToUtcArray(marker);
4467 }
4468 }
4469 registerCalendarSystem('gregory', GregorianCalendarSystem);
4470
4471 const ISO_RE = /^\s*(\d{4})(-?(\d{2})(-?(\d{2})([T ](\d{2}):?(\d{2})(:?(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/;
4472 function parse(str) {
4473 let m = ISO_RE.exec(str);
4474 if (m) {
4475 let marker = new Date(Date.UTC(Number(m[1]), m[3] ? Number(m[3]) - 1 : 0, Number(m[5] || 1), Number(m[7] || 0), Number(m[8] || 0), Number(m[10] || 0), m[12] ? Number(`0.${m[12]}`) * 1000 : 0));
4476 if (isValidDate(marker)) {
4477 let timeZoneOffset = null;
4478 if (m[13]) {
4479 timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 +
4480 Number(m[18] || 0));
4481 }
4482 return {
4483 marker,
4484 isTimeUnspecified: !m[6],
4485 timeZoneOffset,
4486 };
4487 }
4488 }
4489 return null;
4490 }
4491
4492 class DateEnv {
4493 constructor(settings) {
4494 let timeZone = this.timeZone = settings.timeZone;
4495 let isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC';
4496 if (settings.namedTimeZoneImpl && isNamedTimeZone) {
4497 this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone);
4498 }
4499 this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl);
4500 this.calendarSystem = createCalendarSystem(settings.calendarSystem);
4501 this.locale = settings.locale;
4502 this.weekDow = settings.locale.week.dow;
4503 this.weekDoy = settings.locale.week.doy;
4504 if (settings.weekNumberCalculation === 'ISO') {
4505 this.weekDow = 1;
4506 this.weekDoy = 4;
4507 }
4508 if (typeof settings.firstDay === 'number') {
4509 this.weekDow = settings.firstDay;
4510 }
4511 if (typeof settings.weekNumberCalculation === 'function') {
4512 this.weekNumberFunc = settings.weekNumberCalculation;
4513 }
4514 this.weekText = settings.weekText != null ? settings.weekText : settings.locale.options.weekText;
4515 this.weekTextLong = (settings.weekTextLong != null ? settings.weekTextLong : settings.locale.options.weekTextLong) || this.weekText;
4516 this.cmdFormatter = settings.cmdFormatter;
4517 this.defaultSeparator = settings.defaultSeparator;
4518 }
4519 // Creating / Parsing
4520 createMarker(input) {
4521 let meta = this.createMarkerMeta(input);
4522 if (meta === null) {
4523 return null;
4524 }
4525 return meta.marker;
4526 }
4527 createNowMarker() {
4528 if (this.canComputeOffset) {
4529 return this.timestampToMarker(new Date().valueOf());
4530 }
4531 // if we can't compute the current date val for a timezone,
4532 // better to give the current local date vals than UTC
4533 return arrayToUtcDate(dateToLocalArray(new Date()));
4534 }
4535 createMarkerMeta(input) {
4536 if (typeof input === 'string') {
4537 return this.parse(input);
4538 }
4539 let marker = null;
4540 if (typeof input === 'number') {
4541 marker = this.timestampToMarker(input);
4542 }
4543 else if (input instanceof Date) {
4544 input = input.valueOf();
4545 if (!isNaN(input)) {
4546 marker = this.timestampToMarker(input);
4547 }
4548 }
4549 else if (Array.isArray(input)) {
4550 marker = arrayToUtcDate(input);
4551 }
4552 if (marker === null || !isValidDate(marker)) {
4553 return null;
4554 }
4555 return { marker, isTimeUnspecified: false, forcedTzo: null };
4556 }
4557 parse(s) {
4558 let parts = parse(s);
4559 if (parts === null) {
4560 return null;
4561 }
4562 let { marker } = parts;
4563 let forcedTzo = null;
4564 if (parts.timeZoneOffset !== null) {
4565 if (this.canComputeOffset) {
4566 marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000);
4567 }
4568 else {
4569 forcedTzo = parts.timeZoneOffset;
4570 }
4571 }
4572 return { marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo };
4573 }
4574 // Accessors
4575 getYear(marker) {
4576 return this.calendarSystem.getMarkerYear(marker);
4577 }
4578 getMonth(marker) {
4579 return this.calendarSystem.getMarkerMonth(marker);
4580 }
4581 getDay(marker) {
4582 return this.calendarSystem.getMarkerDay(marker);
4583 }
4584 // Adding / Subtracting
4585 add(marker, dur) {
4586 let a = this.calendarSystem.markerToArray(marker);
4587 a[0] += dur.years;
4588 a[1] += dur.months;
4589 a[2] += dur.days;
4590 a[6] += dur.milliseconds;
4591 return this.calendarSystem.arrayToMarker(a);
4592 }
4593 subtract(marker, dur) {
4594 let a = this.calendarSystem.markerToArray(marker);
4595 a[0] -= dur.years;
4596 a[1] -= dur.months;
4597 a[2] -= dur.days;
4598 a[6] -= dur.milliseconds;
4599 return this.calendarSystem.arrayToMarker(a);
4600 }
4601 addYears(marker, n) {
4602 let a = this.calendarSystem.markerToArray(marker);
4603 a[0] += n;
4604 return this.calendarSystem.arrayToMarker(a);
4605 }
4606 addMonths(marker, n) {
4607 let a = this.calendarSystem.markerToArray(marker);
4608 a[1] += n;
4609 return this.calendarSystem.arrayToMarker(a);
4610 }
4611 // Diffing Whole Units
4612 diffWholeYears(m0, m1) {
4613 let { calendarSystem } = this;
4614 if (timeAsMs(m0) === timeAsMs(m1) &&
4615 calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) &&
4616 calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) {
4617 return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0);
4618 }
4619 return null;
4620 }
4621 diffWholeMonths(m0, m1) {
4622 let { calendarSystem } = this;
4623 if (timeAsMs(m0) === timeAsMs(m1) &&
4624 calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) {
4625 return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) +
4626 (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12;
4627 }
4628 return null;
4629 }
4630 // Range / Duration
4631 greatestWholeUnit(m0, m1) {
4632 let n = this.diffWholeYears(m0, m1);
4633 if (n !== null) {
4634 return { unit: 'year', value: n };
4635 }
4636 n = this.diffWholeMonths(m0, m1);
4637 if (n !== null) {
4638 return { unit: 'month', value: n };
4639 }
4640 n = diffWholeWeeks(m0, m1);
4641 if (n !== null) {
4642 return { unit: 'week', value: n };
4643 }
4644 n = diffWholeDays(m0, m1);
4645 if (n !== null) {
4646 return { unit: 'day', value: n };
4647 }
4648 n = diffHours(m0, m1);
4649 if (isInt(n)) {
4650 return { unit: 'hour', value: n };
4651 }
4652 n = diffMinutes(m0, m1);
4653 if (isInt(n)) {
4654 return { unit: 'minute', value: n };
4655 }
4656 n = diffSeconds(m0, m1);
4657 if (isInt(n)) {
4658 return { unit: 'second', value: n };
4659 }
4660 return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() };
4661 }
4662 countDurationsBetween(m0, m1, d) {
4663 // TODO: can use greatestWholeUnit
4664 let diff;
4665 if (d.years) {
4666 diff = this.diffWholeYears(m0, m1);
4667 if (diff !== null) {
4668 return diff / asRoughYears(d);
4669 }
4670 }
4671 if (d.months) {
4672 diff = this.diffWholeMonths(m0, m1);
4673 if (diff !== null) {
4674 return diff / asRoughMonths(d);
4675 }
4676 }
4677 if (d.days) {
4678 diff = diffWholeDays(m0, m1);
4679 if (diff !== null) {
4680 return diff / asRoughDays(d);
4681 }
4682 }
4683 return (m1.valueOf() - m0.valueOf()) / asRoughMs(d);
4684 }
4685 // Start-Of
4686 // these DON'T return zoned-dates. only UTC start-of dates
4687 startOf(m, unit) {
4688 if (unit === 'year') {
4689 return this.startOfYear(m);
4690 }
4691 if (unit === 'month') {
4692 return this.startOfMonth(m);
4693 }
4694 if (unit === 'week') {
4695 return this.startOfWeek(m);
4696 }
4697 if (unit === 'day') {
4698 return startOfDay(m);
4699 }
4700 if (unit === 'hour') {
4701 return startOfHour(m);
4702 }
4703 if (unit === 'minute') {
4704 return startOfMinute(m);
4705 }
4706 if (unit === 'second') {
4707 return startOfSecond(m);
4708 }
4709 return null;
4710 }
4711 startOfYear(m) {
4712 return this.calendarSystem.arrayToMarker([
4713 this.calendarSystem.getMarkerYear(m),
4714 ]);
4715 }
4716 startOfMonth(m) {
4717 return this.calendarSystem.arrayToMarker([
4718 this.calendarSystem.getMarkerYear(m),
4719 this.calendarSystem.getMarkerMonth(m),
4720 ]);
4721 }
4722 startOfWeek(m) {
4723 return this.calendarSystem.arrayToMarker([
4724 this.calendarSystem.getMarkerYear(m),
4725 this.calendarSystem.getMarkerMonth(m),
4726 m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7),
4727 ]);
4728 }
4729 // Week Number
4730 computeWeekNumber(marker) {
4731 if (this.weekNumberFunc) {
4732 return this.weekNumberFunc(this.toDate(marker));
4733 }
4734 return weekOfYear(marker, this.weekDow, this.weekDoy);
4735 }
4736 // TODO: choke on timeZoneName: long
4737 format(marker, formatter, dateOptions = {}) {
4738 return formatter.format({
4739 marker,
4740 timeZoneOffset: dateOptions.forcedTzo != null ?
4741 dateOptions.forcedTzo :
4742 this.offsetForMarker(marker),
4743 }, this);
4744 }
4745 formatRange(start, end, formatter, dateOptions = {}) {
4746 if (dateOptions.isEndExclusive) {
4747 end = addMs(end, -1);
4748 }
4749 return formatter.formatRange({
4750 marker: start,
4751 timeZoneOffset: dateOptions.forcedStartTzo != null ?
4752 dateOptions.forcedStartTzo :
4753 this.offsetForMarker(start),
4754 }, {
4755 marker: end,
4756 timeZoneOffset: dateOptions.forcedEndTzo != null ?
4757 dateOptions.forcedEndTzo :
4758 this.offsetForMarker(end),
4759 }, this, dateOptions.defaultSeparator);
4760 }
4761 /*
4762 DUMB: the omitTime arg is dumb. if we omit the time, we want to omit the timezone offset. and if we do that,
4763 might as well use buildIsoString or some other util directly
4764 */
4765 formatIso(marker, extraOptions = {}) {
4766 let timeZoneOffset = null;
4767 if (!extraOptions.omitTimeZoneOffset) {
4768 if (extraOptions.forcedTzo != null) {
4769 timeZoneOffset = extraOptions.forcedTzo;
4770 }
4771 else {
4772 timeZoneOffset = this.offsetForMarker(marker);
4773 }
4774 }
4775 return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime);
4776 }
4777 // TimeZone
4778 timestampToMarker(ms) {
4779 if (this.timeZone === 'local') {
4780 return arrayToUtcDate(dateToLocalArray(new Date(ms)));
4781 }
4782 if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) {
4783 return new Date(ms);
4784 }
4785 return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms));
4786 }
4787 offsetForMarker(m) {
4788 if (this.timeZone === 'local') {
4789 return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset
4790 }
4791 if (this.timeZone === 'UTC') {
4792 return 0;
4793 }
4794 if (this.namedTimeZoneImpl) {
4795 return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m));
4796 }
4797 return null;
4798 }
4799 // Conversion
4800 toDate(m, forcedTzo) {
4801 if (this.timeZone === 'local') {
4802 return arrayToLocalDate(dateToUtcArray(m));
4803 }
4804 if (this.timeZone === 'UTC') {
4805 return new Date(m.valueOf()); // make sure it's a copy
4806 }
4807 if (!this.namedTimeZoneImpl) {
4808 return new Date(m.valueOf() - (forcedTzo || 0));
4809 }
4810 return new Date(m.valueOf() -
4811 this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60);
4812 }
4813 }
4814
4815 class NamedTimeZoneImpl {
4816 constructor(timeZoneName) {
4817 this.timeZoneName = timeZoneName;
4818 }
4819 }
4820
4821 class SegHierarchy {
4822 constructor(getEntryThickness = (entry) => {
4823 // if no thickness known, assume 1 (if 0, so small it always fits)
4824 return entry.thickness || 1;
4825 }) {
4826 this.getEntryThickness = getEntryThickness;
4827 // settings
4828 this.strictOrder = false;
4829 this.allowReslicing = false;
4830 this.maxCoord = -1; // -1 means no max
4831 this.maxStackCnt = -1; // -1 means no max
4832 this.levelCoords = []; // ordered
4833 this.entriesByLevel = []; // parallel with levelCoords
4834 this.stackCnts = {}; // TODO: use better technique!?
4835 }
4836 addSegs(inputs) {
4837 let hiddenEntries = [];
4838 for (let input of inputs) {
4839 this.insertEntry(input, hiddenEntries);
4840 }
4841 return hiddenEntries;
4842 }
4843 insertEntry(entry, hiddenEntries) {
4844 let insertion = this.findInsertion(entry);
4845 if (this.isInsertionValid(insertion, entry)) {
4846 this.insertEntryAt(entry, insertion);
4847 }
4848 else {
4849 this.handleInvalidInsertion(insertion, entry, hiddenEntries);
4850 }
4851 }
4852 isInsertionValid(insertion, entry) {
4853 return (this.maxCoord === -1 || insertion.levelCoord + this.getEntryThickness(entry) <= this.maxCoord) &&
4854 (this.maxStackCnt === -1 || insertion.stackCnt < this.maxStackCnt);
4855 }
4856 handleInvalidInsertion(insertion, entry, hiddenEntries) {
4857 if (this.allowReslicing && insertion.touchingEntry) {
4858 const hiddenEntry = Object.assign(Object.assign({}, entry), { span: intersectSpans(entry.span, insertion.touchingEntry.span) });
4859 hiddenEntries.push(hiddenEntry);
4860 this.splitEntry(entry, insertion.touchingEntry, hiddenEntries);
4861 }
4862 else {
4863 hiddenEntries.push(entry);
4864 }
4865 }
4866 /*
4867 Does NOT add what hit the `barrier` into hiddenEntries. Should already be done.
4868 */
4869 splitEntry(entry, barrier, hiddenEntries) {
4870 let entrySpan = entry.span;
4871 let barrierSpan = barrier.span;
4872 if (entrySpan.start < barrierSpan.start) {
4873 this.insertEntry({
4874 index: entry.index,
4875 thickness: entry.thickness,
4876 span: { start: entrySpan.start, end: barrierSpan.start },
4877 }, hiddenEntries);
4878 }
4879 if (entrySpan.end > barrierSpan.end) {
4880 this.insertEntry({
4881 index: entry.index,
4882 thickness: entry.thickness,
4883 span: { start: barrierSpan.end, end: entrySpan.end },
4884 }, hiddenEntries);
4885 }
4886 }
4887 insertEntryAt(entry, insertion) {
4888 let { entriesByLevel, levelCoords } = this;
4889 if (insertion.lateral === -1) {
4890 // create a new level
4891 insertAt(levelCoords, insertion.level, insertion.levelCoord);
4892 insertAt(entriesByLevel, insertion.level, [entry]);
4893 }
4894 else {
4895 // insert into existing level
4896 insertAt(entriesByLevel[insertion.level], insertion.lateral, entry);
4897 }
4898 this.stackCnts[buildEntryKey(entry)] = insertion.stackCnt;
4899 }
4900 /*
4901 does not care about limits
4902 */
4903 findInsertion(newEntry) {
4904 let { levelCoords, entriesByLevel, strictOrder, stackCnts } = this;
4905 let levelCnt = levelCoords.length;
4906 let candidateCoord = 0;
4907 let touchingLevel = -1;
4908 let touchingLateral = -1;
4909 let touchingEntry = null;
4910 let stackCnt = 0;
4911 for (let trackingLevel = 0; trackingLevel < levelCnt; trackingLevel += 1) {
4912 const trackingCoord = levelCoords[trackingLevel];
4913 // if the current level is past the placed entry, we have found a good empty space and can stop.
4914 // if strictOrder, keep finding more lateral intersections.
4915 if (!strictOrder && trackingCoord >= candidateCoord + this.getEntryThickness(newEntry)) {
4916 break;
4917 }
4918 let trackingEntries = entriesByLevel[trackingLevel];
4919 let trackingEntry;
4920 let searchRes = binarySearch(trackingEntries, newEntry.span.start, getEntrySpanEnd); // find first entry after newEntry's end
4921 let lateralIndex = searchRes[0] + searchRes[1]; // if exact match (which doesn't collide), go to next one
4922 while ( // loop through entries that horizontally intersect
4923 (trackingEntry = trackingEntries[lateralIndex]) && // but not past the whole entry list
4924 trackingEntry.span.start < newEntry.span.end // and not entirely past newEntry
4925 ) {
4926 let trackingEntryBottom = trackingCoord + this.getEntryThickness(trackingEntry);
4927 // intersects into the top of the candidate?
4928 if (trackingEntryBottom > candidateCoord) {
4929 candidateCoord = trackingEntryBottom;
4930 touchingEntry = trackingEntry;
4931 touchingLevel = trackingLevel;
4932 touchingLateral = lateralIndex;
4933 }
4934 // butts up against top of candidate? (will happen if just intersected as well)
4935 if (trackingEntryBottom === candidateCoord) {
4936 // accumulate the highest possible stackCnt of the trackingEntries that butt up
4937 stackCnt = Math.max(stackCnt, stackCnts[buildEntryKey(trackingEntry)] + 1);
4938 }
4939 lateralIndex += 1;
4940 }
4941 }
4942 // the destination level will be after touchingEntry's level. find it
4943 let destLevel = 0;
4944 if (touchingEntry) {
4945 destLevel = touchingLevel + 1;
4946 while (destLevel < levelCnt && levelCoords[destLevel] < candidateCoord) {
4947 destLevel += 1;
4948 }
4949 }
4950 // if adding to an existing level, find where to insert
4951 let destLateral = -1;
4952 if (destLevel < levelCnt && levelCoords[destLevel] === candidateCoord) {
4953 destLateral = binarySearch(entriesByLevel[destLevel], newEntry.span.end, getEntrySpanEnd)[0];
4954 }
4955 return {
4956 touchingLevel,
4957 touchingLateral,
4958 touchingEntry,
4959 stackCnt,
4960 levelCoord: candidateCoord,
4961 level: destLevel,
4962 lateral: destLateral,
4963 };
4964 }
4965 // sorted by levelCoord (lowest to highest)
4966 toRects() {
4967 let { entriesByLevel, levelCoords } = this;
4968 let levelCnt = entriesByLevel.length;
4969 let rects = [];
4970 for (let level = 0; level < levelCnt; level += 1) {
4971 let entries = entriesByLevel[level];
4972 let levelCoord = levelCoords[level];
4973 for (let entry of entries) {
4974 rects.push(Object.assign(Object.assign({}, entry), { thickness: this.getEntryThickness(entry), levelCoord }));
4975 }
4976 }
4977 return rects;
4978 }
4979 }
4980 function getEntrySpanEnd(entry) {
4981 return entry.span.end;
4982 }
4983 function buildEntryKey(entry) {
4984 return entry.index + ':' + entry.span.start;
4985 }
4986 // returns groups with entries sorted by input order
4987 function groupIntersectingEntries(entries) {
4988 let merges = [];
4989 for (let entry of entries) {
4990 let filteredMerges = [];
4991 let hungryMerge = {
4992 span: entry.span,
4993 entries: [entry],
4994 };
4995 for (let merge of merges) {
4996 if (intersectSpans(merge.span, hungryMerge.span)) {
4997 hungryMerge = {
4998 entries: merge.entries.concat(hungryMerge.entries),
4999 span: joinSpans(merge.span, hungryMerge.span),
5000 };
5001 }
5002 else {
5003 filteredMerges.push(merge);
5004 }
5005 }
5006 filteredMerges.push(hungryMerge);
5007 merges = filteredMerges;
5008 }
5009 return merges;
5010 }
5011 function joinSpans(span0, span1) {
5012 return {
5013 start: Math.min(span0.start, span1.start),
5014 end: Math.max(span0.end, span1.end),
5015 };
5016 }
5017 function intersectSpans(span0, span1) {
5018 let start = Math.max(span0.start, span1.start);
5019 let end = Math.min(span0.end, span1.end);
5020 if (start < end) {
5021 return { start, end };
5022 }
5023 return null;
5024 }
5025 // general util
5026 // ---------------------------------------------------------------------------------------------------------------------
5027 function insertAt(arr, index, item) {
5028 arr.splice(index, 0, item);
5029 }
5030 function binarySearch(a, searchVal, getItemVal) {
5031 let startIndex = 0;
5032 let endIndex = a.length; // exclusive
5033 if (!endIndex || searchVal < getItemVal(a[startIndex])) { // no items OR before first item
5034 return [0, 0];
5035 }
5036 if (searchVal > getItemVal(a[endIndex - 1])) { // after last item
5037 return [endIndex, 0];
5038 }
5039 while (startIndex < endIndex) {
5040 let middleIndex = Math.floor(startIndex + (endIndex - startIndex) / 2);
5041 let middleVal = getItemVal(a[middleIndex]);
5042 if (searchVal < middleVal) {
5043 endIndex = middleIndex;
5044 }
5045 else if (searchVal > middleVal) {
5046 startIndex = middleIndex + 1;
5047 }
5048 else { // equal!
5049 return [middleIndex, 1];
5050 }
5051 }
5052 return [startIndex, 0];
5053 }
5054
5055 class Interaction {
5056 constructor(settings) {
5057 this.component = settings.component;
5058 this.isHitComboAllowed = settings.isHitComboAllowed || null;
5059 }
5060 destroy() {
5061 }
5062 }
5063 function parseInteractionSettings(component, input) {
5064 return {
5065 component,
5066 el: input.el,
5067 useEventCenter: input.useEventCenter != null ? input.useEventCenter : true,
5068 isHitComboAllowed: input.isHitComboAllowed || null,
5069 };
5070 }
5071 function interactionSettingsToStore(settings) {
5072 return {
5073 [settings.component.uid]: settings,
5074 };
5075 }
5076 // global state
5077 const interactionSettingsStore = {};
5078
5079 /*
5080 An abstraction for a dragging interaction originating on an event.
5081 Does higher-level things than PointerDragger, such as possibly:
5082 - a "mirror" that moves with the pointer
5083 - a minimum number of pixels or other criteria for a true drag to begin
5084
5085 subclasses must emit:
5086 - pointerdown
5087 - dragstart
5088 - dragmove
5089 - pointerup
5090 - dragend
5091 */
5092 class ElementDragging {
5093 constructor(el, selector) {
5094 this.emitter = new Emitter();
5095 }
5096 destroy() {
5097 }
5098 setMirrorIsVisible(bool) {
5099 // optional if subclass doesn't want to support a mirror
5100 }
5101 setMirrorNeedsRevert(bool) {
5102 // optional if subclass doesn't want to support a mirror
5103 }
5104 setAutoScrollEnabled(bool) {
5105 // optional
5106 }
5107 }
5108
5109 // TODO: get rid of this in favor of options system,
5110 // tho it's really easy to access this globally rather than pass thru options.
5111 const config = {};
5112
5113 /*
5114 Information about what will happen when an external element is dragged-and-dropped
5115 onto a calendar. Contains information for creating an event.
5116 */
5117 const DRAG_META_REFINERS = {
5118 startTime: createDuration,
5119 duration: createDuration,
5120 create: Boolean,
5121 sourceId: String,
5122 };
5123 function parseDragMeta(raw) {
5124 let { refined, extra } = refineProps(raw, DRAG_META_REFINERS);
5125 return {
5126 startTime: refined.startTime || null,
5127 duration: refined.duration || null,
5128 create: refined.create != null ? refined.create : true,
5129 sourceId: refined.sourceId,
5130 leftoverProps: extra,
5131 };
5132 }
5133
5134 class CalendarRoot extends BaseComponent {
5135 constructor() {
5136 super(...arguments);
5137 this.state = {
5138 forPrint: false,
5139 };
5140 this.handleBeforePrint = () => {
5141 flushSync(() => {
5142 this.setState({ forPrint: true });
5143 });
5144 };
5145 this.handleAfterPrint = () => {
5146 flushSync(() => {
5147 this.setState({ forPrint: false });
5148 });
5149 };
5150 }
5151 render() {
5152 let { props } = this;
5153 let { options } = props;
5154 let { forPrint } = this.state;
5155 let isHeightAuto = forPrint || options.height === 'auto' || options.contentHeight === 'auto';
5156 let height = (!isHeightAuto && options.height != null) ? options.height : '';
5157 let classNames = [
5158 'fc',
5159 forPrint ? 'fc-media-print' : 'fc-media-screen',
5160 `fc-direction-${options.direction}`,
5161 props.theme.getClass('root'),
5162 ];
5163 if (!getCanVGrowWithinCell()) {
5164 classNames.push('fc-liquid-hack');
5165 }
5166 return props.children(classNames, height, isHeightAuto, forPrint);
5167 }
5168 componentDidMount() {
5169 let { emitter } = this.props;
5170 emitter.on('_beforeprint', this.handleBeforePrint);
5171 emitter.on('_afterprint', this.handleAfterPrint);
5172 }
5173 componentWillUnmount() {
5174 let { emitter } = this.props;
5175 emitter.off('_beforeprint', this.handleBeforePrint);
5176 emitter.off('_afterprint', this.handleAfterPrint);
5177 }
5178 }
5179
5180 // Computes a default column header formatting string if `colFormat` is not explicitly defined
5181 function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
5182 // if more than one week row, or if there are a lot of columns with not much space,
5183 // put just the day numbers will be in each cell
5184 if (!datesRepDistinctDays || dayCnt > 10) {
5185 return createFormatter({ weekday: 'short' }); // "Sat"
5186 }
5187 if (dayCnt > 1) {
5188 return createFormatter({ weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }); // "Sat 11/12"
5189 }
5190 return createFormatter({ weekday: 'long' }); // "Saturday"
5191 }
5192
5193 const CLASS_NAME = 'fc-col-header-cell'; // do the cushion too? no
5194 function renderInner$1(renderProps) {
5195 return renderProps.text;
5196 }
5197
5198 class ContentInjector extends BaseComponent {
5199 constructor() {
5200 super(...arguments);
5201 this.id = guid();
5202 this.queuedDomNodes = [];
5203 this.currentDomNodes = [];
5204 this.handleEl = (el) => {
5205 const { options } = this.context;
5206 const { generatorName } = this.props;
5207 if (!options.customRenderingReplaces || !hasCustomRenderingHandler(generatorName, options)) {
5208 this.updateElRef(el);
5209 }
5210 };
5211 this.updateElRef = (el) => {
5212 if (this.props.elRef) {
5213 setRef(this.props.elRef, el);
5214 }
5215 };
5216 }
5217 render() {
5218 const { props, context } = this;
5219 const { options } = context;
5220 const { customGenerator, defaultGenerator, renderProps } = props;
5221 const attrs = buildElAttrs(props, [], this.handleEl);
5222 let useDefault = false;
5223 let innerContent;
5224 let queuedDomNodes = [];
5225 let currentGeneratorMeta;
5226 if (customGenerator != null) {
5227 const customGeneratorRes = typeof customGenerator === 'function' ?
5228 customGenerator(renderProps, y) :
5229 customGenerator;
5230 if (customGeneratorRes === true) {
5231 useDefault = true;
5232 }
5233 else {
5234 const isObject = customGeneratorRes && typeof customGeneratorRes === 'object'; // non-null
5235 if (isObject && ('html' in customGeneratorRes)) {
5236 attrs.dangerouslySetInnerHTML = { __html: customGeneratorRes.html };
5237 }
5238 else if (isObject && ('domNodes' in customGeneratorRes)) {
5239 queuedDomNodes = Array.prototype.slice.call(customGeneratorRes.domNodes);
5240 }
5241 else if (isObject
5242 ? i$1(customGeneratorRes) // vdom node
5243 : typeof customGeneratorRes !== 'function' // primitive value (like string or number)
5244 ) {
5245 // use in vdom
5246 innerContent = customGeneratorRes;
5247 }
5248 else {
5249 // an exotic object for handleCustomRendering
5250 currentGeneratorMeta = customGeneratorRes;
5251 }
5252 }
5253 }
5254 else {
5255 useDefault = !hasCustomRenderingHandler(props.generatorName, options);
5256 }
5257 if (useDefault && defaultGenerator) {
5258 innerContent = defaultGenerator(renderProps);
5259 }
5260 this.queuedDomNodes = queuedDomNodes;
5261 this.currentGeneratorMeta = currentGeneratorMeta;
5262 return y(props.elTag, attrs, innerContent);
5263 }
5264 componentDidMount() {
5265 this.applyQueueudDomNodes();
5266 this.triggerCustomRendering(true);
5267 }
5268 componentDidUpdate() {
5269 this.applyQueueudDomNodes();
5270 this.triggerCustomRendering(true);
5271 }
5272 componentWillUnmount() {
5273 this.triggerCustomRendering(false); // TODO: different API for removal?
5274 }
5275 triggerCustomRendering(isActive) {
5276 var _a;
5277 const { props, context } = this;
5278 const { handleCustomRendering, customRenderingMetaMap } = context.options;
5279 if (handleCustomRendering) {
5280 const generatorMeta = (_a = this.currentGeneratorMeta) !== null && _a !== void 0 ? _a : customRenderingMetaMap === null || customRenderingMetaMap === void 0 ? void 0 : customRenderingMetaMap[props.generatorName];
5281 if (generatorMeta) {
5282 handleCustomRendering(Object.assign(Object.assign({ id: this.id, isActive, containerEl: this.base, reportNewContainerEl: this.updateElRef, // front-end framework tells us about new container els
5283 generatorMeta }, props), { elClasses: (props.elClasses || []).filter(isTruthy) }));
5284 }
5285 }
5286 }
5287 applyQueueudDomNodes() {
5288 const { queuedDomNodes, currentDomNodes } = this;
5289 const el = this.base;
5290 if (!isArraysEqual(queuedDomNodes, currentDomNodes)) {
5291 currentDomNodes.forEach(removeElement);
5292 for (let newNode of queuedDomNodes) {
5293 el.appendChild(newNode);
5294 }
5295 this.currentDomNodes = queuedDomNodes;
5296 }
5297 }
5298 }
5299 ContentInjector.addPropsEquality({
5300 elClasses: isArraysEqual,
5301 elStyle: isPropsEqual,
5302 elAttrs: isNonHandlerPropsEqual,
5303 renderProps: isPropsEqual,
5304 });
5305 // Util
5306 /*
5307 Does UI-framework provide custom way of rendering that does not use Preact VDOM
5308 AND does the calendar's options define custom rendering?
5309 AKA. Should we NOT render the default content?
5310 */
5311 function hasCustomRenderingHandler(generatorName, options) {
5312 var _a;
5313 return Boolean(options.handleCustomRendering &&
5314 generatorName &&
5315 ((_a = options.customRenderingMetaMap) === null || _a === void 0 ? void 0 : _a[generatorName]));
5316 }
5317 function buildElAttrs(props, extraClassNames, elRef) {
5318 const attrs = Object.assign(Object.assign({}, props.elAttrs), { ref: elRef });
5319 if (props.elClasses || extraClassNames) {
5320 attrs.className = (props.elClasses || [])
5321 .concat(extraClassNames || [])
5322 .concat(attrs.className || [])
5323 .filter(Boolean)
5324 .join(' ');
5325 }
5326 if (props.elStyle) {
5327 attrs.style = props.elStyle;
5328 }
5329 return attrs;
5330 }
5331 function isTruthy(val) {
5332 return Boolean(val);
5333 }
5334
5335 const RenderId = createContext(0);
5336
5337 class ContentContainer extends x$1 {
5338 constructor() {
5339 super(...arguments);
5340 this.InnerContent = InnerContentInjector.bind(undefined, this);
5341 this.handleEl = (el) => {
5342 this.el = el;
5343 if (this.props.elRef) {
5344 setRef(this.props.elRef, el);
5345 if (el && this.didMountMisfire) {
5346 this.componentDidMount();
5347 }
5348 }
5349 };
5350 }
5351 render() {
5352 const { props } = this;
5353 const generatedClassNames = generateClassNames(props.classNameGenerator, props.renderProps);
5354 if (props.children) {
5355 const elAttrs = buildElAttrs(props, generatedClassNames, this.handleEl);
5356 const children = props.children(this.InnerContent, props.renderProps, elAttrs);
5357 if (props.elTag) {
5358 return y(props.elTag, elAttrs, children);
5359 }
5360 else {
5361 return children;
5362 }
5363 }
5364 else {
5365 return y((ContentInjector), Object.assign(Object.assign({}, props), { elRef: this.handleEl, elTag: props.elTag || 'div', elClasses: (props.elClasses || []).concat(generatedClassNames), renderId: this.context }));
5366 }
5367 }
5368 componentDidMount() {
5369 var _a, _b;
5370 if (this.el) {
5371 (_b = (_a = this.props).didMount) === null || _b === void 0 ? void 0 : _b.call(_a, Object.assign(Object.assign({}, this.props.renderProps), { el: this.el }));
5372 }
5373 else {
5374 this.didMountMisfire = true;
5375 }
5376 }
5377 componentWillUnmount() {
5378 var _a, _b;
5379 (_b = (_a = this.props).willUnmount) === null || _b === void 0 ? void 0 : _b.call(_a, Object.assign(Object.assign({}, this.props.renderProps), { el: this.el }));
5380 }
5381 }
5382 ContentContainer.contextType = RenderId;
5383 function InnerContentInjector(containerComponent, props) {
5384 const parentProps = containerComponent.props;
5385 return y((ContentInjector), Object.assign({ renderProps: parentProps.renderProps, generatorName: parentProps.generatorName, customGenerator: parentProps.customGenerator, defaultGenerator: parentProps.defaultGenerator, renderId: containerComponent.context }, props));
5386 }
5387 // Utils
5388 function generateClassNames(classNameGenerator, renderProps) {
5389 const classNames = typeof classNameGenerator === 'function' ?
5390 classNameGenerator(renderProps) :
5391 classNameGenerator || [];
5392 return typeof classNames === 'string' ? [classNames] : classNames;
5393 }
5394
5395 // BAD name for this class now. used in the Header
5396 class TableDateCell extends BaseComponent {
5397 render() {
5398 let { dateEnv, options, theme, viewApi } = this.context;
5399 let { props } = this;
5400 let { date, dateProfile } = props;
5401 let dayMeta = getDateMeta(date, props.todayRange, null, dateProfile);
5402 let classNames = [CLASS_NAME].concat(getDayClassNames(dayMeta, theme));
5403 let text = dateEnv.format(date, props.dayHeaderFormat);
5404 // if colCnt is 1, we are already in a day-view and don't need a navlink
5405 let navLinkAttrs = (!dayMeta.isDisabled && props.colCnt > 1)
5406 ? buildNavLinkAttrs(this.context, date)
5407 : {};
5408 let renderProps = Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: viewApi }, props.extraRenderProps), { text }), dayMeta);
5409 return (y(ContentContainer, { elTag: "th", elClasses: classNames, elAttrs: Object.assign({ role: 'columnheader', colSpan: props.colSpan, 'data-date': !dayMeta.isDisabled ? formatDayString(date) : undefined }, props.extraDataAttrs), renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner$1, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContainer) => (y("div", { className: "fc-scrollgrid-sync-inner" }, !dayMeta.isDisabled && (y(InnerContainer, { elTag: "a", elAttrs: navLinkAttrs, elClasses: [
5410 'fc-col-header-cell-cushion',
5411 props.isSticky && 'fc-sticky',
5412 ] }))))));
5413 }
5414 }
5415
5416 const WEEKDAY_FORMAT = createFormatter({ weekday: 'long' });
5417 class TableDowCell extends BaseComponent {
5418 render() {
5419 let { props } = this;
5420 let { dateEnv, theme, viewApi, options } = this.context;
5421 let date = addDays(new Date(259200000), props.dow); // start with Sun, 04 Jan 1970 00:00:00 GMT
5422 let dateMeta = {
5423 dow: props.dow,
5424 isDisabled: false,
5425 isFuture: false,
5426 isPast: false,
5427 isToday: false,
5428 isOther: false,
5429 };
5430 let text = dateEnv.format(date, props.dayHeaderFormat);
5431 let renderProps = Object.assign(Object.assign(Object.assign(Object.assign({ // TODO: make this public?
5432 date }, dateMeta), { view: viewApi }), props.extraRenderProps), { text });
5433 return (y(ContentContainer, { elTag: "th", elClasses: [
5434 CLASS_NAME,
5435 ...getDayClassNames(dateMeta, theme),
5436 ...(props.extraClassNames || []),
5437 ], elAttrs: Object.assign({ role: 'columnheader', colSpan: props.colSpan }, props.extraDataAttrs), renderProps: renderProps, generatorName: "dayHeaderContent", customGenerator: options.dayHeaderContent, defaultGenerator: renderInner$1, classNameGenerator: options.dayHeaderClassNames, didMount: options.dayHeaderDidMount, willUnmount: options.dayHeaderWillUnmount }, (InnerContent) => (y("div", { className: "fc-scrollgrid-sync-inner" },
5438 y(InnerContent, { elTag: "a", elClasses: [
5439 'fc-col-header-cell-cushion',
5440 props.isSticky && 'fc-sticky',
5441 ], elAttrs: {
5442 'aria-label': dateEnv.format(date, WEEKDAY_FORMAT),
5443 } })))));
5444 }
5445 }
5446
5447 class NowTimer extends x$1 {
5448 constructor(props, context) {
5449 super(props, context);
5450 this.initialNowDate = getNow(context.options.now, context.dateEnv);
5451 this.initialNowQueriedMs = new Date().valueOf();
5452 this.state = this.computeTiming().currentState;
5453 }
5454 render() {
5455 let { props, state } = this;
5456 return props.children(state.nowDate, state.todayRange);
5457 }
5458 componentDidMount() {
5459 this.setTimeout();
5460 }
5461 componentDidUpdate(prevProps) {
5462 if (prevProps.unit !== this.props.unit) {
5463 this.clearTimeout();
5464 this.setTimeout();
5465 }
5466 }
5467 componentWillUnmount() {
5468 this.clearTimeout();
5469 }
5470 computeTiming() {
5471 let { props, context } = this;
5472 let unroundedNow = addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs);
5473 let currentUnitStart = context.dateEnv.startOf(unroundedNow, props.unit);
5474 let nextUnitStart = context.dateEnv.add(currentUnitStart, createDuration(1, props.unit));
5475 let waitMs = nextUnitStart.valueOf() - unroundedNow.valueOf();
5476 // there is a max setTimeout ms value (https://stackoverflow.com/a/3468650/96342)
5477 // ensure no longer than a day
5478 waitMs = Math.min(1000 * 60 * 60 * 24, waitMs);
5479 return {
5480 currentState: { nowDate: currentUnitStart, todayRange: buildDayRange(currentUnitStart) },
5481 nextState: { nowDate: nextUnitStart, todayRange: buildDayRange(nextUnitStart) },
5482 waitMs,
5483 };
5484 }
5485 setTimeout() {
5486 let { nextState, waitMs } = this.computeTiming();
5487 this.timeoutId = setTimeout(() => {
5488 this.setState(nextState, () => {
5489 this.setTimeout();
5490 });
5491 }, waitMs);
5492 }
5493 clearTimeout() {
5494 if (this.timeoutId) {
5495 clearTimeout(this.timeoutId);
5496 }
5497 }
5498 }
5499 NowTimer.contextType = ViewContextType;
5500 function buildDayRange(date) {
5501 let start = startOfDay(date);
5502 let end = addDays(start, 1);
5503 return { start, end };
5504 }
5505
5506 class DayHeader extends BaseComponent {
5507 constructor() {
5508 super(...arguments);
5509 this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
5510 }
5511 render() {
5512 let { context } = this;
5513 let { dates, dateProfile, datesRepDistinctDays, renderIntro } = this.props;
5514 let dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, datesRepDistinctDays, dates.length);
5515 return (y(NowTimer, { unit: "day" }, (nowDate, todayRange) => (y("tr", { role: "row" },
5516 renderIntro && renderIntro('day'),
5517 dates.map((date) => (datesRepDistinctDays ? (y(TableDateCell, { key: date.toISOString(), date: date, dateProfile: dateProfile, todayRange: todayRange, colCnt: dates.length, dayHeaderFormat: dayHeaderFormat })) : (y(TableDowCell, { key: date.getUTCDay(), dow: date.getUTCDay(), dayHeaderFormat: dayHeaderFormat }))))))));
5518 }
5519 }
5520 function createDayHeaderFormatter(explicitFormat, datesRepDistinctDays, dateCnt) {
5521 return explicitFormat || computeFallbackHeaderFormat(datesRepDistinctDays, dateCnt);
5522 }
5523
5524 class DaySeriesModel {
5525 constructor(range, dateProfileGenerator) {
5526 let date = range.start;
5527 let { end } = range;
5528 let indices = [];
5529 let dates = [];
5530 let dayIndex = -1;
5531 while (date < end) { // loop each day from start to end
5532 if (dateProfileGenerator.isHiddenDay(date)) {
5533 indices.push(dayIndex + 0.5); // mark that it's between indices
5534 }
5535 else {
5536 dayIndex += 1;
5537 indices.push(dayIndex);
5538 dates.push(date);
5539 }
5540 date = addDays(date, 1);
5541 }
5542 this.dates = dates;
5543 this.indices = indices;
5544 this.cnt = dates.length;
5545 }
5546 sliceRange(range) {
5547 let firstIndex = this.getDateDayIndex(range.start); // inclusive first index
5548 let lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index
5549 let clippedFirstIndex = Math.max(0, firstIndex);
5550 let clippedLastIndex = Math.min(this.cnt - 1, lastIndex);
5551 // deal with in-between indices
5552 clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell
5553 clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell
5554 if (clippedFirstIndex <= clippedLastIndex) {
5555 return {
5556 firstIndex: clippedFirstIndex,
5557 lastIndex: clippedLastIndex,
5558 isStart: firstIndex === clippedFirstIndex,
5559 isEnd: lastIndex === clippedLastIndex,
5560 };
5561 }
5562 return null;
5563 }
5564 // Given a date, returns its chronolocial cell-index from the first cell of the grid.
5565 // If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
5566 // If before the first offset, returns a negative number.
5567 // If after the last offset, returns an offset past the last cell offset.
5568 // Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
5569 getDateDayIndex(date) {
5570 let { indices } = this;
5571 let dayOffset = Math.floor(diffDays(this.dates[0], date));
5572 if (dayOffset < 0) {
5573 return indices[0] - 1;
5574 }
5575 if (dayOffset >= indices.length) {
5576 return indices[indices.length - 1] + 1;
5577 }
5578 return indices[dayOffset];
5579 }
5580 }
5581
5582 class DayTableModel {
5583 constructor(daySeries, breakOnWeeks) {
5584 let { dates } = daySeries;
5585 let daysPerRow;
5586 let firstDay;
5587 let rowCnt;
5588 if (breakOnWeeks) {
5589 // count columns until the day-of-week repeats
5590 firstDay = dates[0].getUTCDay();
5591 for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow += 1) {
5592 if (dates[daysPerRow].getUTCDay() === firstDay) {
5593 break;
5594 }
5595 }
5596 rowCnt = Math.ceil(dates.length / daysPerRow);
5597 }
5598 else {
5599 rowCnt = 1;
5600 daysPerRow = dates.length;
5601 }
5602 this.rowCnt = rowCnt;
5603 this.colCnt = daysPerRow;
5604 this.daySeries = daySeries;
5605 this.cells = this.buildCells();
5606 this.headerDates = this.buildHeaderDates();
5607 }
5608 buildCells() {
5609 let rows = [];
5610 for (let row = 0; row < this.rowCnt; row += 1) {
5611 let cells = [];
5612 for (let col = 0; col < this.colCnt; col += 1) {
5613 cells.push(this.buildCell(row, col));
5614 }
5615 rows.push(cells);
5616 }
5617 return rows;
5618 }
5619 buildCell(row, col) {
5620 let date = this.daySeries.dates[row * this.colCnt + col];
5621 return {
5622 key: date.toISOString(),
5623 date,
5624 };
5625 }
5626 buildHeaderDates() {
5627 let dates = [];
5628 for (let col = 0; col < this.colCnt; col += 1) {
5629 dates.push(this.cells[0][col].date);
5630 }
5631 return dates;
5632 }
5633 sliceRange(range) {
5634 let { colCnt } = this;
5635 let seriesSeg = this.daySeries.sliceRange(range);
5636 let segs = [];
5637 if (seriesSeg) {
5638 let { firstIndex, lastIndex } = seriesSeg;
5639 let index = firstIndex;
5640 while (index <= lastIndex) {
5641 let row = Math.floor(index / colCnt);
5642 let nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1);
5643 segs.push({
5644 row,
5645 firstCol: index % colCnt,
5646 lastCol: (nextIndex - 1) % colCnt,
5647 isStart: seriesSeg.isStart && index === firstIndex,
5648 isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex,
5649 });
5650 index = nextIndex;
5651 }
5652 }
5653 return segs;
5654 }
5655 }
5656
5657 class Slicer {
5658 constructor() {
5659 this.sliceBusinessHours = memoize(this._sliceBusinessHours);
5660 this.sliceDateSelection = memoize(this._sliceDateSpan);
5661 this.sliceEventStore = memoize(this._sliceEventStore);
5662 this.sliceEventDrag = memoize(this._sliceInteraction);
5663 this.sliceEventResize = memoize(this._sliceInteraction);
5664 this.forceDayIfListItem = false; // hack
5665 }
5666 sliceProps(props, dateProfile, nextDayThreshold, context, ...extraArgs) {
5667 let { eventUiBases } = props;
5668 let eventSegs = this.sliceEventStore(props.eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs);
5669 return {
5670 dateSelectionSegs: this.sliceDateSelection(props.dateSelection, dateProfile, nextDayThreshold, eventUiBases, context, ...extraArgs),
5671 businessHourSegs: this.sliceBusinessHours(props.businessHours, dateProfile, nextDayThreshold, context, ...extraArgs),
5672 fgEventSegs: eventSegs.fg,
5673 bgEventSegs: eventSegs.bg,
5674 eventDrag: this.sliceEventDrag(props.eventDrag, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs),
5675 eventResize: this.sliceEventResize(props.eventResize, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs),
5676 eventSelection: props.eventSelection,
5677 }; // TODO: give interactionSegs?
5678 }
5679 sliceNowDate(// does not memoize
5680 date, dateProfile, nextDayThreshold, context, ...extraArgs) {
5681 return this._sliceDateSpan({ range: { start: date, end: addMs(date, 1) }, allDay: false }, // add 1 ms, protect against null range
5682 dateProfile, nextDayThreshold, {}, context, ...extraArgs);
5683 }
5684 _sliceBusinessHours(businessHours, dateProfile, nextDayThreshold, context, ...extraArgs) {
5685 if (!businessHours) {
5686 return [];
5687 }
5688 return this._sliceEventStore(expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), context), {}, dateProfile, nextDayThreshold, ...extraArgs).bg;
5689 }
5690 _sliceEventStore(eventStore, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs) {
5691 if (eventStore) {
5692 let rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
5693 return {
5694 bg: this.sliceEventRanges(rangeRes.bg, extraArgs),
5695 fg: this.sliceEventRanges(rangeRes.fg, extraArgs),
5696 };
5697 }
5698 return { bg: [], fg: [] };
5699 }
5700 _sliceInteraction(interaction, eventUiBases, dateProfile, nextDayThreshold, ...extraArgs) {
5701 if (!interaction) {
5702 return null;
5703 }
5704 let rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
5705 return {
5706 segs: this.sliceEventRanges(rangeRes.fg, extraArgs),
5707 affectedInstances: interaction.affectedEvents.instances,
5708 isEvent: interaction.isEvent,
5709 };
5710 }
5711 _sliceDateSpan(dateSpan, dateProfile, nextDayThreshold, eventUiBases, context, ...extraArgs) {
5712 if (!dateSpan) {
5713 return [];
5714 }
5715 let activeRange = computeActiveRange(dateProfile, Boolean(nextDayThreshold));
5716 let activeDateSpanRange = intersectRanges(dateSpan.range, activeRange);
5717 if (activeDateSpanRange) {
5718 dateSpan = Object.assign(Object.assign({}, dateSpan), { range: activeDateSpanRange });
5719 let eventRange = fabricateEventRange(dateSpan, eventUiBases, context);
5720 let segs = this.sliceRange(dateSpan.range, ...extraArgs);
5721 for (let seg of segs) {
5722 seg.eventRange = eventRange;
5723 }
5724 return segs;
5725 }
5726 return [];
5727 }
5728 /*
5729 "complete" seg means it has component and eventRange
5730 */
5731 sliceEventRanges(eventRanges, extraArgs) {
5732 let segs = [];
5733 for (let eventRange of eventRanges) {
5734 segs.push(...this.sliceEventRange(eventRange, extraArgs));
5735 }
5736 return segs;
5737 }
5738 /*
5739 "complete" seg means it has component and eventRange
5740 */
5741 sliceEventRange(eventRange, extraArgs) {
5742 let dateRange = eventRange.range;
5743 // hack to make multi-day events that are being force-displayed as list-items to take up only one day
5744 if (this.forceDayIfListItem && eventRange.ui.display === 'list-item') {
5745 dateRange = {
5746 start: dateRange.start,
5747 end: addDays(dateRange.start, 1),
5748 };
5749 }
5750 let segs = this.sliceRange(dateRange, ...extraArgs);
5751 for (let seg of segs) {
5752 seg.eventRange = eventRange;
5753 seg.isStart = eventRange.isStart && seg.isStart;
5754 seg.isEnd = eventRange.isEnd && seg.isEnd;
5755 }
5756 return segs;
5757 }
5758 }
5759 /*
5760 for incorporating slotMinTime/slotMaxTime if appropriate
5761 TODO: should be part of DateProfile!
5762 TimelineDateProfile already does this btw
5763 */
5764 function computeActiveRange(dateProfile, isComponentAllDay) {
5765 let range = dateProfile.activeRange;
5766 if (isComponentAllDay) {
5767 return range;
5768 }
5769 return {
5770 start: addMs(range.start, dateProfile.slotMinTime.milliseconds),
5771 end: addMs(range.end, dateProfile.slotMaxTime.milliseconds - 864e5), // 864e5 = ms in a day
5772 };
5773 }
5774
5775 function reduceEventStore(eventStore, action, eventSources, dateProfile, context) {
5776 switch (action.type) {
5777 case 'RECEIVE_EVENTS': // raw
5778 return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, context);
5779 case 'RESET_RAW_EVENTS':
5780 return resetRawEvents(eventStore, eventSources[action.sourceId], action.rawEvents, dateProfile.activeRange, context);
5781 case 'ADD_EVENTS': // already parsed, but not expanded
5782 return addEvent(eventStore, action.eventStore, // new ones
5783 dateProfile ? dateProfile.activeRange : null, context);
5784 case 'RESET_EVENTS':
5785 return action.eventStore;
5786 case 'MERGE_EVENTS': // already parsed and expanded
5787 return mergeEventStores(eventStore, action.eventStore);
5788 case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
5789 case 'NEXT':
5790 case 'CHANGE_DATE':
5791 case 'CHANGE_VIEW_TYPE':
5792 if (dateProfile) {
5793 return expandRecurring(eventStore, dateProfile.activeRange, context);
5794 }
5795 return eventStore;
5796 case 'REMOVE_EVENTS':
5797 return excludeSubEventStore(eventStore, action.eventStore);
5798 case 'REMOVE_EVENT_SOURCE':
5799 return excludeEventsBySourceId(eventStore, action.sourceId);
5800 case 'REMOVE_ALL_EVENT_SOURCES':
5801 return filterEventStoreDefs(eventStore, (eventDef) => (!eventDef.sourceId // only keep events with no source id
5802 ));
5803 case 'REMOVE_ALL_EVENTS':
5804 return createEmptyEventStore();
5805 default:
5806 return eventStore;
5807 }
5808 }
5809 function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, context) {
5810 if (eventSource && // not already removed
5811 fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources
5812 ) {
5813 let subset = parseEvents(transformRawEvents(rawEvents, eventSource, context), eventSource, context);
5814 if (fetchRange) {
5815 subset = expandRecurring(subset, fetchRange, context);
5816 }
5817 return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset);
5818 }
5819 return eventStore;
5820 }
5821 function resetRawEvents(existingEventStore, eventSource, rawEvents, activeRange, context) {
5822 const { defIdMap, instanceIdMap } = buildPublicIdMaps(existingEventStore);
5823 let newEventStore = parseEvents(transformRawEvents(rawEvents, eventSource, context), eventSource, context, false, defIdMap, instanceIdMap);
5824 return expandRecurring(newEventStore, activeRange, context);
5825 }
5826 function transformRawEvents(rawEvents, eventSource, context) {
5827 let calEachTransform = context.options.eventDataTransform;
5828 let sourceEachTransform = eventSource ? eventSource.eventDataTransform : null;
5829 if (sourceEachTransform) {
5830 rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform);
5831 }
5832 if (calEachTransform) {
5833 rawEvents = transformEachRawEvent(rawEvents, calEachTransform);
5834 }
5835 return rawEvents;
5836 }
5837 function transformEachRawEvent(rawEvents, func) {
5838 let refinedEvents;
5839 if (!func) {
5840 refinedEvents = rawEvents;
5841 }
5842 else {
5843 refinedEvents = [];
5844 for (let rawEvent of rawEvents) {
5845 let refinedEvent = func(rawEvent);
5846 if (refinedEvent) {
5847 refinedEvents.push(refinedEvent);
5848 }
5849 else if (refinedEvent == null) {
5850 refinedEvents.push(rawEvent);
5851 } // if a different falsy value, do nothing
5852 }
5853 }
5854 return refinedEvents;
5855 }
5856 function addEvent(eventStore, subset, expandRange, context) {
5857 if (expandRange) {
5858 subset = expandRecurring(subset, expandRange, context);
5859 }
5860 return mergeEventStores(eventStore, subset);
5861 }
5862 function rezoneEventStoreDates(eventStore, oldDateEnv, newDateEnv) {
5863 let { defs } = eventStore;
5864 let instances = mapHash(eventStore.instances, (instance) => {
5865 let def = defs[instance.defId];
5866 if (def.allDay) {
5867 return instance; // isn't dependent on timezone
5868 }
5869 return Object.assign(Object.assign({}, instance), { range: {
5870 start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)),
5871 end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo)),
5872 }, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo });
5873 });
5874 return { defs, instances };
5875 }
5876 function excludeEventsBySourceId(eventStore, sourceId) {
5877 return filterEventStoreDefs(eventStore, (eventDef) => eventDef.sourceId !== sourceId);
5878 }
5879 // QUESTION: why not just return instances? do a general object-property-exclusion util
5880 function excludeInstances(eventStore, removals) {
5881 return {
5882 defs: eventStore.defs,
5883 instances: filterHash(eventStore.instances, (instance) => !removals[instance.instanceId]),
5884 };
5885 }
5886 function buildPublicIdMaps(eventStore) {
5887 const { defs, instances } = eventStore;
5888 const defIdMap = {};
5889 const instanceIdMap = {};
5890 for (let defId in defs) {
5891 const def = defs[defId];
5892 const { publicId } = def;
5893 if (publicId) {
5894 defIdMap[publicId] = defId;
5895 }
5896 }
5897 for (let instanceId in instances) {
5898 const instance = instances[instanceId];
5899 const def = defs[instance.defId];
5900 const { publicId } = def;
5901 if (publicId) {
5902 instanceIdMap[publicId] = instanceId;
5903 }
5904 }
5905 return { defIdMap, instanceIdMap };
5906 }
5907
5908 // high-level segmenting-aware tester functions
5909 // ------------------------------------------------------------------------------------------------------------------------
5910 function isInteractionValid(interaction, dateProfile, context) {
5911 let { instances } = interaction.mutatedEvents;
5912 for (let instanceId in instances) {
5913 if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) {
5914 return false;
5915 }
5916 }
5917 return isNewPropsValid({ eventDrag: interaction }, context); // HACK: the eventDrag props is used for ALL interactions
5918 }
5919 function isDateSelectionValid(dateSelection, dateProfile, context) {
5920 if (!rangeContainsRange(dateProfile.validRange, dateSelection.range)) {
5921 return false;
5922 }
5923 return isNewPropsValid({ dateSelection }, context);
5924 }
5925 function isNewPropsValid(newProps, context) {
5926 let calendarState = context.getCurrentData();
5927 let props = Object.assign({ businessHours: calendarState.businessHours, dateSelection: '', eventStore: calendarState.eventStore, eventUiBases: calendarState.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps);
5928 return (context.pluginHooks.isPropsValid || isPropsValid)(props, context);
5929 }
5930 function isPropsValid(state, context, dateSpanMeta = {}, filterConfig) {
5931 if (state.eventDrag && !isInteractionPropsValid(state, context, dateSpanMeta, filterConfig)) {
5932 return false;
5933 }
5934 if (state.dateSelection && !isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig)) {
5935 return false;
5936 }
5937 return true;
5938 }
5939 // Moving Event Validation
5940 // ------------------------------------------------------------------------------------------------------------------------
5941 function isInteractionPropsValid(state, context, dateSpanMeta, filterConfig) {
5942 let currentState = context.getCurrentData();
5943 let interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions
5944 let subjectEventStore = interaction.mutatedEvents;
5945 let subjectDefs = subjectEventStore.defs;
5946 let subjectInstances = subjectEventStore.instances;
5947 let subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ?
5948 state.eventUiBases :
5949 { '': currentState.selectionConfig });
5950 if (filterConfig) {
5951 subjectConfigs = mapHash(subjectConfigs, filterConfig);
5952 }
5953 // exclude the subject events. TODO: exclude defs too?
5954 let otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances);
5955 let otherDefs = otherEventStore.defs;
5956 let otherInstances = otherEventStore.instances;
5957 let otherConfigs = compileEventUis(otherDefs, state.eventUiBases);
5958 for (let subjectInstanceId in subjectInstances) {
5959 let subjectInstance = subjectInstances[subjectInstanceId];
5960 let subjectRange = subjectInstance.range;
5961 let subjectConfig = subjectConfigs[subjectInstance.defId];
5962 let subjectDef = subjectDefs[subjectInstance.defId];
5963 // constraint
5964 if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, context)) {
5965 return false;
5966 }
5967 // overlap
5968 let { eventOverlap } = context.options;
5969 let eventOverlapFunc = typeof eventOverlap === 'function' ? eventOverlap : null;
5970 for (let otherInstanceId in otherInstances) {
5971 let otherInstance = otherInstances[otherInstanceId];
5972 // intersect! evaluate
5973 if (rangesIntersect(subjectRange, otherInstance.range)) {
5974 let otherOverlap = otherConfigs[otherInstance.defId].overlap;
5975 // consider the other event's overlap. only do this if the subject event is a "real" event
5976 if (otherOverlap === false && interaction.isEvent) {
5977 return false;
5978 }
5979 if (subjectConfig.overlap === false) {
5980 return false;
5981 }
5982 if (eventOverlapFunc && !eventOverlapFunc(new EventImpl(context, otherDefs[otherInstance.defId], otherInstance), // still event
5983 new EventImpl(context, subjectDef, subjectInstance))) {
5984 return false;
5985 }
5986 }
5987 }
5988 // allow (a function)
5989 let calendarEventStore = currentState.eventStore; // need global-to-calendar, not local to component (splittable)state
5990 for (let subjectAllow of subjectConfig.allows) {
5991 let subjectDateSpan = Object.assign(Object.assign({}, dateSpanMeta), { range: subjectInstance.range, allDay: subjectDef.allDay });
5992 let origDef = calendarEventStore.defs[subjectDef.defId];
5993 let origInstance = calendarEventStore.instances[subjectInstanceId];
5994 let eventApi;
5995 if (origDef) { // was previously in the calendar
5996 eventApi = new EventImpl(context, origDef, origInstance);
5997 }
5998 else { // was an external event
5999 eventApi = new EventImpl(context, subjectDef); // no instance, because had no dates
6000 }
6001 if (!subjectAllow(buildDateSpanApiWithContext(subjectDateSpan, context), eventApi)) {
6002 return false;
6003 }
6004 }
6005 }
6006 return true;
6007 }
6008 // Date Selection Validation
6009 // ------------------------------------------------------------------------------------------------------------------------
6010 function isDateSelectionPropsValid(state, context, dateSpanMeta, filterConfig) {
6011 let relevantEventStore = state.eventStore;
6012 let relevantDefs = relevantEventStore.defs;
6013 let relevantInstances = relevantEventStore.instances;
6014 let selection = state.dateSelection;
6015 let selectionRange = selection.range;
6016 let { selectionConfig } = context.getCurrentData();
6017 if (filterConfig) {
6018 selectionConfig = filterConfig(selectionConfig);
6019 }
6020 // constraint
6021 if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, context)) {
6022 return false;
6023 }
6024 // overlap
6025 let { selectOverlap } = context.options;
6026 let selectOverlapFunc = typeof selectOverlap === 'function' ? selectOverlap : null;
6027 for (let relevantInstanceId in relevantInstances) {
6028 let relevantInstance = relevantInstances[relevantInstanceId];
6029 // intersect! evaluate
6030 if (rangesIntersect(selectionRange, relevantInstance.range)) {
6031 if (selectionConfig.overlap === false) {
6032 return false;
6033 }
6034 if (selectOverlapFunc && !selectOverlapFunc(new EventImpl(context, relevantDefs[relevantInstance.defId], relevantInstance), null)) {
6035 return false;
6036 }
6037 }
6038 }
6039 // allow (a function)
6040 for (let selectionAllow of selectionConfig.allows) {
6041 let fullDateSpan = Object.assign(Object.assign({}, dateSpanMeta), selection);
6042 if (!selectionAllow(buildDateSpanApiWithContext(fullDateSpan, context), null)) {
6043 return false;
6044 }
6045 }
6046 return true;
6047 }
6048 // Constraint Utils
6049 // ------------------------------------------------------------------------------------------------------------------------
6050 function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, context) {
6051 for (let constraint of constraints) {
6052 if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, context), subjectRange)) {
6053 return false;
6054 }
6055 }
6056 return true;
6057 }
6058 function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours
6059 otherEventStore, // for if constraint is an even group ID
6060 businessHoursUnexpanded, // for if constraint is 'businessHours'
6061 context) {
6062 if (constraint === 'businessHours') {
6063 return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, context));
6064 }
6065 if (typeof constraint === 'string') { // an group ID
6066 return eventStoreToRanges(filterEventStoreDefs(otherEventStore, (eventDef) => eventDef.groupId === constraint));
6067 }
6068 if (typeof constraint === 'object' && constraint) { // non-null object
6069 return eventStoreToRanges(expandRecurring(constraint, subjectRange, context));
6070 }
6071 return []; // if it's false
6072 }
6073 // TODO: move to event-store file?
6074 function eventStoreToRanges(eventStore) {
6075 let { instances } = eventStore;
6076 let ranges = [];
6077 for (let instanceId in instances) {
6078 ranges.push(instances[instanceId].range);
6079 }
6080 return ranges;
6081 }
6082 // TODO: move to geom file?
6083 function anyRangesContainRange(outerRanges, innerRange) {
6084 for (let outerRange of outerRanges) {
6085 if (rangeContainsRange(outerRange, innerRange)) {
6086 return true;
6087 }
6088 }
6089 return false;
6090 }
6091
6092 class JsonRequestError extends Error {
6093 constructor(message, response) {
6094 super(message);
6095 this.response = response;
6096 }
6097 }
6098 function requestJson(method, url, params) {
6099 method = method.toUpperCase();
6100 const fetchOptions = {
6101 method,
6102 };
6103 if (method === 'GET') {
6104 url += (url.indexOf('?') === -1 ? '?' : '&') +
6105 new URLSearchParams(params);
6106 }
6107 else {
6108 fetchOptions.body = new URLSearchParams(params);
6109 fetchOptions.headers = {
6110 'Content-Type': 'application/x-www-form-urlencoded',
6111 };
6112 }
6113 return fetch(url, fetchOptions).then((fetchRes) => {
6114 if (fetchRes.ok) {
6115 return fetchRes.json().then((parsedResponse) => {
6116 return [parsedResponse, fetchRes];
6117 }, () => {
6118 throw new JsonRequestError('Failure parsing JSON', fetchRes);
6119 });
6120 }
6121 else {
6122 throw new JsonRequestError('Request failed', fetchRes);
6123 }
6124 });
6125 }
6126
6127 class DelayedRunner {
6128 constructor(drainedOption) {
6129 this.drainedOption = drainedOption;
6130 this.isRunning = false;
6131 this.isDirty = false;
6132 this.pauseDepths = {};
6133 this.timeoutId = 0;
6134 }
6135 request(delay) {
6136 this.isDirty = true;
6137 if (!this.isPaused()) {
6138 this.clearTimeout();
6139 if (delay == null) {
6140 this.tryDrain();
6141 }
6142 else {
6143 this.timeoutId = setTimeout(// NOT OPTIMAL! TODO: look at debounce
6144 this.tryDrain.bind(this), delay);
6145 }
6146 }
6147 }
6148 pause(scope = '') {
6149 let { pauseDepths } = this;
6150 pauseDepths[scope] = (pauseDepths[scope] || 0) + 1;
6151 this.clearTimeout();
6152 }
6153 resume(scope = '', force) {
6154 let { pauseDepths } = this;
6155 if (scope in pauseDepths) {
6156 if (force) {
6157 delete pauseDepths[scope];
6158 }
6159 else {
6160 pauseDepths[scope] -= 1;
6161 let depth = pauseDepths[scope];
6162 if (depth <= 0) {
6163 delete pauseDepths[scope];
6164 }
6165 }
6166 this.tryDrain();
6167 }
6168 }
6169 isPaused() {
6170 return Object.keys(this.pauseDepths).length;
6171 }
6172 tryDrain() {
6173 if (!this.isRunning && !this.isPaused()) {
6174 this.isRunning = true;
6175 while (this.isDirty) {
6176 this.isDirty = false;
6177 this.drained(); // might set isDirty to true again
6178 }
6179 this.isRunning = false;
6180 }
6181 }
6182 clear() {
6183 this.clearTimeout();
6184 this.isDirty = false;
6185 this.pauseDepths = {};
6186 }
6187 clearTimeout() {
6188 if (this.timeoutId) {
6189 clearTimeout(this.timeoutId);
6190 this.timeoutId = 0;
6191 }
6192 }
6193 drained() {
6194 if (this.drainedOption) {
6195 this.drainedOption();
6196 }
6197 }
6198 }
6199
6200 const VISIBLE_HIDDEN_RE = /^(visible|hidden)$/;
6201 class Scroller extends BaseComponent {
6202 constructor() {
6203 super(...arguments);
6204 this.handleEl = (el) => {
6205 this.el = el;
6206 setRef(this.props.elRef, el);
6207 };
6208 }
6209 render() {
6210 let { props } = this;
6211 let { liquid, liquidIsAbsolute } = props;
6212 let isAbsolute = liquid && liquidIsAbsolute;
6213 let className = ['fc-scroller'];
6214 if (liquid) {
6215 if (liquidIsAbsolute) {
6216 className.push('fc-scroller-liquid-absolute');
6217 }
6218 else {
6219 className.push('fc-scroller-liquid');
6220 }
6221 }
6222 return (y("div", { ref: this.handleEl, className: className.join(' '), style: {
6223 overflowX: props.overflowX,
6224 overflowY: props.overflowY,
6225 left: (isAbsolute && -(props.overcomeLeft || 0)) || '',
6226 right: (isAbsolute && -(props.overcomeRight || 0)) || '',
6227 bottom: (isAbsolute && -(props.overcomeBottom || 0)) || '',
6228 marginLeft: (!isAbsolute && -(props.overcomeLeft || 0)) || '',
6229 marginRight: (!isAbsolute && -(props.overcomeRight || 0)) || '',
6230 marginBottom: (!isAbsolute && -(props.overcomeBottom || 0)) || '',
6231 maxHeight: props.maxHeight || '',
6232 } }, props.children));
6233 }
6234 needsXScrolling() {
6235 if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6236 return false;
6237 }
6238 // testing scrollWidth>clientWidth is unreliable cross-browser when pixel heights aren't integers.
6239 // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6240 // inner-child margins and absolute positioning
6241 let { el } = this;
6242 let realClientWidth = this.el.getBoundingClientRect().width - this.getYScrollbarWidth();
6243 let { children } = el;
6244 for (let i = 0; i < children.length; i += 1) {
6245 let childEl = children[i];
6246 if (childEl.getBoundingClientRect().width > realClientWidth) {
6247 return true;
6248 }
6249 }
6250 return false;
6251 }
6252 needsYScrolling() {
6253 if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6254 return false;
6255 }
6256 // testing scrollHeight>clientHeight is unreliable cross-browser when pixel heights aren't integers.
6257 // much more reliable to see if children are taller than the scroller, even tho doesn't account for
6258 // inner-child margins and absolute positioning
6259 let { el } = this;
6260 let realClientHeight = this.el.getBoundingClientRect().height - this.getXScrollbarWidth();
6261 let { children } = el;
6262 for (let i = 0; i < children.length; i += 1) {
6263 let childEl = children[i];
6264 if (childEl.getBoundingClientRect().height > realClientHeight) {
6265 return true;
6266 }
6267 }
6268 return false;
6269 }
6270 getXScrollbarWidth() {
6271 if (VISIBLE_HIDDEN_RE.test(this.props.overflowX)) {
6272 return 0;
6273 }
6274 return this.el.offsetHeight - this.el.clientHeight; // only works because we guarantee no borders. TODO: add to CSS with important?
6275 }
6276 getYScrollbarWidth() {
6277 if (VISIBLE_HIDDEN_RE.test(this.props.overflowY)) {
6278 return 0;
6279 }
6280 return this.el.offsetWidth - this.el.clientWidth; // only works because we guarantee no borders. TODO: add to CSS with important?
6281 }
6282 }
6283
6284 /*
6285 TODO: somehow infer OtherArgs from masterCallback?
6286 TODO: infer RefType from masterCallback if provided
6287 */
6288 class RefMap {
6289 constructor(masterCallback) {
6290 this.masterCallback = masterCallback;
6291 this.currentMap = {};
6292 this.depths = {};
6293 this.callbackMap = {};
6294 this.handleValue = (val, key) => {
6295 let { depths, currentMap } = this;
6296 let removed = false;
6297 let added = false;
6298 if (val !== null) {
6299 // for bug... ACTUALLY: can probably do away with this now that callers don't share numeric indices anymore
6300 removed = (key in currentMap);
6301 currentMap[key] = val;
6302 depths[key] = (depths[key] || 0) + 1;
6303 added = true;
6304 }
6305 else {
6306 depths[key] -= 1;
6307 if (!depths[key]) {
6308 delete currentMap[key];
6309 delete this.callbackMap[key];
6310 removed = true;
6311 }
6312 }
6313 if (this.masterCallback) {
6314 if (removed) {
6315 this.masterCallback(null, String(key));
6316 }
6317 if (added) {
6318 this.masterCallback(val, String(key));
6319 }
6320 }
6321 };
6322 }
6323 createRef(key) {
6324 let refCallback = this.callbackMap[key];
6325 if (!refCallback) {
6326 refCallback = this.callbackMap[key] = (val) => {
6327 this.handleValue(val, String(key));
6328 };
6329 }
6330 return refCallback;
6331 }
6332 // TODO: check callers that don't care about order. should use getAll instead
6333 // NOTE: this method has become less valuable now that we are encouraged to map order by some other index
6334 // TODO: provide ONE array-export function, buildArray, which fails on non-numeric indexes. caller can manipulate and "collect"
6335 collect(startIndex, endIndex, step) {
6336 return collectFromHash(this.currentMap, startIndex, endIndex, step);
6337 }
6338 getAll() {
6339 return hashValuesToArray(this.currentMap);
6340 }
6341 }
6342
6343 function computeShrinkWidth(chunkEls) {
6344 let shrinkCells = findElements(chunkEls, '.fc-scrollgrid-shrink');
6345 let largestWidth = 0;
6346 for (let shrinkCell of shrinkCells) {
6347 largestWidth = Math.max(largestWidth, computeSmallestCellWidth(shrinkCell));
6348 }
6349 return Math.ceil(largestWidth); // <table> elements work best with integers. round up to ensure contents fits
6350 }
6351 function getSectionHasLiquidHeight(props, sectionConfig) {
6352 return props.liquid && sectionConfig.liquid; // does the section do liquid-height? (need to have whole scrollgrid liquid-height as well)
6353 }
6354 function getAllowYScrolling(props, sectionConfig) {
6355 return sectionConfig.maxHeight != null || // if its possible for the height to max out, we might need scrollbars
6356 getSectionHasLiquidHeight(props, sectionConfig); // if the section is liquid height, it might condense enough to require scrollbars
6357 }
6358 // TODO: ONLY use `arg`. force out internal function to use same API
6359 function renderChunkContent(sectionConfig, chunkConfig, arg, isHeader) {
6360 let { expandRows } = arg;
6361 let content = typeof chunkConfig.content === 'function' ?
6362 chunkConfig.content(arg) :
6363 y('table', {
6364 role: 'presentation',
6365 className: [
6366 chunkConfig.tableClassName,
6367 sectionConfig.syncRowHeights ? 'fc-scrollgrid-sync-table' : '',
6368 ].join(' '),
6369 style: {
6370 minWidth: arg.tableMinWidth,
6371 width: arg.clientWidth,
6372 height: expandRows ? arg.clientHeight : '', // css `height` on a <table> serves as a min-height
6373 },
6374 }, arg.tableColGroupNode, y(isHeader ? 'thead' : 'tbody', {
6375 role: 'presentation',
6376 }, typeof chunkConfig.rowContent === 'function'
6377 ? chunkConfig.rowContent(arg)
6378 : chunkConfig.rowContent));
6379 return content;
6380 }
6381 function isColPropsEqual(cols0, cols1) {
6382 return isArraysEqual(cols0, cols1, isPropsEqual);
6383 }
6384 function renderMicroColGroup(cols, shrinkWidth) {
6385 let colNodes = [];
6386 /*
6387 for ColProps with spans, it would have been great to make a single <col span="">
6388 HOWEVER, Chrome was getting messing up distributing the width to <td>/<th> elements with colspans.
6389 SOLUTION: making individual <col> elements makes Chrome behave.
6390 */
6391 for (let colProps of cols) {
6392 let span = colProps.span || 1;
6393 for (let i = 0; i < span; i += 1) {
6394 colNodes.push(y("col", { style: {
6395 width: colProps.width === 'shrink' ? sanitizeShrinkWidth(shrinkWidth) : (colProps.width || ''),
6396 minWidth: colProps.minWidth || '',
6397 } }));
6398 }
6399 }
6400 return y('colgroup', {}, ...colNodes);
6401 }
6402 function sanitizeShrinkWidth(shrinkWidth) {
6403 /* why 4? if we do 0, it will kill any border, which are needed for computeSmallestCellWidth
6404 4 accounts for 2 2-pixel borders. TODO: better solution? */
6405 return shrinkWidth == null ? 4 : shrinkWidth;
6406 }
6407 function hasShrinkWidth(cols) {
6408 for (let col of cols) {
6409 if (col.width === 'shrink') {
6410 return true;
6411 }
6412 }
6413 return false;
6414 }
6415 function getScrollGridClassNames(liquid, context) {
6416 let classNames = [
6417 'fc-scrollgrid',
6418 context.theme.getClass('table'),
6419 ];
6420 if (liquid) {
6421 classNames.push('fc-scrollgrid-liquid');
6422 }
6423 return classNames;
6424 }
6425 function getSectionClassNames(sectionConfig, wholeTableVGrow) {
6426 let classNames = [
6427 'fc-scrollgrid-section',
6428 `fc-scrollgrid-section-${sectionConfig.type}`,
6429 sectionConfig.className, // used?
6430 ];
6431 if (wholeTableVGrow && sectionConfig.liquid && sectionConfig.maxHeight == null) {
6432 classNames.push('fc-scrollgrid-section-liquid');
6433 }
6434 if (sectionConfig.isSticky) {
6435 classNames.push('fc-scrollgrid-section-sticky');
6436 }
6437 return classNames;
6438 }
6439 function renderScrollShim(arg) {
6440 return (y("div", { className: "fc-scrollgrid-sticky-shim", style: {
6441 width: arg.clientWidth,
6442 minWidth: arg.tableMinWidth,
6443 } }));
6444 }
6445 function getStickyHeaderDates(options) {
6446 let { stickyHeaderDates } = options;
6447 if (stickyHeaderDates == null || stickyHeaderDates === 'auto') {
6448 stickyHeaderDates = options.height === 'auto' || options.viewHeight === 'auto';
6449 }
6450 return stickyHeaderDates;
6451 }
6452 function getStickyFooterScrollbar(options) {
6453 let { stickyFooterScrollbar } = options;
6454 if (stickyFooterScrollbar == null || stickyFooterScrollbar === 'auto') {
6455 stickyFooterScrollbar = options.height === 'auto' || options.viewHeight === 'auto';
6456 }
6457 return stickyFooterScrollbar;
6458 }
6459
6460 class SimpleScrollGrid extends BaseComponent {
6461 constructor() {
6462 super(...arguments);
6463 this.processCols = memoize((a) => a, isColPropsEqual); // so we get same `cols` props every time
6464 // yucky to memoize VNodes, but much more efficient for consumers
6465 this.renderMicroColGroup = memoize(renderMicroColGroup);
6466 this.scrollerRefs = new RefMap();
6467 this.scrollerElRefs = new RefMap(this._handleScrollerEl.bind(this));
6468 this.state = {
6469 shrinkWidth: null,
6470 forceYScrollbars: false,
6471 scrollerClientWidths: {},
6472 scrollerClientHeights: {},
6473 };
6474 // TODO: can do a really simple print-view. dont need to join rows
6475 this.handleSizing = () => {
6476 this.safeSetState(Object.assign({ shrinkWidth: this.computeShrinkWidth() }, this.computeScrollerDims()));
6477 };
6478 }
6479 render() {
6480 let { props, state, context } = this;
6481 let sectionConfigs = props.sections || [];
6482 let cols = this.processCols(props.cols);
6483 let microColGroupNode = this.renderMicroColGroup(cols, state.shrinkWidth);
6484 let classNames = getScrollGridClassNames(props.liquid, context);
6485 if (props.collapsibleWidth) {
6486 classNames.push('fc-scrollgrid-collapsible');
6487 }
6488 // TODO: make DRY
6489 let configCnt = sectionConfigs.length;
6490 let configI = 0;
6491 let currentConfig;
6492 let headSectionNodes = [];
6493 let bodySectionNodes = [];
6494 let footSectionNodes = [];
6495 while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'header') {
6496 headSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6497 configI += 1;
6498 }
6499 while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'body') {
6500 bodySectionNodes.push(this.renderSection(currentConfig, microColGroupNode, false));
6501 configI += 1;
6502 }
6503 while (configI < configCnt && (currentConfig = sectionConfigs[configI]).type === 'footer') {
6504 footSectionNodes.push(this.renderSection(currentConfig, microColGroupNode, true));
6505 configI += 1;
6506 }
6507 // firefox bug: when setting height on table and there is a thead or tfoot,
6508 // the necessary height:100% on the liquid-height body section forces the *whole* table to be taller. (bug #5524)
6509 // use getCanVGrowWithinCell as a way to detect table-stupid firefox.
6510 // if so, use a simpler dom structure, jam everything into a lone tbody.
6511 let isBuggy = !getCanVGrowWithinCell();
6512 const roleAttrs = { role: 'rowgroup' };
6513 return y('table', {
6514 role: 'grid',
6515 className: classNames.join(' '),
6516 style: { height: props.height },
6517 }, Boolean(!isBuggy && headSectionNodes.length) && y('thead', roleAttrs, ...headSectionNodes), Boolean(!isBuggy && bodySectionNodes.length) && y('tbody', roleAttrs, ...bodySectionNodes), Boolean(!isBuggy && footSectionNodes.length) && y('tfoot', roleAttrs, ...footSectionNodes), isBuggy && y('tbody', roleAttrs, ...headSectionNodes, ...bodySectionNodes, ...footSectionNodes));
6518 }
6519 renderSection(sectionConfig, microColGroupNode, isHeader) {
6520 if ('outerContent' in sectionConfig) {
6521 return (y(_, { key: sectionConfig.key }, sectionConfig.outerContent));
6522 }
6523 return (y("tr", { key: sectionConfig.key, role: "presentation", className: getSectionClassNames(sectionConfig, this.props.liquid).join(' ') }, this.renderChunkTd(sectionConfig, microColGroupNode, sectionConfig.chunk, isHeader)));
6524 }
6525 renderChunkTd(sectionConfig, microColGroupNode, chunkConfig, isHeader) {
6526 if ('outerContent' in chunkConfig) {
6527 return chunkConfig.outerContent;
6528 }
6529 let { props } = this;
6530 let { forceYScrollbars, scrollerClientWidths, scrollerClientHeights } = this.state;
6531 let needsYScrolling = getAllowYScrolling(props, sectionConfig); // TODO: do lazily. do in section config?
6532 let isLiquid = getSectionHasLiquidHeight(props, sectionConfig);
6533 // for `!props.liquid` - is WHOLE scrollgrid natural height?
6534 // TODO: do same thing in advanced scrollgrid? prolly not b/c always has horizontal scrollbars
6535 let overflowY = !props.liquid ? 'visible' :
6536 forceYScrollbars ? 'scroll' :
6537 !needsYScrolling ? 'hidden' :
6538 'auto';
6539 let sectionKey = sectionConfig.key;
6540 let content = renderChunkContent(sectionConfig, chunkConfig, {
6541 tableColGroupNode: microColGroupNode,
6542 tableMinWidth: '',
6543 clientWidth: (!props.collapsibleWidth && scrollerClientWidths[sectionKey] !== undefined) ? scrollerClientWidths[sectionKey] : null,
6544 clientHeight: scrollerClientHeights[sectionKey] !== undefined ? scrollerClientHeights[sectionKey] : null,
6545 expandRows: sectionConfig.expandRows,
6546 syncRowHeights: false,
6547 rowSyncHeights: [],
6548 reportRowHeightChange: () => { },
6549 }, isHeader);
6550 return y(isHeader ? 'th' : 'td', {
6551 ref: chunkConfig.elRef,
6552 role: 'presentation',
6553 }, y("div", { className: `fc-scroller-harness${isLiquid ? ' fc-scroller-harness-liquid' : ''}` },
6554 y(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
6555 : true }, content)));
6556 }
6557 _handleScrollerEl(scrollerEl, key) {
6558 let section = getSectionByKey(this.props.sections, key);
6559 if (section) {
6560 setRef(section.chunk.scrollerElRef, scrollerEl);
6561 }
6562 }
6563 componentDidMount() {
6564 this.handleSizing();
6565 this.context.addResizeHandler(this.handleSizing);
6566 }
6567 componentDidUpdate() {
6568 // TODO: need better solution when state contains non-sizing things
6569 this.handleSizing();
6570 }
6571 componentWillUnmount() {
6572 this.context.removeResizeHandler(this.handleSizing);
6573 }
6574 computeShrinkWidth() {
6575 return hasShrinkWidth(this.props.cols)
6576 ? computeShrinkWidth(this.scrollerElRefs.getAll())
6577 : 0;
6578 }
6579 computeScrollerDims() {
6580 let scrollbarWidth = getScrollbarWidths();
6581 let { scrollerRefs, scrollerElRefs } = this;
6582 let forceYScrollbars = false;
6583 let scrollerClientWidths = {};
6584 let scrollerClientHeights = {};
6585 for (let sectionKey in scrollerRefs.currentMap) {
6586 let scroller = scrollerRefs.currentMap[sectionKey];
6587 if (scroller && scroller.needsYScrolling()) {
6588 forceYScrollbars = true;
6589 break;
6590 }
6591 }
6592 for (let section of this.props.sections) {
6593 let sectionKey = section.key;
6594 let scrollerEl = scrollerElRefs.currentMap[sectionKey];
6595 if (scrollerEl) {
6596 let harnessEl = scrollerEl.parentNode; // TODO: weird way to get this. need harness b/c doesn't include table borders
6597 scrollerClientWidths[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().width - (forceYScrollbars
6598 ? scrollbarWidth.y // use global because scroller might not have scrollbars yet but will need them in future
6599 : 0));
6600 scrollerClientHeights[sectionKey] = Math.floor(harnessEl.getBoundingClientRect().height);
6601 }
6602 }
6603 return { forceYScrollbars, scrollerClientWidths, scrollerClientHeights };
6604 }
6605 }
6606 SimpleScrollGrid.addStateEquality({
6607 scrollerClientWidths: isPropsEqual,
6608 scrollerClientHeights: isPropsEqual,
6609 });
6610 function getSectionByKey(sections, key) {
6611 for (let section of sections) {
6612 if (section.key === key) {
6613 return section;
6614 }
6615 }
6616 return null;
6617 }
6618
6619 class EventContainer extends BaseComponent {
6620 constructor() {
6621 super(...arguments);
6622 this.handleEl = (el) => {
6623 this.el = el;
6624 if (el) {
6625 setElSeg(el, this.props.seg);
6626 }
6627 };
6628 }
6629 render() {
6630 const { props, context } = this;
6631 const { options } = context;
6632 const { seg } = props;
6633 const { eventRange } = seg;
6634 const { ui } = eventRange;
6635 const renderProps = {
6636 event: new EventImpl(context, eventRange.def, eventRange.instance),
6637 view: context.viewApi,
6638 timeText: props.timeText,
6639 textColor: ui.textColor,
6640 backgroundColor: ui.backgroundColor,
6641 borderColor: ui.borderColor,
6642 isDraggable: !props.disableDragging && computeSegDraggable(seg, context),
6643 isStartResizable: !props.disableResizing && computeSegStartResizable(seg, context),
6644 isEndResizable: !props.disableResizing && computeSegEndResizable(seg),
6645 isMirror: Boolean(props.isDragging || props.isResizing || props.isDateSelecting),
6646 isStart: Boolean(seg.isStart),
6647 isEnd: Boolean(seg.isEnd),
6648 isPast: Boolean(props.isPast),
6649 isFuture: Boolean(props.isFuture),
6650 isToday: Boolean(props.isToday),
6651 isSelected: Boolean(props.isSelected),
6652 isDragging: Boolean(props.isDragging),
6653 isResizing: Boolean(props.isResizing),
6654 };
6655 return (y(ContentContainer, Object.assign({}, props /* contains children */, { elRef: this.handleEl, elClasses: [
6656 ...getEventClassNames(renderProps),
6657 ...seg.eventRange.ui.classNames,
6658 ...(props.elClasses || []),
6659 ], renderProps: renderProps, generatorName: "eventContent", customGenerator: options.eventContent, defaultGenerator: props.defaultGenerator, classNameGenerator: options.eventClassNames, didMount: options.eventDidMount, willUnmount: options.eventWillUnmount })));
6660 }
6661 componentDidUpdate(prevProps) {
6662 if (this.el && this.props.seg !== prevProps.seg) {
6663 setElSeg(this.el, this.props.seg);
6664 }
6665 }
6666 }
6667
6668 // should not be a purecomponent
6669 class StandardEvent extends BaseComponent {
6670 render() {
6671 let { props, context } = this;
6672 let { options } = context;
6673 let { seg } = props;
6674 let { ui } = seg.eventRange;
6675 let timeFormat = options.eventTimeFormat || props.defaultTimeFormat;
6676 let timeText = buildSegTimeText(seg, timeFormat, context, props.defaultDisplayEventTime, props.defaultDisplayEventEnd);
6677 return (y(EventContainer, Object.assign({}, props /* includes elRef */, { elTag: "a", elStyle: {
6678 borderColor: ui.borderColor,
6679 backgroundColor: ui.backgroundColor,
6680 }, elAttrs: getSegAnchorAttrs(seg, context), defaultGenerator: renderInnerContent$1, timeText: timeText }), (InnerContent, eventContentArg) => (y(_, null,
6681 y(InnerContent, { elTag: "div", elClasses: ['fc-event-main'], elStyle: { color: eventContentArg.textColor } }),
6682 Boolean(eventContentArg.isStartResizable) && (y("div", { className: "fc-event-resizer fc-event-resizer-start" })),
6683 Boolean(eventContentArg.isEndResizable) && (y("div", { className: "fc-event-resizer fc-event-resizer-end" }))))));
6684 }
6685 }
6686 function renderInnerContent$1(innerProps) {
6687 return (y("div", { className: "fc-event-main-frame" },
6688 innerProps.timeText && (y("div", { className: "fc-event-time" }, innerProps.timeText)),
6689 y("div", { className: "fc-event-title-container" },
6690 y("div", { className: "fc-event-title fc-sticky" }, innerProps.event.title || y(_, null, "\u00A0")))));
6691 }
6692
6693 const NowIndicatorContainer = (props) => (y(ViewContextType.Consumer, null, (context) => {
6694 let { options } = context;
6695 let renderProps = {
6696 isAxis: props.isAxis,
6697 date: context.dateEnv.toDate(props.date),
6698 view: context.viewApi,
6699 };
6700 return (y(ContentContainer, Object.assign({}, props /* includes children */, { elTag: props.elTag || 'div', renderProps: renderProps, generatorName: "nowIndicatorContent", customGenerator: options.nowIndicatorContent, classNameGenerator: options.nowIndicatorClassNames, didMount: options.nowIndicatorDidMount, willUnmount: options.nowIndicatorWillUnmount })));
6701 }));
6702
6703 const DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
6704 class DayCellContainer extends BaseComponent {
6705 constructor() {
6706 super(...arguments);
6707 this.refineRenderProps = memoizeObjArg(refineRenderProps);
6708 }
6709 render() {
6710 let { props, context } = this;
6711 let { options } = context;
6712 let renderProps = this.refineRenderProps({
6713 date: props.date,
6714 dateProfile: props.dateProfile,
6715 todayRange: props.todayRange,
6716 isMonthStart: props.isMonthStart || false,
6717 showDayNumber: props.showDayNumber,
6718 extraRenderProps: props.extraRenderProps,
6719 viewApi: context.viewApi,
6720 dateEnv: context.dateEnv,
6721 monthStartFormat: options.monthStartFormat,
6722 });
6723 return (y(ContentContainer, Object.assign({}, props /* includes children */, { elClasses: [
6724 ...getDayClassNames(renderProps, context.theme),
6725 ...(props.elClasses || []),
6726 ], elAttrs: Object.assign(Object.assign({}, props.elAttrs), (renderProps.isDisabled ? {} : { 'data-date': formatDayString(props.date) })), renderProps: renderProps, generatorName: "dayCellContent", customGenerator: options.dayCellContent, defaultGenerator: props.defaultGenerator, classNameGenerator:
6727 // don't use custom classNames if disabled
6728 renderProps.isDisabled ? undefined : options.dayCellClassNames, didMount: options.dayCellDidMount, willUnmount: options.dayCellWillUnmount })));
6729 }
6730 }
6731 function hasCustomDayCellContent(options) {
6732 return Boolean(options.dayCellContent || hasCustomRenderingHandler('dayCellContent', options));
6733 }
6734 function refineRenderProps(raw) {
6735 let { date, dateEnv, dateProfile, isMonthStart } = raw;
6736 let dayMeta = getDateMeta(date, raw.todayRange, null, dateProfile);
6737 let dayNumberText = raw.showDayNumber ? (dateEnv.format(date, isMonthStart ? raw.monthStartFormat : DAY_NUM_FORMAT)) : '';
6738 return Object.assign(Object.assign(Object.assign({ date: dateEnv.toDate(date), view: raw.viewApi }, dayMeta), { isMonthStart,
6739 dayNumberText }), raw.extraRenderProps);
6740 }
6741
6742 class BgEvent extends BaseComponent {
6743 render() {
6744 let { props } = this;
6745 let { seg } = props;
6746 return (y(EventContainer, { elTag: "div", elClasses: ['fc-bg-event'], elStyle: { backgroundColor: seg.eventRange.ui.backgroundColor }, defaultGenerator: renderInnerContent, seg: seg, timeText: "", isDragging: false, isResizing: false, isDateSelecting: false, isSelected: false, isPast: props.isPast, isFuture: props.isFuture, isToday: props.isToday, disableDragging: true, disableResizing: true }));
6747 }
6748 }
6749 function renderInnerContent(props) {
6750 let { title } = props.event;
6751 return title && (y("div", { className: "fc-event-title" }, props.event.title));
6752 }
6753 function renderFill(fillType) {
6754 return (y("div", { className: `fc-${fillType}` }));
6755 }
6756
6757 const WeekNumberContainer = (props) => (y(ViewContextType.Consumer, null, (context) => {
6758 let { dateEnv, options } = context;
6759 let { date } = props;
6760 let format = options.weekNumberFormat || props.defaultFormat;
6761 let num = dateEnv.computeWeekNumber(date); // TODO: somehow use for formatting as well?
6762 let text = dateEnv.format(date, format);
6763 let renderProps = { num, text, date };
6764 return (y(ContentContainer // why isn't WeekNumberContentArg being auto-detected?
6765 , Object.assign({}, props /* includes children */, { renderProps: renderProps, generatorName: "weekNumberContent", customGenerator: options.weekNumberContent, defaultGenerator: renderInner, classNameGenerator: options.weekNumberClassNames, didMount: options.weekNumberDidMount, willUnmount: options.weekNumberWillUnmount })));
6766 }));
6767 function renderInner(innerProps) {
6768 return innerProps.text;
6769 }
6770
6771 const PADDING_FROM_VIEWPORT = 10;
6772 class Popover extends BaseComponent {
6773 constructor() {
6774 super(...arguments);
6775 this.state = {
6776 titleId: getUniqueDomId(),
6777 };
6778 this.handleRootEl = (el) => {
6779 this.rootEl = el;
6780 if (this.props.elRef) {
6781 setRef(this.props.elRef, el);
6782 }
6783 };
6784 // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
6785 this.handleDocumentMouseDown = (ev) => {
6786 // only hide the popover if the click happened outside the popover
6787 const target = getEventTargetViaRoot(ev);
6788 if (!this.rootEl.contains(target)) {
6789 this.handleCloseClick();
6790 }
6791 };
6792 this.handleDocumentKeyDown = (ev) => {
6793 if (ev.key === 'Escape') {
6794 this.handleCloseClick();
6795 }
6796 };
6797 this.handleCloseClick = () => {
6798 let { onClose } = this.props;
6799 if (onClose) {
6800 onClose();
6801 }
6802 };
6803 }
6804 render() {
6805 let { theme, options } = this.context;
6806 let { props, state } = this;
6807 let classNames = [
6808 'fc-popover',
6809 theme.getClass('popover'),
6810 ].concat(props.extraClassNames || []);
6811 return j(y("div", Object.assign({}, props.extraAttrs, { id: props.id, className: classNames.join(' '), "aria-labelledby": state.titleId, ref: this.handleRootEl }),
6812 y("div", { className: 'fc-popover-header ' + theme.getClass('popoverHeader') },
6813 y("span", { className: "fc-popover-title", id: state.titleId }, props.title),
6814 y("span", { className: 'fc-popover-close ' + theme.getIconClass('close'), title: options.closeHint, onClick: this.handleCloseClick })),
6815 y("div", { className: 'fc-popover-body ' + theme.getClass('popoverContent') }, props.children)), props.parentEl);
6816 }
6817 componentDidMount() {
6818 document.addEventListener('mousedown', this.handleDocumentMouseDown);
6819 document.addEventListener('keydown', this.handleDocumentKeyDown);
6820 this.updateSize();
6821 }
6822 componentWillUnmount() {
6823 document.removeEventListener('mousedown', this.handleDocumentMouseDown);
6824 document.removeEventListener('keydown', this.handleDocumentKeyDown);
6825 }
6826 updateSize() {
6827 let { isRtl } = this.context;
6828 let { alignmentEl, alignGridTop } = this.props;
6829 let { rootEl } = this;
6830 let alignmentRect = computeClippedClientRect(alignmentEl);
6831 if (alignmentRect) {
6832 let popoverDims = rootEl.getBoundingClientRect();
6833 // position relative to viewport
6834 let popoverTop = alignGridTop
6835 ? elementClosest(alignmentEl, '.fc-scrollgrid').getBoundingClientRect().top
6836 : alignmentRect.top;
6837 let popoverLeft = isRtl ? alignmentRect.right - popoverDims.width : alignmentRect.left;
6838 // constrain
6839 popoverTop = Math.max(popoverTop, PADDING_FROM_VIEWPORT);
6840 popoverLeft = Math.min(popoverLeft, document.documentElement.clientWidth - PADDING_FROM_VIEWPORT - popoverDims.width);
6841 popoverLeft = Math.max(popoverLeft, PADDING_FROM_VIEWPORT);
6842 let origin = rootEl.offsetParent.getBoundingClientRect();
6843 applyStyle(rootEl, {
6844 top: popoverTop - origin.top,
6845 left: popoverLeft - origin.left,
6846 });
6847 }
6848 }
6849 }
6850
6851 class MorePopover extends DateComponent {
6852 constructor() {
6853 super(...arguments);
6854 this.handleRootEl = (rootEl) => {
6855 this.rootEl = rootEl;
6856 if (rootEl) {
6857 this.context.registerInteractiveComponent(this, {
6858 el: rootEl,
6859 useEventCenter: false,
6860 });
6861 }
6862 else {
6863 this.context.unregisterInteractiveComponent(this);
6864 }
6865 };
6866 }
6867 render() {
6868 let { options, dateEnv } = this.context;
6869 let { props } = this;
6870 let { startDate, todayRange, dateProfile } = props;
6871 let title = dateEnv.format(startDate, options.dayPopoverFormat);
6872 return (y(DayCellContainer, { elRef: this.handleRootEl, date: startDate, dateProfile: dateProfile, todayRange: todayRange }, (InnerContent, renderProps, elAttrs) => (y(Popover, { elRef: elAttrs.ref, id: props.id, title: title, extraClassNames: ['fc-more-popover'].concat(elAttrs.className || []), extraAttrs: elAttrs /* TODO: make these time-based when not whole-day? */, parentEl: props.parentEl, alignmentEl: props.alignmentEl, alignGridTop: props.alignGridTop, onClose: props.onClose },
6873 hasCustomDayCellContent(options) && (y(InnerContent, { elTag: "div", elClasses: ['fc-more-popover-misc'] })),
6874 props.children))));
6875 }
6876 queryHit(positionLeft, positionTop, elWidth, elHeight) {
6877 let { rootEl, props } = this;
6878 if (positionLeft >= 0 && positionLeft < elWidth &&
6879 positionTop >= 0 && positionTop < elHeight) {
6880 return {
6881 dateProfile: props.dateProfile,
6882 dateSpan: Object.assign({ allDay: !props.forceTimed, range: {
6883 start: props.startDate,
6884 end: props.endDate,
6885 } }, props.extraDateSpan),
6886 dayEl: rootEl,
6887 rect: {
6888 left: 0,
6889 top: 0,
6890 right: elWidth,
6891 bottom: elHeight,
6892 },
6893 layer: 1, // important when comparing with hits from other components
6894 };
6895 }
6896 return null;
6897 }
6898 }
6899
6900 class MoreLinkContainer extends BaseComponent {
6901 constructor() {
6902 super(...arguments);
6903 this.state = {
6904 isPopoverOpen: false,
6905 popoverId: getUniqueDomId(),
6906 };
6907 this.handleLinkEl = (linkEl) => {
6908 this.linkEl = linkEl;
6909 if (this.props.elRef) {
6910 setRef(this.props.elRef, linkEl);
6911 }
6912 };
6913 this.handleClick = (ev) => {
6914 let { props, context } = this;
6915 let { moreLinkClick } = context.options;
6916 let date = computeRange(props).start;
6917 function buildPublicSeg(seg) {
6918 let { def, instance, range } = seg.eventRange;
6919 return {
6920 event: new EventImpl(context, def, instance),
6921 start: context.dateEnv.toDate(range.start),
6922 end: context.dateEnv.toDate(range.end),
6923 isStart: seg.isStart,
6924 isEnd: seg.isEnd,
6925 };
6926 }
6927 if (typeof moreLinkClick === 'function') {
6928 moreLinkClick = moreLinkClick({
6929 date,
6930 allDay: Boolean(props.allDayDate),
6931 allSegs: props.allSegs.map(buildPublicSeg),
6932 hiddenSegs: props.hiddenSegs.map(buildPublicSeg),
6933 jsEvent: ev,
6934 view: context.viewApi,
6935 });
6936 }
6937 if (!moreLinkClick || moreLinkClick === 'popover') {
6938 this.setState({ isPopoverOpen: true });
6939 }
6940 else if (typeof moreLinkClick === 'string') { // a view name
6941 context.calendarApi.zoomTo(date, moreLinkClick);
6942 }
6943 };
6944 this.handlePopoverClose = () => {
6945 this.setState({ isPopoverOpen: false });
6946 };
6947 }
6948 render() {
6949 let { props, state } = this;
6950 return (y(ViewContextType.Consumer, null, (context) => {
6951 let { viewApi, options, calendarApi } = context;
6952 let { moreLinkText } = options;
6953 let { moreCnt } = props;
6954 let range = computeRange(props);
6955 let text = typeof moreLinkText === 'function' // TODO: eventually use formatWithOrdinals
6956 ? moreLinkText.call(calendarApi, moreCnt)
6957 : `+${moreCnt} ${moreLinkText}`;
6958 let hint = formatWithOrdinals(options.moreLinkHint, [moreCnt], text);
6959 let renderProps = {
6960 num: moreCnt,
6961 shortText: `+${moreCnt}`,
6962 text,
6963 view: viewApi,
6964 };
6965 return (y(_, null,
6966 Boolean(props.moreCnt) && (y(ContentContainer, { elTag: props.elTag || 'a', elRef: this.handleLinkEl, elClasses: [
6967 ...(props.elClasses || []),
6968 'fc-more-link',
6969 ], elStyle: props.elStyle, elAttrs: Object.assign(Object.assign(Object.assign({}, props.elAttrs), createAriaClickAttrs(this.handleClick)), { title: hint, 'aria-expanded': state.isPopoverOpen, 'aria-controls': state.isPopoverOpen ? state.popoverId : '' }), renderProps: renderProps, generatorName: "moreLinkContent", customGenerator: options.moreLinkContent, defaultGenerator: props.defaultGenerator || renderMoreLinkInner, classNameGenerator: options.moreLinkClassNames, didMount: options.moreLinkDidMount, willUnmount: options.moreLinkWillUnmount }, props.children)),
6970 state.isPopoverOpen && (y(MorePopover, { id: state.popoverId, startDate: range.start, endDate: range.end, dateProfile: props.dateProfile, todayRange: props.todayRange, extraDateSpan: props.extraDateSpan, parentEl: this.parentEl, alignmentEl: props.alignmentElRef ?
6971 props.alignmentElRef.current :
6972 this.linkEl, alignGridTop: props.alignGridTop, forceTimed: props.forceTimed, onClose: this.handlePopoverClose }, props.popoverContent()))));
6973 }));
6974 }
6975 componentDidMount() {
6976 this.updateParentEl();
6977 }
6978 componentDidUpdate() {
6979 this.updateParentEl();
6980 }
6981 updateParentEl() {
6982 if (this.linkEl) {
6983 this.parentEl = elementClosest(this.linkEl, '.fc-view-harness');
6984 }
6985 }
6986 }
6987 function renderMoreLinkInner(props) {
6988 return props.text;
6989 }
6990 function computeRange(props) {
6991 if (props.allDayDate) {
6992 return {
6993 start: props.allDayDate,
6994 end: addDays(props.allDayDate, 1),
6995 };
6996 }
6997 let { hiddenSegs } = props;
6998 return {
6999 start: computeEarliestSegStart(hiddenSegs),
7000 end: computeLatestSegEnd(hiddenSegs),
7001 };
7002 }
7003 function computeEarliestSegStart(segs) {
7004 return segs.reduce(pickEarliestStart).eventRange.range.start;
7005 }
7006 function pickEarliestStart(seg0, seg1) {
7007 return seg0.eventRange.range.start < seg1.eventRange.range.start ? seg0 : seg1;
7008 }
7009 function computeLatestSegEnd(segs) {
7010 return segs.reduce(pickLatestEnd).eventRange.range.end;
7011 }
7012 function pickLatestEnd(seg0, seg1) {
7013 return seg0.eventRange.range.end > seg1.eventRange.range.end ? seg0 : seg1;
7014 }
7015
7016 class ViewContainer extends BaseComponent {
7017 render() {
7018 let { props, context } = this;
7019 let { options } = context;
7020 let renderProps = { view: context.viewApi };
7021 return (y(ContentContainer, Object.assign({}, props, { elTag: props.elTag || 'div', elClasses: [
7022 ...buildViewClassNames(props.viewSpec),
7023 ...(props.elClasses || []),
7024 ], renderProps: renderProps, classNameGenerator: options.viewClassNames, generatorName: undefined, didMount: options.viewDidMount, willUnmount: options.viewWillUnmount }), () => props.children));
7025 }
7026 }
7027 function buildViewClassNames(viewSpec) {
7028 return [
7029 `fc-${viewSpec.type}-view`,
7030 'fc-view',
7031 ];
7032 }
7033
7034 const EVENT_SOURCE_REFINERS = {
7035 id: String,
7036 defaultAllDay: Boolean,
7037 url: String,
7038 format: String,
7039 events: identity,
7040 eventDataTransform: identity,
7041 // for any network-related sources
7042 success: identity,
7043 failure: identity,
7044 };
7045 function parseEventSource(raw, context, refiners = buildEventSourceRefiners(context)) {
7046 let rawObj;
7047 if (typeof raw === 'string') {
7048 rawObj = { url: raw };
7049 }
7050 else if (typeof raw === 'function' || Array.isArray(raw)) {
7051 rawObj = { events: raw };
7052 }
7053 else if (typeof raw === 'object' && raw) { // not null
7054 rawObj = raw;
7055 }
7056 if (rawObj) {
7057 let { refined, extra } = refineProps(rawObj, refiners);
7058 let metaRes = buildEventSourceMeta(refined, context);
7059 if (metaRes) {
7060 return {
7061 _raw: raw,
7062 isFetching: false,
7063 latestFetchId: '',
7064 fetchRange: null,
7065 defaultAllDay: refined.defaultAllDay,
7066 eventDataTransform: refined.eventDataTransform,
7067 success: refined.success,
7068 failure: refined.failure,
7069 publicId: refined.id || '',
7070 sourceId: guid(),
7071 sourceDefId: metaRes.sourceDefId,
7072 meta: metaRes.meta,
7073 ui: createEventUi(refined, context),
7074 extendedProps: extra,
7075 };
7076 }
7077 }
7078 return null;
7079 }
7080 function buildEventSourceRefiners(context) {
7081 return Object.assign(Object.assign(Object.assign({}, EVENT_UI_REFINERS), EVENT_SOURCE_REFINERS), context.pluginHooks.eventSourceRefiners);
7082 }
7083 function buildEventSourceMeta(raw, context) {
7084 let defs = context.pluginHooks.eventSourceDefs;
7085 for (let i = defs.length - 1; i >= 0; i -= 1) { // later-added plugins take precedence
7086 let def = defs[i];
7087 let meta = def.parseMeta(raw);
7088 if (meta) {
7089 return { sourceDefId: i, meta };
7090 }
7091 }
7092 return null;
7093 }
7094
7095 class CalendarImpl {
7096 getCurrentData() {
7097 return this.currentDataManager.getCurrentData();
7098 }
7099 dispatch(action) {
7100 this.currentDataManager.dispatch(action);
7101 }
7102 get view() { return this.getCurrentData().viewApi; }
7103 batchRendering(callback) {
7104 callback();
7105 }
7106 updateSize() {
7107 this.trigger('_resize', true);
7108 }
7109 // Options
7110 // -----------------------------------------------------------------------------------------------------------------
7111 setOption(name, val) {
7112 this.dispatch({
7113 type: 'SET_OPTION',
7114 optionName: name,
7115 rawOptionValue: val,
7116 });
7117 }
7118 getOption(name) {
7119 return this.currentDataManager.currentCalendarOptionsInput[name];
7120 }
7121 getAvailableLocaleCodes() {
7122 return Object.keys(this.getCurrentData().availableRawLocales);
7123 }
7124 // Trigger
7125 // -----------------------------------------------------------------------------------------------------------------
7126 on(handlerName, handler) {
7127 let { currentDataManager } = this;
7128 if (currentDataManager.currentCalendarOptionsRefiners[handlerName]) {
7129 currentDataManager.emitter.on(handlerName, handler);
7130 }
7131 else {
7132 console.warn(`Unknown listener name '${handlerName}'`);
7133 }
7134 }
7135 off(handlerName, handler) {
7136 this.currentDataManager.emitter.off(handlerName, handler);
7137 }
7138 // not meant for public use
7139 trigger(handlerName, ...args) {
7140 this.currentDataManager.emitter.trigger(handlerName, ...args);
7141 }
7142 // View
7143 // -----------------------------------------------------------------------------------------------------------------
7144 changeView(viewType, dateOrRange) {
7145 this.batchRendering(() => {
7146 this.unselect();
7147 if (dateOrRange) {
7148 if (dateOrRange.start && dateOrRange.end) { // a range
7149 this.dispatch({
7150 type: 'CHANGE_VIEW_TYPE',
7151 viewType,
7152 });
7153 this.dispatch({
7154 type: 'SET_OPTION',
7155 optionName: 'visibleRange',
7156 rawOptionValue: dateOrRange,
7157 });
7158 }
7159 else {
7160 let { dateEnv } = this.getCurrentData();
7161 this.dispatch({
7162 type: 'CHANGE_VIEW_TYPE',
7163 viewType,
7164 dateMarker: dateEnv.createMarker(dateOrRange),
7165 });
7166 }
7167 }
7168 else {
7169 this.dispatch({
7170 type: 'CHANGE_VIEW_TYPE',
7171 viewType,
7172 });
7173 }
7174 });
7175 }
7176 // Forces navigation to a view for the given date.
7177 // `viewType` can be a specific view name or a generic one like "week" or "day".
7178 // needs to change
7179 zoomTo(dateMarker, viewType) {
7180 let state = this.getCurrentData();
7181 let spec;
7182 viewType = viewType || 'day'; // day is default zoom
7183 spec = state.viewSpecs[viewType] || this.getUnitViewSpec(viewType);
7184 this.unselect();
7185 if (spec) {
7186 this.dispatch({
7187 type: 'CHANGE_VIEW_TYPE',
7188 viewType: spec.type,
7189 dateMarker,
7190 });
7191 }
7192 else {
7193 this.dispatch({
7194 type: 'CHANGE_DATE',
7195 dateMarker,
7196 });
7197 }
7198 }
7199 // Given a duration singular unit, like "week" or "day", finds a matching view spec.
7200 // Preference is given to views that have corresponding buttons.
7201 getUnitViewSpec(unit) {
7202 let { viewSpecs, toolbarConfig } = this.getCurrentData();
7203 let viewTypes = [].concat(toolbarConfig.header ? toolbarConfig.header.viewsWithButtons : [], toolbarConfig.footer ? toolbarConfig.footer.viewsWithButtons : []);
7204 let i;
7205 let spec;
7206 for (let viewType in viewSpecs) {
7207 viewTypes.push(viewType);
7208 }
7209 for (i = 0; i < viewTypes.length; i += 1) {
7210 spec = viewSpecs[viewTypes[i]];
7211 if (spec) {
7212 if (spec.singleUnit === unit) {
7213 return spec;
7214 }
7215 }
7216 }
7217 return null;
7218 }
7219 // Current Date
7220 // -----------------------------------------------------------------------------------------------------------------
7221 prev() {
7222 this.unselect();
7223 this.dispatch({ type: 'PREV' });
7224 }
7225 next() {
7226 this.unselect();
7227 this.dispatch({ type: 'NEXT' });
7228 }
7229 prevYear() {
7230 let state = this.getCurrentData();
7231 this.unselect();
7232 this.dispatch({
7233 type: 'CHANGE_DATE',
7234 dateMarker: state.dateEnv.addYears(state.currentDate, -1),
7235 });
7236 }
7237 nextYear() {
7238 let state = this.getCurrentData();
7239 this.unselect();
7240 this.dispatch({
7241 type: 'CHANGE_DATE',
7242 dateMarker: state.dateEnv.addYears(state.currentDate, 1),
7243 });
7244 }
7245 today() {
7246 let state = this.getCurrentData();
7247 this.unselect();
7248 this.dispatch({
7249 type: 'CHANGE_DATE',
7250 dateMarker: getNow(state.calendarOptions.now, state.dateEnv),
7251 });
7252 }
7253 gotoDate(zonedDateInput) {
7254 let state = this.getCurrentData();
7255 this.unselect();
7256 this.dispatch({
7257 type: 'CHANGE_DATE',
7258 dateMarker: state.dateEnv.createMarker(zonedDateInput),
7259 });
7260 }
7261 incrementDate(deltaInput) {
7262 let state = this.getCurrentData();
7263 let delta = createDuration(deltaInput);
7264 if (delta) { // else, warn about invalid input?
7265 this.unselect();
7266 this.dispatch({
7267 type: 'CHANGE_DATE',
7268 dateMarker: state.dateEnv.add(state.currentDate, delta),
7269 });
7270 }
7271 }
7272 getDate() {
7273 let state = this.getCurrentData();
7274 return state.dateEnv.toDate(state.currentDate);
7275 }
7276 // Date Formatting Utils
7277 // -----------------------------------------------------------------------------------------------------------------
7278 formatDate(d, formatter) {
7279 let { dateEnv } = this.getCurrentData();
7280 return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
7281 }
7282 // `settings` is for formatter AND isEndExclusive
7283 formatRange(d0, d1, settings) {
7284 let { dateEnv } = this.getCurrentData();
7285 return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings), settings);
7286 }
7287 formatIso(d, omitTime) {
7288 let { dateEnv } = this.getCurrentData();
7289 return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime });
7290 }
7291 // Date Selection / Event Selection / DayClick
7292 // -----------------------------------------------------------------------------------------------------------------
7293 select(dateOrObj, endDate) {
7294 let selectionInput;
7295 if (endDate == null) {
7296 if (dateOrObj.start != null) {
7297 selectionInput = dateOrObj;
7298 }
7299 else {
7300 selectionInput = {
7301 start: dateOrObj,
7302 end: null,
7303 };
7304 }
7305 }
7306 else {
7307 selectionInput = {
7308 start: dateOrObj,
7309 end: endDate,
7310 };
7311 }
7312 let state = this.getCurrentData();
7313 let selection = parseDateSpan(selectionInput, state.dateEnv, createDuration({ days: 1 }));
7314 if (selection) { // throw parse error otherwise?
7315 this.dispatch({ type: 'SELECT_DATES', selection });
7316 triggerDateSelect(selection, null, state);
7317 }
7318 }
7319 unselect(pev) {
7320 let state = this.getCurrentData();
7321 if (state.dateSelection) {
7322 this.dispatch({ type: 'UNSELECT_DATES' });
7323 triggerDateUnselect(pev, state);
7324 }
7325 }
7326 // Public Events API
7327 // -----------------------------------------------------------------------------------------------------------------
7328 addEvent(eventInput, sourceInput) {
7329 if (eventInput instanceof EventImpl) {
7330 let def = eventInput._def;
7331 let instance = eventInput._instance;
7332 let currentData = this.getCurrentData();
7333 // not already present? don't want to add an old snapshot
7334 if (!currentData.eventStore.defs[def.defId]) {
7335 this.dispatch({
7336 type: 'ADD_EVENTS',
7337 eventStore: eventTupleToStore({ def, instance }), // TODO: better util for two args?
7338 });
7339 this.triggerEventAdd(eventInput);
7340 }
7341 return eventInput;
7342 }
7343 let state = this.getCurrentData();
7344 let eventSource;
7345 if (sourceInput instanceof EventSourceImpl) {
7346 eventSource = sourceInput.internalEventSource;
7347 }
7348 else if (typeof sourceInput === 'boolean') {
7349 if (sourceInput) { // true. part of the first event source
7350 [eventSource] = hashValuesToArray(state.eventSources);
7351 }
7352 }
7353 else if (sourceInput != null) { // an ID. accepts a number too
7354 let sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
7355 if (!sourceApi) {
7356 console.warn(`Could not find an event source with ID "${sourceInput}"`); // TODO: test
7357 return null;
7358 }
7359 eventSource = sourceApi.internalEventSource;
7360 }
7361 let tuple = parseEvent(eventInput, eventSource, state, false);
7362 if (tuple) {
7363 let newEventApi = new EventImpl(state, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
7364 this.dispatch({
7365 type: 'ADD_EVENTS',
7366 eventStore: eventTupleToStore(tuple),
7367 });
7368 this.triggerEventAdd(newEventApi);
7369 return newEventApi;
7370 }
7371 return null;
7372 }
7373 triggerEventAdd(eventApi) {
7374 let { emitter } = this.getCurrentData();
7375 emitter.trigger('eventAdd', {
7376 event: eventApi,
7377 relatedEvents: [],
7378 revert: () => {
7379 this.dispatch({
7380 type: 'REMOVE_EVENTS',
7381 eventStore: eventApiToStore(eventApi),
7382 });
7383 },
7384 });
7385 }
7386 // TODO: optimize
7387 getEventById(id) {
7388 let state = this.getCurrentData();
7389 let { defs, instances } = state.eventStore;
7390 id = String(id);
7391 for (let defId in defs) {
7392 let def = defs[defId];
7393 if (def.publicId === id) {
7394 if (def.recurringDef) {
7395 return new EventImpl(state, def, null);
7396 }
7397 for (let instanceId in instances) {
7398 let instance = instances[instanceId];
7399 if (instance.defId === def.defId) {
7400 return new EventImpl(state, def, instance);
7401 }
7402 }
7403 }
7404 }
7405 return null;
7406 }
7407 getEvents() {
7408 let currentData = this.getCurrentData();
7409 return buildEventApis(currentData.eventStore, currentData);
7410 }
7411 removeAllEvents() {
7412 this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
7413 }
7414 // Public Event Sources API
7415 // -----------------------------------------------------------------------------------------------------------------
7416 getEventSources() {
7417 let state = this.getCurrentData();
7418 let sourceHash = state.eventSources;
7419 let sourceApis = [];
7420 for (let internalId in sourceHash) {
7421 sourceApis.push(new EventSourceImpl(state, sourceHash[internalId]));
7422 }
7423 return sourceApis;
7424 }
7425 getEventSourceById(id) {
7426 let state = this.getCurrentData();
7427 let sourceHash = state.eventSources;
7428 id = String(id);
7429 for (let sourceId in sourceHash) {
7430 if (sourceHash[sourceId].publicId === id) {
7431 return new EventSourceImpl(state, sourceHash[sourceId]);
7432 }
7433 }
7434 return null;
7435 }
7436 addEventSource(sourceInput) {
7437 let state = this.getCurrentData();
7438 if (sourceInput instanceof EventSourceImpl) {
7439 // not already present? don't want to add an old snapshot
7440 if (!state.eventSources[sourceInput.internalEventSource.sourceId]) {
7441 this.dispatch({
7442 type: 'ADD_EVENT_SOURCES',
7443 sources: [sourceInput.internalEventSource],
7444 });
7445 }
7446 return sourceInput;
7447 }
7448 let eventSource = parseEventSource(sourceInput, state);
7449 if (eventSource) { // TODO: error otherwise?
7450 this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
7451 return new EventSourceImpl(state, eventSource);
7452 }
7453 return null;
7454 }
7455 removeAllEventSources() {
7456 this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
7457 }
7458 refetchEvents() {
7459 this.dispatch({ type: 'FETCH_EVENT_SOURCES', isRefetch: true });
7460 }
7461 // Scroll
7462 // -----------------------------------------------------------------------------------------------------------------
7463 scrollToTime(timeInput) {
7464 let time = createDuration(timeInput);
7465 if (time) {
7466 this.trigger('_scrollRequest', { time });
7467 }
7468 }
7469 }
7470
7471 class Store {
7472 constructor() {
7473 this.handlers = [];
7474 }
7475 set(value) {
7476 this.currentValue = value;
7477 for (let handler of this.handlers) {
7478 handler(value);
7479 }
7480 }
7481 subscribe(handler) {
7482 this.handlers.push(handler);
7483 if (this.currentValue !== undefined) {
7484 handler(this.currentValue);
7485 }
7486 }
7487 }
7488
7489 /*
7490 Subscribers will get a LIST of CustomRenderings
7491 */
7492 class CustomRenderingStore extends Store {
7493 constructor() {
7494 super(...arguments);
7495 this.map = new Map();
7496 }
7497 // for consistent order
7498 handle(customRendering) {
7499 const { map } = this;
7500 let updated = false;
7501 if (customRendering.isActive) {
7502 map.set(customRendering.id, customRendering);
7503 updated = true;
7504 }
7505 else if (map.has(customRendering.id)) {
7506 map.delete(customRendering.id);
7507 updated = true;
7508 }
7509 if (updated) {
7510 this.set(map);
7511 }
7512 }
7513 }
7514
7515 var internal = {
7516 __proto__: null,
7517 BASE_OPTION_DEFAULTS: BASE_OPTION_DEFAULTS,
7518 identity: identity,
7519 refineProps: refineProps,
7520 createEventInstance: createEventInstance,
7521 parseEventDef: parseEventDef,
7522 refineEventDef: refineEventDef,
7523 parseBusinessHours: parseBusinessHours,
7524 padStart: padStart,
7525 isInt: isInt,
7526 parseFieldSpecs: parseFieldSpecs,
7527 compareByFieldSpecs: compareByFieldSpecs,
7528 flexibleCompare: flexibleCompare,
7529 preventSelection: preventSelection,
7530 allowSelection: allowSelection,
7531 preventContextMenu: preventContextMenu,
7532 allowContextMenu: allowContextMenu,
7533 compareNumbers: compareNumbers,
7534 enableCursor: enableCursor,
7535 disableCursor: disableCursor,
7536 guid: guid,
7537 computeVisibleDayRange: computeVisibleDayRange,
7538 isMultiDayRange: isMultiDayRange,
7539 diffDates: diffDates,
7540 removeExact: removeExact,
7541 isArraysEqual: isArraysEqual,
7542 memoize: memoize,
7543 memoizeObjArg: memoizeObjArg,
7544 memoizeArraylike: memoizeArraylike,
7545 memoizeHashlike: memoizeHashlike,
7546 intersectRects: intersectRects,
7547 pointInsideRect: pointInsideRect,
7548 constrainPoint: constrainPoint,
7549 getRectCenter: getRectCenter,
7550 diffPoints: diffPoints,
7551 translateRect: translateRect,
7552 mapHash: mapHash,
7553 filterHash: filterHash,
7554 isPropsEqual: isPropsEqual,
7555 compareObjs: compareObjs,
7556 collectFromHash: collectFromHash,
7557 findElements: findElements,
7558 findDirectChildren: findDirectChildren,
7559 removeElement: removeElement,
7560 applyStyle: applyStyle,
7561 elementMatches: elementMatches,
7562 elementClosest: elementClosest,
7563 getEventTargetViaRoot: getEventTargetViaRoot,
7564 getUniqueDomId: getUniqueDomId,
7565 parseClassNames: parseClassNames,
7566 getCanVGrowWithinCell: getCanVGrowWithinCell,
7567 createEmptyEventStore: createEmptyEventStore,
7568 mergeEventStores: mergeEventStores,
7569 getRelevantEvents: getRelevantEvents,
7570 eventTupleToStore: eventTupleToStore,
7571 combineEventUis: combineEventUis,
7572 createEventUi: createEventUi,
7573 Splitter: Splitter,
7574 getDayClassNames: getDayClassNames,
7575 getDateMeta: getDateMeta,
7576 getSlotClassNames: getSlotClassNames,
7577 buildNavLinkAttrs: buildNavLinkAttrs,
7578 preventDefault: preventDefault,
7579 whenTransitionDone: whenTransitionDone,
7580 computeInnerRect: computeInnerRect,
7581 computeEdges: computeEdges,
7582 getClippingParents: getClippingParents,
7583 computeRect: computeRect,
7584 unpromisify: unpromisify,
7585 Emitter: Emitter,
7586 rangeContainsMarker: rangeContainsMarker,
7587 intersectRanges: intersectRanges,
7588 rangesEqual: rangesEqual,
7589 rangesIntersect: rangesIntersect,
7590 rangeContainsRange: rangeContainsRange,
7591 PositionCache: PositionCache,
7592 ScrollController: ScrollController,
7593 ElementScrollController: ElementScrollController,
7594 WindowScrollController: WindowScrollController,
7595 Theme: Theme,
7596 ViewContextType: ViewContextType,
7597 DateComponent: DateComponent,
7598 DateProfileGenerator: DateProfileGenerator,
7599 isDateSpansEqual: isDateSpansEqual,
7600 addDays: addDays,
7601 startOfDay: startOfDay,
7602 addMs: addMs,
7603 addWeeks: addWeeks,
7604 diffWeeks: diffWeeks,
7605 diffWholeWeeks: diffWholeWeeks,
7606 diffWholeDays: diffWholeDays,
7607 diffDayAndTime: diffDayAndTime,
7608 diffDays: diffDays,
7609 isValidDate: isValidDate,
7610 createDuration: createDuration,
7611 asCleanDays: asCleanDays,
7612 multiplyDuration: multiplyDuration,
7613 addDurations: addDurations,
7614 asRoughMinutes: asRoughMinutes,
7615 asRoughSeconds: asRoughSeconds,
7616 asRoughMs: asRoughMs,
7617 wholeDivideDurations: wholeDivideDurations,
7618 greatestDurationDenominator: greatestDurationDenominator,
7619 DateEnv: DateEnv,
7620 createFormatter: createFormatter,
7621 formatIsoTimeString: formatIsoTimeString,
7622 formatDayString: formatDayString,
7623 buildIsoString: buildIsoString,
7624 formatIsoMonthStr: formatIsoMonthStr,
7625 NamedTimeZoneImpl: NamedTimeZoneImpl,
7626 parseMarker: parse,
7627 SegHierarchy: SegHierarchy,
7628 buildEntryKey: buildEntryKey,
7629 getEntrySpanEnd: getEntrySpanEnd,
7630 binarySearch: binarySearch,
7631 groupIntersectingEntries: groupIntersectingEntries,
7632 intersectSpans: intersectSpans,
7633 Interaction: Interaction,
7634 interactionSettingsToStore: interactionSettingsToStore,
7635 interactionSettingsStore: interactionSettingsStore,
7636 ElementDragging: ElementDragging,
7637 config: config,
7638 parseDragMeta: parseDragMeta,
7639 CalendarRoot: CalendarRoot,
7640 DayHeader: DayHeader,
7641 computeFallbackHeaderFormat: computeFallbackHeaderFormat,
7642 TableDateCell: TableDateCell,
7643 TableDowCell: TableDowCell,
7644 DaySeriesModel: DaySeriesModel,
7645 sliceEventStore: sliceEventStore,
7646 hasBgRendering: hasBgRendering,
7647 getElSeg: getElSeg,
7648 buildSegTimeText: buildSegTimeText,
7649 sortEventSegs: sortEventSegs,
7650 getSegMeta: getSegMeta,
7651 buildEventRangeKey: buildEventRangeKey,
7652 getSegAnchorAttrs: getSegAnchorAttrs,
7653 DayTableModel: DayTableModel,
7654 Slicer: Slicer,
7655 applyMutationToEventStore: applyMutationToEventStore,
7656 isPropsValid: isPropsValid,
7657 isInteractionValid: isInteractionValid,
7658 isDateSelectionValid: isDateSelectionValid,
7659 requestJson: requestJson,
7660 BaseComponent: BaseComponent,
7661 setRef: setRef,
7662 DelayedRunner: DelayedRunner,
7663 SimpleScrollGrid: SimpleScrollGrid,
7664 hasShrinkWidth: hasShrinkWidth,
7665 renderMicroColGroup: renderMicroColGroup,
7666 getScrollGridClassNames: getScrollGridClassNames,
7667 getSectionClassNames: getSectionClassNames,
7668 getSectionHasLiquidHeight: getSectionHasLiquidHeight,
7669 getAllowYScrolling: getAllowYScrolling,
7670 renderChunkContent: renderChunkContent,
7671 computeShrinkWidth: computeShrinkWidth,
7672 sanitizeShrinkWidth: sanitizeShrinkWidth,
7673 isColPropsEqual: isColPropsEqual,
7674 renderScrollShim: renderScrollShim,
7675 getStickyFooterScrollbar: getStickyFooterScrollbar,
7676 getStickyHeaderDates: getStickyHeaderDates,
7677 Scroller: Scroller,
7678 getScrollbarWidths: getScrollbarWidths,
7679 RefMap: RefMap,
7680 getIsRtlScrollbarOnLeft: getIsRtlScrollbarOnLeft,
7681 NowTimer: NowTimer,
7682 ScrollResponder: ScrollResponder,
7683 StandardEvent: StandardEvent,
7684 NowIndicatorContainer: NowIndicatorContainer,
7685 DayCellContainer: DayCellContainer,
7686 hasCustomDayCellContent: hasCustomDayCellContent,
7687 EventContainer: EventContainer,
7688 renderFill: renderFill,
7689 BgEvent: BgEvent,
7690 WeekNumberContainer: WeekNumberContainer,
7691 MoreLinkContainer: MoreLinkContainer,
7692 computeEarliestSegStart: computeEarliestSegStart,
7693 ViewContainer: ViewContainer,
7694 triggerDateSelect: triggerDateSelect,
7695 getDefaultEventEnd: getDefaultEventEnd,
7696 injectStyles: injectStyles,
7697 CalendarImpl: CalendarImpl,
7698 EventImpl: EventImpl,
7699 buildEventApis: buildEventApis,
7700 buildElAttrs: buildElAttrs,
7701 ContentContainer: ContentContainer,
7702 CustomRenderingStore: CustomRenderingStore
7703 };
7704
7705 const globalLocales = [];
7706
7707 const MINIMAL_RAW_EN_LOCALE = {
7708 code: 'en',
7709 week: {
7710 dow: 0,
7711 doy: 4, // 4 days need to be within the year to be considered the first week
7712 },
7713 direction: 'ltr',
7714 buttonText: {
7715 prev: 'prev',
7716 next: 'next',
7717 prevYear: 'prev year',
7718 nextYear: 'next year',
7719 year: 'year',
7720 today: 'today',
7721 month: 'month',
7722 week: 'week',
7723 day: 'day',
7724 list: 'list',
7725 },
7726 weekText: 'W',
7727 weekTextLong: 'Week',
7728 closeHint: 'Close',
7729 timeHint: 'Time',
7730 eventHint: 'Event',
7731 allDayText: 'all-day',
7732 moreLinkText: 'more',
7733 noEventsText: 'No events to display',
7734 };
7735 const RAW_EN_LOCALE = Object.assign(Object.assign({}, MINIMAL_RAW_EN_LOCALE), {
7736 // Includes things we don't want other locales to inherit,
7737 // things that derive from other translatable strings.
7738 buttonHints: {
7739 prev: 'Previous $0',
7740 next: 'Next $0',
7741 today(buttonText, unit) {
7742 return (unit === 'day')
7743 ? 'Today'
7744 : `This ${buttonText}`;
7745 },
7746 }, viewHint: '$0 view', navLinkHint: 'Go to $0', moreLinkHint(eventCnt) {
7747 return `Show ${eventCnt} more event${eventCnt === 1 ? '' : 's'}`;
7748 } });
7749 function organizeRawLocales(explicitRawLocales) {
7750 let defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en';
7751 let allRawLocales = globalLocales.concat(explicitRawLocales);
7752 let rawLocaleMap = {
7753 en: RAW_EN_LOCALE,
7754 };
7755 for (let rawLocale of allRawLocales) {
7756 rawLocaleMap[rawLocale.code] = rawLocale;
7757 }
7758 return {
7759 map: rawLocaleMap,
7760 defaultCode,
7761 };
7762 }
7763 function buildLocale(inputSingular, available) {
7764 if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) {
7765 return parseLocale(inputSingular.code, [inputSingular.code], inputSingular);
7766 }
7767 return queryLocale(inputSingular, available);
7768 }
7769 function queryLocale(codeArg, available) {
7770 let codes = [].concat(codeArg || []); // will convert to array
7771 let raw = queryRawLocale(codes, available) || RAW_EN_LOCALE;
7772 return parseLocale(codeArg, codes, raw);
7773 }
7774 function queryRawLocale(codes, available) {
7775 for (let i = 0; i < codes.length; i += 1) {
7776 let parts = codes[i].toLocaleLowerCase().split('-');
7777 for (let j = parts.length; j > 0; j -= 1) {
7778 let simpleId = parts.slice(0, j).join('-');
7779 if (available[simpleId]) {
7780 return available[simpleId];
7781 }
7782 }
7783 }
7784 return null;
7785 }
7786 function parseLocale(codeArg, codes, raw) {
7787 let merged = mergeProps([MINIMAL_RAW_EN_LOCALE, raw], ['buttonText']);
7788 delete merged.code; // don't want this part of the options
7789 let { week } = merged;
7790 delete merged.week;
7791 return {
7792 codeArg,
7793 codes,
7794 week,
7795 simpleNumberFormat: new Intl.NumberFormat(codeArg),
7796 options: merged,
7797 };
7798 }
7799
7800 // TODO: easier way to add new hooks? need to update a million things
7801 function createPlugin(input) {
7802 return {
7803 id: guid(),
7804 name: input.name,
7805 premiumReleaseDate: input.premiumReleaseDate ? new Date(input.premiumReleaseDate) : undefined,
7806 deps: input.deps || [],
7807 reducers: input.reducers || [],
7808 isLoadingFuncs: input.isLoadingFuncs || [],
7809 contextInit: [].concat(input.contextInit || []),
7810 eventRefiners: input.eventRefiners || {},
7811 eventDefMemberAdders: input.eventDefMemberAdders || [],
7812 eventSourceRefiners: input.eventSourceRefiners || {},
7813 isDraggableTransformers: input.isDraggableTransformers || [],
7814 eventDragMutationMassagers: input.eventDragMutationMassagers || [],
7815 eventDefMutationAppliers: input.eventDefMutationAppliers || [],
7816 dateSelectionTransformers: input.dateSelectionTransformers || [],
7817 datePointTransforms: input.datePointTransforms || [],
7818 dateSpanTransforms: input.dateSpanTransforms || [],
7819 views: input.views || {},
7820 viewPropsTransformers: input.viewPropsTransformers || [],
7821 isPropsValid: input.isPropsValid || null,
7822 externalDefTransforms: input.externalDefTransforms || [],
7823 viewContainerAppends: input.viewContainerAppends || [],
7824 eventDropTransformers: input.eventDropTransformers || [],
7825 componentInteractions: input.componentInteractions || [],
7826 calendarInteractions: input.calendarInteractions || [],
7827 themeClasses: input.themeClasses || {},
7828 eventSourceDefs: input.eventSourceDefs || [],
7829 cmdFormatter: input.cmdFormatter,
7830 recurringTypes: input.recurringTypes || [],
7831 namedTimeZonedImpl: input.namedTimeZonedImpl,
7832 initialView: input.initialView || '',
7833 elementDraggingImpl: input.elementDraggingImpl,
7834 optionChangeHandlers: input.optionChangeHandlers || {},
7835 scrollGridImpl: input.scrollGridImpl || null,
7836 listenerRefiners: input.listenerRefiners || {},
7837 optionRefiners: input.optionRefiners || {},
7838 propSetHandlers: input.propSetHandlers || {},
7839 };
7840 }
7841 function buildPluginHooks(pluginDefs, globalDefs) {
7842 let currentPluginIds = {};
7843 let hooks = {
7844 premiumReleaseDate: undefined,
7845 reducers: [],
7846 isLoadingFuncs: [],
7847 contextInit: [],
7848 eventRefiners: {},
7849 eventDefMemberAdders: [],
7850 eventSourceRefiners: {},
7851 isDraggableTransformers: [],
7852 eventDragMutationMassagers: [],
7853 eventDefMutationAppliers: [],
7854 dateSelectionTransformers: [],
7855 datePointTransforms: [],
7856 dateSpanTransforms: [],
7857 views: {},
7858 viewPropsTransformers: [],
7859 isPropsValid: null,
7860 externalDefTransforms: [],
7861 viewContainerAppends: [],
7862 eventDropTransformers: [],
7863 componentInteractions: [],
7864 calendarInteractions: [],
7865 themeClasses: {},
7866 eventSourceDefs: [],
7867 cmdFormatter: null,
7868 recurringTypes: [],
7869 namedTimeZonedImpl: null,
7870 initialView: '',
7871 elementDraggingImpl: null,
7872 optionChangeHandlers: {},
7873 scrollGridImpl: null,
7874 listenerRefiners: {},
7875 optionRefiners: {},
7876 propSetHandlers: {},
7877 };
7878 function addDefs(defs) {
7879 for (let def of defs) {
7880 const pluginName = def.name;
7881 const currentId = currentPluginIds[pluginName];
7882 if (currentId === undefined) {
7883 currentPluginIds[pluginName] = def.id;
7884 addDefs(def.deps);
7885 hooks = combineHooks(hooks, def);
7886 }
7887 else if (currentId !== def.id) {
7888 // different ID than the one already added
7889 console.warn(`Duplicate plugin '${pluginName}'`);
7890 }
7891 }
7892 }
7893 if (pluginDefs) {
7894 addDefs(pluginDefs);
7895 }
7896 addDefs(globalDefs);
7897 return hooks;
7898 }
7899 function buildBuildPluginHooks() {
7900 let currentOverrideDefs = [];
7901 let currentGlobalDefs = [];
7902 let currentHooks;
7903 return (overrideDefs, globalDefs) => {
7904 if (!currentHooks || !isArraysEqual(overrideDefs, currentOverrideDefs) || !isArraysEqual(globalDefs, currentGlobalDefs)) {
7905 currentHooks = buildPluginHooks(overrideDefs, globalDefs);
7906 }
7907 currentOverrideDefs = overrideDefs;
7908 currentGlobalDefs = globalDefs;
7909 return currentHooks;
7910 };
7911 }
7912 function combineHooks(hooks0, hooks1) {
7913 return {
7914 premiumReleaseDate: compareOptionalDates(hooks0.premiumReleaseDate, hooks1.premiumReleaseDate),
7915 reducers: hooks0.reducers.concat(hooks1.reducers),
7916 isLoadingFuncs: hooks0.isLoadingFuncs.concat(hooks1.isLoadingFuncs),
7917 contextInit: hooks0.contextInit.concat(hooks1.contextInit),
7918 eventRefiners: Object.assign(Object.assign({}, hooks0.eventRefiners), hooks1.eventRefiners),
7919 eventDefMemberAdders: hooks0.eventDefMemberAdders.concat(hooks1.eventDefMemberAdders),
7920 eventSourceRefiners: Object.assign(Object.assign({}, hooks0.eventSourceRefiners), hooks1.eventSourceRefiners),
7921 isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers),
7922 eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers),
7923 eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers),
7924 dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers),
7925 datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms),
7926 dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms),
7927 views: Object.assign(Object.assign({}, hooks0.views), hooks1.views),
7928 viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers),
7929 isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid,
7930 externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms),
7931 viewContainerAppends: hooks0.viewContainerAppends.concat(hooks1.viewContainerAppends),
7932 eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers),
7933 calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions),
7934 componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions),
7935 themeClasses: Object.assign(Object.assign({}, hooks0.themeClasses), hooks1.themeClasses),
7936 eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs),
7937 cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter,
7938 recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes),
7939 namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl,
7940 initialView: hooks0.initialView || hooks1.initialView,
7941 elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl,
7942 optionChangeHandlers: Object.assign(Object.assign({}, hooks0.optionChangeHandlers), hooks1.optionChangeHandlers),
7943 scrollGridImpl: hooks1.scrollGridImpl || hooks0.scrollGridImpl,
7944 listenerRefiners: Object.assign(Object.assign({}, hooks0.listenerRefiners), hooks1.listenerRefiners),
7945 optionRefiners: Object.assign(Object.assign({}, hooks0.optionRefiners), hooks1.optionRefiners),
7946 propSetHandlers: Object.assign(Object.assign({}, hooks0.propSetHandlers), hooks1.propSetHandlers),
7947 };
7948 }
7949 function compareOptionalDates(date0, date1) {
7950 if (date0 === undefined) {
7951 return date1;
7952 }
7953 if (date1 === undefined) {
7954 return date0;
7955 }
7956 return new Date(Math.max(date0.valueOf(), date1.valueOf()));
7957 }
7958
7959 class StandardTheme extends Theme {
7960 }
7961 StandardTheme.prototype.classes = {
7962 root: 'fc-theme-standard',
7963 tableCellShaded: 'fc-cell-shaded',
7964 buttonGroup: 'fc-button-group',
7965 button: 'fc-button fc-button-primary',
7966 buttonActive: 'fc-button-active',
7967 };
7968 StandardTheme.prototype.baseIconClass = 'fc-icon';
7969 StandardTheme.prototype.iconClasses = {
7970 close: 'fc-icon-x',
7971 prev: 'fc-icon-chevron-left',
7972 next: 'fc-icon-chevron-right',
7973 prevYear: 'fc-icon-chevrons-left',
7974 nextYear: 'fc-icon-chevrons-right',
7975 };
7976 StandardTheme.prototype.rtlIconClasses = {
7977 prev: 'fc-icon-chevron-right',
7978 next: 'fc-icon-chevron-left',
7979 prevYear: 'fc-icon-chevrons-right',
7980 nextYear: 'fc-icon-chevrons-left',
7981 };
7982 StandardTheme.prototype.iconOverrideOption = 'buttonIcons'; // TODO: make TS-friendly
7983 StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon';
7984 StandardTheme.prototype.iconOverridePrefix = 'fc-icon-';
7985
7986 function compileViewDefs(defaultConfigs, overrideConfigs) {
7987 let hash = {};
7988 let viewType;
7989 for (viewType in defaultConfigs) {
7990 ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
7991 }
7992 for (viewType in overrideConfigs) {
7993 ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
7994 }
7995 return hash;
7996 }
7997 function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
7998 if (hash[viewType]) {
7999 return hash[viewType];
8000 }
8001 let viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs);
8002 if (viewDef) {
8003 hash[viewType] = viewDef;
8004 }
8005 return viewDef;
8006 }
8007 function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
8008 let defaultConfig = defaultConfigs[viewType];
8009 let overrideConfig = overrideConfigs[viewType];
8010 let queryProp = (name) => ((defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] :
8011 ((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null));
8012 let theComponent = queryProp('component');
8013 let superType = queryProp('superType');
8014 let superDef = null;
8015 if (superType) {
8016 if (superType === viewType) {
8017 throw new Error('Can\'t have a custom view type that references itself');
8018 }
8019 superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs);
8020 }
8021 if (!theComponent && superDef) {
8022 theComponent = superDef.component;
8023 }
8024 if (!theComponent) {
8025 return null; // don't throw a warning, might be settings for a single-unit view
8026 }
8027 return {
8028 type: viewType,
8029 component: theComponent,
8030 defaults: Object.assign(Object.assign({}, (superDef ? superDef.defaults : {})), (defaultConfig ? defaultConfig.rawOptions : {})),
8031 overrides: Object.assign(Object.assign({}, (superDef ? superDef.overrides : {})), (overrideConfig ? overrideConfig.rawOptions : {})),
8032 };
8033 }
8034
8035 function parseViewConfigs(inputs) {
8036 return mapHash(inputs, parseViewConfig);
8037 }
8038 function parseViewConfig(input) {
8039 let rawOptions = typeof input === 'function' ?
8040 { component: input } :
8041 input;
8042 let { component } = rawOptions;
8043 if (rawOptions.content) {
8044 // TODO: remove content/classNames/didMount/etc from options?
8045 component = createViewHookComponent(rawOptions);
8046 }
8047 else if (component && !(component.prototype instanceof BaseComponent)) {
8048 // WHY?: people were using `component` property for `content`
8049 // TODO: converge on one setting name
8050 component = createViewHookComponent(Object.assign(Object.assign({}, rawOptions), { content: component }));
8051 }
8052 return {
8053 superType: rawOptions.type,
8054 component: component,
8055 rawOptions, // includes type and component too :(
8056 };
8057 }
8058 function createViewHookComponent(options) {
8059 return (viewProps) => (y(ViewContextType.Consumer, null, (context) => (y(ContentContainer, { elTag: "div", elClasses: buildViewClassNames(context.viewSpec), renderProps: Object.assign(Object.assign({}, viewProps), { nextDayThreshold: context.options.nextDayThreshold }), generatorName: undefined, customGenerator: options.content, classNameGenerator: options.classNames, didMount: options.didMount, willUnmount: options.willUnmount }))));
8060 }
8061
8062 function buildViewSpecs(defaultInputs, optionOverrides, dynamicOptionOverrides, localeDefaults) {
8063 let defaultConfigs = parseViewConfigs(defaultInputs);
8064 let overrideConfigs = parseViewConfigs(optionOverrides.views);
8065 let viewDefs = compileViewDefs(defaultConfigs, overrideConfigs);
8066 return mapHash(viewDefs, (viewDef) => buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults));
8067 }
8068 function buildViewSpec(viewDef, overrideConfigs, optionOverrides, dynamicOptionOverrides, localeDefaults) {
8069 let durationInput = viewDef.overrides.duration ||
8070 viewDef.defaults.duration ||
8071 dynamicOptionOverrides.duration ||
8072 optionOverrides.duration;
8073 let duration = null;
8074 let durationUnit = '';
8075 let singleUnit = '';
8076 let singleUnitOverrides = {};
8077 if (durationInput) {
8078 duration = createDurationCached(durationInput);
8079 if (duration) { // valid?
8080 let denom = greatestDurationDenominator(duration);
8081 durationUnit = denom.unit;
8082 if (denom.value === 1) {
8083 singleUnit = durationUnit;
8084 singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].rawOptions : {};
8085 }
8086 }
8087 }
8088 let queryButtonText = (optionsSubset) => {
8089 let buttonTextMap = optionsSubset.buttonText || {};
8090 let buttonTextKey = viewDef.defaults.buttonTextKey;
8091 if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) {
8092 return buttonTextMap[buttonTextKey];
8093 }
8094 if (buttonTextMap[viewDef.type] != null) {
8095 return buttonTextMap[viewDef.type];
8096 }
8097 if (buttonTextMap[singleUnit] != null) {
8098 return buttonTextMap[singleUnit];
8099 }
8100 return null;
8101 };
8102 let queryButtonTitle = (optionsSubset) => {
8103 let buttonHints = optionsSubset.buttonHints || {};
8104 let buttonKey = viewDef.defaults.buttonTextKey; // use same key as text
8105 if (buttonKey != null && buttonHints[buttonKey] != null) {
8106 return buttonHints[buttonKey];
8107 }
8108 if (buttonHints[viewDef.type] != null) {
8109 return buttonHints[viewDef.type];
8110 }
8111 if (buttonHints[singleUnit] != null) {
8112 return buttonHints[singleUnit];
8113 }
8114 return null;
8115 };
8116 return {
8117 type: viewDef.type,
8118 component: viewDef.component,
8119 duration,
8120 durationUnit,
8121 singleUnit,
8122 optionDefaults: viewDef.defaults,
8123 optionOverrides: Object.assign(Object.assign({}, singleUnitOverrides), viewDef.overrides),
8124 buttonTextOverride: queryButtonText(dynamicOptionOverrides) ||
8125 queryButtonText(optionOverrides) || // constructor-specified buttonText lookup hash takes precedence
8126 viewDef.overrides.buttonText,
8127 buttonTextDefault: queryButtonText(localeDefaults) ||
8128 viewDef.defaults.buttonText ||
8129 queryButtonText(BASE_OPTION_DEFAULTS) ||
8130 viewDef.type,
8131 // not DRY
8132 buttonTitleOverride: queryButtonTitle(dynamicOptionOverrides) ||
8133 queryButtonTitle(optionOverrides) ||
8134 viewDef.overrides.buttonHint,
8135 buttonTitleDefault: queryButtonTitle(localeDefaults) ||
8136 viewDef.defaults.buttonHint ||
8137 queryButtonTitle(BASE_OPTION_DEFAULTS),
8138 // will eventually fall back to buttonText
8139 };
8140 }
8141 // hack to get memoization working
8142 let durationInputMap = {};
8143 function createDurationCached(durationInput) {
8144 let json = JSON.stringify(durationInput);
8145 let res = durationInputMap[json];
8146 if (res === undefined) {
8147 res = createDuration(durationInput);
8148 durationInputMap[json] = res;
8149 }
8150 return res;
8151 }
8152
8153 function reduceViewType(viewType, action) {
8154 switch (action.type) {
8155 case 'CHANGE_VIEW_TYPE':
8156 viewType = action.viewType;
8157 }
8158 return viewType;
8159 }
8160
8161 function reduceDynamicOptionOverrides(dynamicOptionOverrides, action) {
8162 switch (action.type) {
8163 case 'SET_OPTION':
8164 return Object.assign(Object.assign({}, dynamicOptionOverrides), { [action.optionName]: action.rawOptionValue });
8165 default:
8166 return dynamicOptionOverrides;
8167 }
8168 }
8169
8170 function reduceDateProfile(currentDateProfile, action, currentDate, dateProfileGenerator) {
8171 let dp;
8172 switch (action.type) {
8173 case 'CHANGE_VIEW_TYPE':
8174 return dateProfileGenerator.build(action.dateMarker || currentDate);
8175 case 'CHANGE_DATE':
8176 return dateProfileGenerator.build(action.dateMarker);
8177 case 'PREV':
8178 dp = dateProfileGenerator.buildPrev(currentDateProfile, currentDate);
8179 if (dp.isValid) {
8180 return dp;
8181 }
8182 break;
8183 case 'NEXT':
8184 dp = dateProfileGenerator.buildNext(currentDateProfile, currentDate);
8185 if (dp.isValid) {
8186 return dp;
8187 }
8188 break;
8189 }
8190 return currentDateProfile;
8191 }
8192
8193 function initEventSources(calendarOptions, dateProfile, context) {
8194 let activeRange = dateProfile ? dateProfile.activeRange : null;
8195 return addSources({}, parseInitialSources(calendarOptions, context), activeRange, context);
8196 }
8197 function reduceEventSources(eventSources, action, dateProfile, context) {
8198 let activeRange = dateProfile ? dateProfile.activeRange : null; // need this check?
8199 switch (action.type) {
8200 case 'ADD_EVENT_SOURCES': // already parsed
8201 return addSources(eventSources, action.sources, activeRange, context);
8202 case 'REMOVE_EVENT_SOURCE':
8203 return removeSource(eventSources, action.sourceId);
8204 case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
8205 case 'NEXT':
8206 case 'CHANGE_DATE':
8207 case 'CHANGE_VIEW_TYPE':
8208 if (dateProfile) {
8209 return fetchDirtySources(eventSources, activeRange, context);
8210 }
8211 return eventSources;
8212 case 'FETCH_EVENT_SOURCES':
8213 return fetchSourcesByIds(eventSources, action.sourceIds ? // why no type?
8214 arrayToHash(action.sourceIds) :
8215 excludeStaticSources(eventSources, context), activeRange, action.isRefetch || false, context);
8216 case 'RECEIVE_EVENTS':
8217 case 'RECEIVE_EVENT_ERROR':
8218 return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange);
8219 case 'REMOVE_ALL_EVENT_SOURCES':
8220 return {};
8221 default:
8222 return eventSources;
8223 }
8224 }
8225 function reduceEventSourcesNewTimeZone(eventSources, dateProfile, context) {
8226 let activeRange = dateProfile ? dateProfile.activeRange : null; // need this check?
8227 return fetchSourcesByIds(eventSources, excludeStaticSources(eventSources, context), activeRange, true, context);
8228 }
8229 function computeEventSourcesLoading(eventSources) {
8230 for (let sourceId in eventSources) {
8231 if (eventSources[sourceId].isFetching) {
8232 return true;
8233 }
8234 }
8235 return false;
8236 }
8237 function addSources(eventSourceHash, sources, fetchRange, context) {
8238 let hash = {};
8239 for (let source of sources) {
8240 hash[source.sourceId] = source;
8241 }
8242 if (fetchRange) {
8243 hash = fetchDirtySources(hash, fetchRange, context);
8244 }
8245 return Object.assign(Object.assign({}, eventSourceHash), hash);
8246 }
8247 function removeSource(eventSourceHash, sourceId) {
8248 return filterHash(eventSourceHash, (eventSource) => eventSource.sourceId !== sourceId);
8249 }
8250 function fetchDirtySources(sourceHash, fetchRange, context) {
8251 return fetchSourcesByIds(sourceHash, filterHash(sourceHash, (eventSource) => isSourceDirty(eventSource, fetchRange, context)), fetchRange, false, context);
8252 }
8253 function isSourceDirty(eventSource, fetchRange, context) {
8254 if (!doesSourceNeedRange(eventSource, context)) {
8255 return !eventSource.latestFetchId;
8256 }
8257 return !context.options.lazyFetching ||
8258 !eventSource.fetchRange ||
8259 eventSource.isFetching || // always cancel outdated in-progress fetches
8260 fetchRange.start < eventSource.fetchRange.start ||
8261 fetchRange.end > eventSource.fetchRange.end;
8262 }
8263 function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, isRefetch, context) {
8264 let nextSources = {};
8265 for (let sourceId in prevSources) {
8266 let source = prevSources[sourceId];
8267 if (sourceIdHash[sourceId]) {
8268 nextSources[sourceId] = fetchSource(source, fetchRange, isRefetch, context);
8269 }
8270 else {
8271 nextSources[sourceId] = source;
8272 }
8273 }
8274 return nextSources;
8275 }
8276 function fetchSource(eventSource, fetchRange, isRefetch, context) {
8277 let { options, calendarApi } = context;
8278 let sourceDef = context.pluginHooks.eventSourceDefs[eventSource.sourceDefId];
8279 let fetchId = guid();
8280 sourceDef.fetch({
8281 eventSource,
8282 range: fetchRange,
8283 isRefetch,
8284 context,
8285 }, (res) => {
8286 let { rawEvents } = res;
8287 if (options.eventSourceSuccess) {
8288 rawEvents = options.eventSourceSuccess.call(calendarApi, rawEvents, res.response) || rawEvents;
8289 }
8290 if (eventSource.success) {
8291 rawEvents = eventSource.success.call(calendarApi, rawEvents, res.response) || rawEvents;
8292 }
8293 context.dispatch({
8294 type: 'RECEIVE_EVENTS',
8295 sourceId: eventSource.sourceId,
8296 fetchId,
8297 fetchRange,
8298 rawEvents,
8299 });
8300 }, (error) => {
8301 let errorHandled = false;
8302 if (options.eventSourceFailure) {
8303 options.eventSourceFailure.call(calendarApi, error);
8304 errorHandled = true;
8305 }
8306 if (eventSource.failure) {
8307 eventSource.failure(error);
8308 errorHandled = true;
8309 }
8310 if (!errorHandled) {
8311 console.warn(error.message, error);
8312 }
8313 context.dispatch({
8314 type: 'RECEIVE_EVENT_ERROR',
8315 sourceId: eventSource.sourceId,
8316 fetchId,
8317 fetchRange,
8318 error,
8319 });
8320 });
8321 return Object.assign(Object.assign({}, eventSource), { isFetching: true, latestFetchId: fetchId });
8322 }
8323 function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) {
8324 let eventSource = sourceHash[sourceId];
8325 if (eventSource && // not already removed
8326 fetchId === eventSource.latestFetchId) {
8327 return Object.assign(Object.assign({}, sourceHash), { [sourceId]: Object.assign(Object.assign({}, eventSource), { isFetching: false, fetchRange }) });
8328 }
8329 return sourceHash;
8330 }
8331 function excludeStaticSources(eventSources, context) {
8332 return filterHash(eventSources, (eventSource) => doesSourceNeedRange(eventSource, context));
8333 }
8334 function parseInitialSources(rawOptions, context) {
8335 let refiners = buildEventSourceRefiners(context);
8336 let rawSources = [].concat(rawOptions.eventSources || []);
8337 let sources = []; // parsed
8338 if (rawOptions.initialEvents) {
8339 rawSources.unshift(rawOptions.initialEvents);
8340 }
8341 if (rawOptions.events) {
8342 rawSources.unshift(rawOptions.events);
8343 }
8344 for (let rawSource of rawSources) {
8345 let source = parseEventSource(rawSource, context, refiners);
8346 if (source) {
8347 sources.push(source);
8348 }
8349 }
8350 return sources;
8351 }
8352 function doesSourceNeedRange(eventSource, context) {
8353 let defs = context.pluginHooks.eventSourceDefs;
8354 return !defs[eventSource.sourceDefId].ignoreRange;
8355 }
8356
8357 function reduceDateSelection(currentSelection, action) {
8358 switch (action.type) {
8359 case 'UNSELECT_DATES':
8360 return null;
8361 case 'SELECT_DATES':
8362 return action.selection;
8363 default:
8364 return currentSelection;
8365 }
8366 }
8367
8368 function reduceSelectedEvent(currentInstanceId, action) {
8369 switch (action.type) {
8370 case 'UNSELECT_EVENT':
8371 return '';
8372 case 'SELECT_EVENT':
8373 return action.eventInstanceId;
8374 default:
8375 return currentInstanceId;
8376 }
8377 }
8378
8379 function reduceEventDrag(currentDrag, action) {
8380 let newDrag;
8381 switch (action.type) {
8382 case 'UNSET_EVENT_DRAG':
8383 return null;
8384 case 'SET_EVENT_DRAG':
8385 newDrag = action.state;
8386 return {
8387 affectedEvents: newDrag.affectedEvents,
8388 mutatedEvents: newDrag.mutatedEvents,
8389 isEvent: newDrag.isEvent,
8390 };
8391 default:
8392 return currentDrag;
8393 }
8394 }
8395
8396 function reduceEventResize(currentResize, action) {
8397 let newResize;
8398 switch (action.type) {
8399 case 'UNSET_EVENT_RESIZE':
8400 return null;
8401 case 'SET_EVENT_RESIZE':
8402 newResize = action.state;
8403 return {
8404 affectedEvents: newResize.affectedEvents,
8405 mutatedEvents: newResize.mutatedEvents,
8406 isEvent: newResize.isEvent,
8407 };
8408 default:
8409 return currentResize;
8410 }
8411 }
8412
8413 function parseToolbars(calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) {
8414 let header = calendarOptions.headerToolbar ? parseToolbar(calendarOptions.headerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) : null;
8415 let footer = calendarOptions.footerToolbar ? parseToolbar(calendarOptions.footerToolbar, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) : null;
8416 return { header, footer };
8417 }
8418 function parseToolbar(sectionStrHash, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi) {
8419 let sectionWidgets = {};
8420 let viewsWithButtons = [];
8421 let hasTitle = false;
8422 for (let sectionName in sectionStrHash) {
8423 let sectionStr = sectionStrHash[sectionName];
8424 let sectionRes = parseSection(sectionStr, calendarOptions, calendarOptionOverrides, theme, viewSpecs, calendarApi);
8425 sectionWidgets[sectionName] = sectionRes.widgets;
8426 viewsWithButtons.push(...sectionRes.viewsWithButtons);
8427 hasTitle = hasTitle || sectionRes.hasTitle;
8428 }
8429 return { sectionWidgets, viewsWithButtons, hasTitle };
8430 }
8431 /*
8432 BAD: querying icons and text here. should be done at render time
8433 */
8434 function parseSection(sectionStr, calendarOptions, // defaults+overrides, then refined
8435 calendarOptionOverrides, // overrides only!, unrefined :(
8436 theme, viewSpecs, calendarApi) {
8437 let isRtl = calendarOptions.direction === 'rtl';
8438 let calendarCustomButtons = calendarOptions.customButtons || {};
8439 let calendarButtonTextOverrides = calendarOptionOverrides.buttonText || {};
8440 let calendarButtonText = calendarOptions.buttonText || {};
8441 let calendarButtonHintOverrides = calendarOptionOverrides.buttonHints || {};
8442 let calendarButtonHints = calendarOptions.buttonHints || {};
8443 let sectionSubstrs = sectionStr ? sectionStr.split(' ') : [];
8444 let viewsWithButtons = [];
8445 let hasTitle = false;
8446 let widgets = sectionSubstrs.map((buttonGroupStr) => (buttonGroupStr.split(',').map((buttonName) => {
8447 if (buttonName === 'title') {
8448 hasTitle = true;
8449 return { buttonName };
8450 }
8451 let customButtonProps;
8452 let viewSpec;
8453 let buttonClick;
8454 let buttonIcon; // only one of these will be set
8455 let buttonText; // "
8456 let buttonHint;
8457 // ^ for the title="" attribute, for accessibility
8458 if ((customButtonProps = calendarCustomButtons[buttonName])) {
8459 buttonClick = (ev) => {
8460 if (customButtonProps.click) {
8461 customButtonProps.click.call(ev.target, ev, ev.target); // TODO: use Calendar this context?
8462 }
8463 };
8464 (buttonIcon = theme.getCustomButtonIconClass(customButtonProps)) ||
8465 (buttonIcon = theme.getIconClass(buttonName, isRtl)) ||
8466 (buttonText = customButtonProps.text);
8467 buttonHint = customButtonProps.hint || customButtonProps.text;
8468 }
8469 else if ((viewSpec = viewSpecs[buttonName])) {
8470 viewsWithButtons.push(buttonName);
8471 buttonClick = () => {
8472 calendarApi.changeView(buttonName);
8473 };
8474 (buttonText = viewSpec.buttonTextOverride) ||
8475 (buttonIcon = theme.getIconClass(buttonName, isRtl)) ||
8476 (buttonText = viewSpec.buttonTextDefault);
8477 let textFallback = viewSpec.buttonTextOverride ||
8478 viewSpec.buttonTextDefault;
8479 buttonHint = formatWithOrdinals(viewSpec.buttonTitleOverride ||
8480 viewSpec.buttonTitleDefault ||
8481 calendarOptions.viewHint, [textFallback, buttonName], // view-name = buttonName
8482 textFallback);
8483 }
8484 else if (calendarApi[buttonName]) { // a calendarApi method
8485 buttonClick = () => {
8486 calendarApi[buttonName]();
8487 };
8488 (buttonText = calendarButtonTextOverrides[buttonName]) ||
8489 (buttonIcon = theme.getIconClass(buttonName, isRtl)) ||
8490 (buttonText = calendarButtonText[buttonName]); // everything else is considered default
8491 if (buttonName === 'prevYear' || buttonName === 'nextYear') {
8492 let prevOrNext = buttonName === 'prevYear' ? 'prev' : 'next';
8493 buttonHint = formatWithOrdinals(calendarButtonHintOverrides[prevOrNext] ||
8494 calendarButtonHints[prevOrNext], [
8495 calendarButtonText.year || 'year',
8496 'year',
8497 ], calendarButtonText[buttonName]);
8498 }
8499 else {
8500 buttonHint = (navUnit) => formatWithOrdinals(calendarButtonHintOverrides[buttonName] ||
8501 calendarButtonHints[buttonName], [
8502 calendarButtonText[navUnit] || navUnit,
8503 navUnit,
8504 ], calendarButtonText[buttonName]);
8505 }
8506 }
8507 return { buttonName, buttonClick, buttonIcon, buttonText, buttonHint };
8508 })));
8509 return { widgets, viewsWithButtons, hasTitle };
8510 }
8511
8512 // always represents the current view. otherwise, it'd need to change value every time date changes
8513 class ViewImpl {
8514 constructor(type, getCurrentData, dateEnv) {
8515 this.type = type;
8516 this.getCurrentData = getCurrentData;
8517 this.dateEnv = dateEnv;
8518 }
8519 get calendar() {
8520 return this.getCurrentData().calendarApi;
8521 }
8522 get title() {
8523 return this.getCurrentData().viewTitle;
8524 }
8525 get activeStart() {
8526 return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.start);
8527 }
8528 get activeEnd() {
8529 return this.dateEnv.toDate(this.getCurrentData().dateProfile.activeRange.end);
8530 }
8531 get currentStart() {
8532 return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.start);
8533 }
8534 get currentEnd() {
8535 return this.dateEnv.toDate(this.getCurrentData().dateProfile.currentRange.end);
8536 }
8537 getOption(name) {
8538 return this.getCurrentData().options[name]; // are the view-specific options
8539 }
8540 }
8541
8542 let eventSourceDef$2 = {
8543 ignoreRange: true,
8544 parseMeta(refined) {
8545 if (Array.isArray(refined.events)) {
8546 return refined.events;
8547 }
8548 return null;
8549 },
8550 fetch(arg, successCallback) {
8551 successCallback({
8552 rawEvents: arg.eventSource.meta,
8553 });
8554 },
8555 };
8556 const arrayEventSourcePlugin = createPlugin({
8557 name: 'array-event-source',
8558 eventSourceDefs: [eventSourceDef$2],
8559 });
8560
8561 let eventSourceDef$1 = {
8562 parseMeta(refined) {
8563 if (typeof refined.events === 'function') {
8564 return refined.events;
8565 }
8566 return null;
8567 },
8568 fetch(arg, successCallback, errorCallback) {
8569 const { dateEnv } = arg.context;
8570 const func = arg.eventSource.meta;
8571 unpromisify(func.bind(null, buildRangeApiWithTimeZone(arg.range, dateEnv)), (rawEvents) => successCallback({ rawEvents }), errorCallback);
8572 },
8573 };
8574 const funcEventSourcePlugin = createPlugin({
8575 name: 'func-event-source',
8576 eventSourceDefs: [eventSourceDef$1],
8577 });
8578
8579 const JSON_FEED_EVENT_SOURCE_REFINERS = {
8580 method: String,
8581 extraParams: identity,
8582 startParam: String,
8583 endParam: String,
8584 timeZoneParam: String,
8585 };
8586
8587 let eventSourceDef = {
8588 parseMeta(refined) {
8589 if (refined.url && (refined.format === 'json' || !refined.format)) {
8590 return {
8591 url: refined.url,
8592 format: 'json',
8593 method: (refined.method || 'GET').toUpperCase(),
8594 extraParams: refined.extraParams,
8595 startParam: refined.startParam,
8596 endParam: refined.endParam,
8597 timeZoneParam: refined.timeZoneParam,
8598 };
8599 }
8600 return null;
8601 },
8602 fetch(arg, successCallback, errorCallback) {
8603 const { meta } = arg.eventSource;
8604 const requestParams = buildRequestParams(meta, arg.range, arg.context);
8605 requestJson(meta.method, meta.url, requestParams).then(([rawEvents, response]) => {
8606 successCallback({ rawEvents, response });
8607 }, errorCallback);
8608 },
8609 };
8610 const jsonFeedEventSourcePlugin = createPlugin({
8611 name: 'json-event-source',
8612 eventSourceRefiners: JSON_FEED_EVENT_SOURCE_REFINERS,
8613 eventSourceDefs: [eventSourceDef],
8614 });
8615 function buildRequestParams(meta, range, context) {
8616 let { dateEnv, options } = context;
8617 let startParam;
8618 let endParam;
8619 let timeZoneParam;
8620 let customRequestParams;
8621 let params = {};
8622 startParam = meta.startParam;
8623 if (startParam == null) {
8624 startParam = options.startParam;
8625 }
8626 endParam = meta.endParam;
8627 if (endParam == null) {
8628 endParam = options.endParam;
8629 }
8630 timeZoneParam = meta.timeZoneParam;
8631 if (timeZoneParam == null) {
8632 timeZoneParam = options.timeZoneParam;
8633 }
8634 // retrieve any outbound GET/POST data from the options
8635 if (typeof meta.extraParams === 'function') {
8636 // supplied as a function that returns a key/value object
8637 customRequestParams = meta.extraParams();
8638 }
8639 else {
8640 // probably supplied as a straight key/value object
8641 customRequestParams = meta.extraParams || {};
8642 }
8643 Object.assign(params, customRequestParams);
8644 params[startParam] = dateEnv.formatIso(range.start);
8645 params[endParam] = dateEnv.formatIso(range.end);
8646 if (dateEnv.timeZone !== 'local') {
8647 params[timeZoneParam] = dateEnv.timeZone;
8648 }
8649 return params;
8650 }
8651
8652 const SIMPLE_RECURRING_REFINERS = {
8653 daysOfWeek: identity,
8654 startTime: createDuration,
8655 endTime: createDuration,
8656 duration: createDuration,
8657 startRecur: identity,
8658 endRecur: identity,
8659 };
8660
8661 let recurring = {
8662 parse(refined, dateEnv) {
8663 if (refined.daysOfWeek || refined.startTime || refined.endTime || refined.startRecur || refined.endRecur) {
8664 let recurringData = {
8665 daysOfWeek: refined.daysOfWeek || null,
8666 startTime: refined.startTime || null,
8667 endTime: refined.endTime || null,
8668 startRecur: refined.startRecur ? dateEnv.createMarker(refined.startRecur) : null,
8669 endRecur: refined.endRecur ? dateEnv.createMarker(refined.endRecur) : null,
8670 };
8671 let duration;
8672 if (refined.duration) {
8673 duration = refined.duration;
8674 }
8675 if (!duration && refined.startTime && refined.endTime) {
8676 duration = subtractDurations(refined.endTime, refined.startTime);
8677 }
8678 return {
8679 allDayGuess: Boolean(!refined.startTime && !refined.endTime),
8680 duration,
8681 typeData: recurringData, // doesn't need endTime anymore but oh well
8682 };
8683 }
8684 return null;
8685 },
8686 expand(typeData, framingRange, dateEnv) {
8687 let clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur });
8688 if (clippedFramingRange) {
8689 return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv);
8690 }
8691 return [];
8692 },
8693 };
8694 const simpleRecurringEventsPlugin = createPlugin({
8695 name: 'simple-recurring-event',
8696 recurringTypes: [recurring],
8697 eventRefiners: SIMPLE_RECURRING_REFINERS,
8698 });
8699 function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) {
8700 let dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null;
8701 let dayMarker = startOfDay(framingRange.start);
8702 let endMarker = framingRange.end;
8703 let instanceStarts = [];
8704 while (dayMarker < endMarker) {
8705 let instanceStart;
8706 // if everyday, or this particular day-of-week
8707 if (!dowHash || dowHash[dayMarker.getUTCDay()]) {
8708 if (startTime) {
8709 instanceStart = dateEnv.add(dayMarker, startTime);
8710 }
8711 else {
8712 instanceStart = dayMarker;
8713 }
8714 instanceStarts.push(instanceStart);
8715 }
8716 dayMarker = addDays(dayMarker, 1);
8717 }
8718 return instanceStarts;
8719 }
8720
8721 const changeHandlerPlugin = createPlugin({
8722 name: 'change-handler',
8723 optionChangeHandlers: {
8724 events(events, context) {
8725 handleEventSources([events], context);
8726 },
8727 eventSources: handleEventSources,
8728 },
8729 });
8730 /*
8731 BUG: if `event` was supplied, all previously-given `eventSources` will be wiped out
8732 */
8733 function handleEventSources(inputs, context) {
8734 let unfoundSources = hashValuesToArray(context.getCurrentData().eventSources);
8735 if (unfoundSources.length === 1 &&
8736 inputs.length === 1 &&
8737 Array.isArray(unfoundSources[0]._raw) &&
8738 Array.isArray(inputs[0])) {
8739 context.dispatch({
8740 type: 'RESET_RAW_EVENTS',
8741 sourceId: unfoundSources[0].sourceId,
8742 rawEvents: inputs[0],
8743 });
8744 return;
8745 }
8746 let newInputs = [];
8747 for (let input of inputs) {
8748 let inputFound = false;
8749 for (let i = 0; i < unfoundSources.length; i += 1) {
8750 if (unfoundSources[i]._raw === input) {
8751 unfoundSources.splice(i, 1); // delete
8752 inputFound = true;
8753 break;
8754 }
8755 }
8756 if (!inputFound) {
8757 newInputs.push(input);
8758 }
8759 }
8760 for (let unfoundSource of unfoundSources) {
8761 context.dispatch({
8762 type: 'REMOVE_EVENT_SOURCE',
8763 sourceId: unfoundSource.sourceId,
8764 });
8765 }
8766 for (let newInput of newInputs) {
8767 context.calendarApi.addEventSource(newInput);
8768 }
8769 }
8770
8771 function handleDateProfile(dateProfile, context) {
8772 context.emitter.trigger('datesSet', Object.assign(Object.assign({}, buildRangeApiWithTimeZone(dateProfile.activeRange, context.dateEnv)), { view: context.viewApi }));
8773 }
8774
8775 function handleEventStore(eventStore, context) {
8776 let { emitter } = context;
8777 if (emitter.hasHandlers('eventsSet')) {
8778 emitter.trigger('eventsSet', buildEventApis(eventStore, context));
8779 }
8780 }
8781
8782 /*
8783 this array is exposed on the root namespace so that UMD plugins can add to it.
8784 see the rollup-bundles script.
8785 */
8786 const globalPlugins = [
8787 arrayEventSourcePlugin,
8788 funcEventSourcePlugin,
8789 jsonFeedEventSourcePlugin,
8790 simpleRecurringEventsPlugin,
8791 changeHandlerPlugin,
8792 createPlugin({
8793 name: 'misc',
8794 isLoadingFuncs: [
8795 (state) => computeEventSourcesLoading(state.eventSources),
8796 ],
8797 propSetHandlers: {
8798 dateProfile: handleDateProfile,
8799 eventStore: handleEventStore,
8800 },
8801 }),
8802 ];
8803
8804 class TaskRunner {
8805 constructor(runTaskOption, drainedOption) {
8806 this.runTaskOption = runTaskOption;
8807 this.drainedOption = drainedOption;
8808 this.queue = [];
8809 this.delayedRunner = new DelayedRunner(this.drain.bind(this));
8810 }
8811 request(task, delay) {
8812 this.queue.push(task);
8813 this.delayedRunner.request(delay);
8814 }
8815 pause(scope) {
8816 this.delayedRunner.pause(scope);
8817 }
8818 resume(scope, force) {
8819 this.delayedRunner.resume(scope, force);
8820 }
8821 drain() {
8822 let { queue } = this;
8823 while (queue.length) {
8824 let completedTasks = [];
8825 let task;
8826 while ((task = queue.shift())) {
8827 this.runTask(task);
8828 completedTasks.push(task);
8829 }
8830 this.drained(completedTasks);
8831 } // keep going, in case new tasks were added in the drained handler
8832 }
8833 runTask(task) {
8834 if (this.runTaskOption) {
8835 this.runTaskOption(task);
8836 }
8837 }
8838 drained(completedTasks) {
8839 if (this.drainedOption) {
8840 this.drainedOption(completedTasks);
8841 }
8842 }
8843 }
8844
8845 // Computes what the title at the top of the calendarApi should be for this view
8846 function buildTitle(dateProfile, viewOptions, dateEnv) {
8847 let range;
8848 // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
8849 if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
8850 range = dateProfile.currentRange;
8851 }
8852 else { // for day units or smaller, use the actual day range
8853 range = dateProfile.activeRange;
8854 }
8855 return dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || buildTitleFormat(dateProfile)), {
8856 isEndExclusive: dateProfile.isRangeAllDay,
8857 defaultSeparator: viewOptions.titleRangeSeparator,
8858 });
8859 }
8860 // Generates the format string that should be used to generate the title for the current date range.
8861 // Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
8862 function buildTitleFormat(dateProfile) {
8863 let { currentRangeUnit } = dateProfile;
8864 if (currentRangeUnit === 'year') {
8865 return { year: 'numeric' };
8866 }
8867 if (currentRangeUnit === 'month') {
8868 return { year: 'numeric', month: 'long' }; // like "September 2014"
8869 }
8870 let days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end);
8871 if (days !== null && days > 1) {
8872 // multi-day range. shorter, like "Sep 9 - 10 2014"
8873 return { year: 'numeric', month: 'short', day: 'numeric' };
8874 }
8875 // one day. longer, like "September 9 2014"
8876 return { year: 'numeric', month: 'long', day: 'numeric' };
8877 }
8878
8879 // in future refactor, do the redux-style function(state=initial) for initial-state
8880 // also, whatever is happening in constructor, have it happen in action queue too
8881 class CalendarDataManager {
8882 constructor(props) {
8883 this.computeCurrentViewData = memoize(this._computeCurrentViewData);
8884 this.organizeRawLocales = memoize(organizeRawLocales);
8885 this.buildLocale = memoize(buildLocale);
8886 this.buildPluginHooks = buildBuildPluginHooks();
8887 this.buildDateEnv = memoize(buildDateEnv$1);
8888 this.buildTheme = memoize(buildTheme);
8889 this.parseToolbars = memoize(parseToolbars);
8890 this.buildViewSpecs = memoize(buildViewSpecs);
8891 this.buildDateProfileGenerator = memoizeObjArg(buildDateProfileGenerator);
8892 this.buildViewApi = memoize(buildViewApi);
8893 this.buildViewUiProps = memoizeObjArg(buildViewUiProps);
8894 this.buildEventUiBySource = memoize(buildEventUiBySource, isPropsEqual);
8895 this.buildEventUiBases = memoize(buildEventUiBases);
8896 this.parseContextBusinessHours = memoizeObjArg(parseContextBusinessHours);
8897 this.buildTitle = memoize(buildTitle);
8898 this.emitter = new Emitter();
8899 this.actionRunner = new TaskRunner(this._handleAction.bind(this), this.updateData.bind(this));
8900 this.currentCalendarOptionsInput = {};
8901 this.currentCalendarOptionsRefined = {};
8902 this.currentViewOptionsInput = {};
8903 this.currentViewOptionsRefined = {};
8904 this.currentCalendarOptionsRefiners = {};
8905 this.optionsForRefining = [];
8906 this.optionsForHandling = [];
8907 this.getCurrentData = () => this.data;
8908 this.dispatch = (action) => {
8909 this.actionRunner.request(action); // protects against recursive calls to _handleAction
8910 };
8911 this.props = props;
8912 this.actionRunner.pause();
8913 let dynamicOptionOverrides = {};
8914 let optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi);
8915 let currentViewType = optionsData.calendarOptions.initialView || optionsData.pluginHooks.initialView;
8916 let currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides);
8917 // wire things up
8918 // TODO: not DRY
8919 props.calendarApi.currentDataManager = this;
8920 this.emitter.setThisContext(props.calendarApi);
8921 this.emitter.setOptions(currentViewData.options);
8922 let currentDate = getInitialDate(optionsData.calendarOptions, optionsData.dateEnv);
8923 let dateProfile = currentViewData.dateProfileGenerator.build(currentDate);
8924 if (!rangeContainsMarker(dateProfile.activeRange, currentDate)) {
8925 currentDate = dateProfile.currentRange.start;
8926 }
8927 let calendarContext = {
8928 dateEnv: optionsData.dateEnv,
8929 options: optionsData.calendarOptions,
8930 pluginHooks: optionsData.pluginHooks,
8931 calendarApi: props.calendarApi,
8932 dispatch: this.dispatch,
8933 emitter: this.emitter,
8934 getCurrentData: this.getCurrentData,
8935 };
8936 // needs to be after setThisContext
8937 for (let callback of optionsData.pluginHooks.contextInit) {
8938 callback(calendarContext);
8939 }
8940 // NOT DRY
8941 let eventSources = initEventSources(optionsData.calendarOptions, dateProfile, calendarContext);
8942 let initialState = {
8943 dynamicOptionOverrides,
8944 currentViewType,
8945 currentDate,
8946 dateProfile,
8947 businessHours: this.parseContextBusinessHours(calendarContext),
8948 eventSources,
8949 eventUiBases: {},
8950 eventStore: createEmptyEventStore(),
8951 renderableEventStore: createEmptyEventStore(),
8952 dateSelection: null,
8953 eventSelection: '',
8954 eventDrag: null,
8955 eventResize: null,
8956 selectionConfig: this.buildViewUiProps(calendarContext).selectionConfig,
8957 };
8958 let contextAndState = Object.assign(Object.assign({}, calendarContext), initialState);
8959 for (let reducer of optionsData.pluginHooks.reducers) {
8960 Object.assign(initialState, reducer(null, null, contextAndState));
8961 }
8962 if (computeIsLoading(initialState, calendarContext)) {
8963 this.emitter.trigger('loading', true); // NOT DRY
8964 }
8965 this.state = initialState;
8966 this.updateData();
8967 this.actionRunner.resume();
8968 }
8969 resetOptions(optionOverrides, changedOptionNames) {
8970 let { props } = this;
8971 if (changedOptionNames === undefined) {
8972 props.optionOverrides = optionOverrides;
8973 }
8974 else {
8975 props.optionOverrides = Object.assign(Object.assign({}, (props.optionOverrides || {})), optionOverrides);
8976 this.optionsForRefining.push(...changedOptionNames);
8977 }
8978 if (changedOptionNames === undefined || changedOptionNames.length) {
8979 this.actionRunner.request({
8980 type: 'NOTHING',
8981 });
8982 }
8983 }
8984 _handleAction(action) {
8985 let { props, state, emitter } = this;
8986 let dynamicOptionOverrides = reduceDynamicOptionOverrides(state.dynamicOptionOverrides, action);
8987 let optionsData = this.computeOptionsData(props.optionOverrides, dynamicOptionOverrides, props.calendarApi);
8988 let currentViewType = reduceViewType(state.currentViewType, action);
8989 let currentViewData = this.computeCurrentViewData(currentViewType, optionsData, props.optionOverrides, dynamicOptionOverrides);
8990 // wire things up
8991 // TODO: not DRY
8992 props.calendarApi.currentDataManager = this;
8993 emitter.setThisContext(props.calendarApi);
8994 emitter.setOptions(currentViewData.options);
8995 let calendarContext = {
8996 dateEnv: optionsData.dateEnv,
8997 options: optionsData.calendarOptions,
8998 pluginHooks: optionsData.pluginHooks,
8999 calendarApi: props.calendarApi,
9000 dispatch: this.dispatch,
9001 emitter,
9002 getCurrentData: this.getCurrentData,
9003 };
9004 let { currentDate, dateProfile } = state;
9005 if (this.data && this.data.dateProfileGenerator !== currentViewData.dateProfileGenerator) { // hack
9006 dateProfile = currentViewData.dateProfileGenerator.build(currentDate);
9007 }
9008 currentDate = reduceCurrentDate(currentDate, action);
9009 dateProfile = reduceDateProfile(dateProfile, action, currentDate, currentViewData.dateProfileGenerator);
9010 if (action.type === 'PREV' || // TODO: move this logic into DateProfileGenerator
9011 action.type === 'NEXT' || // "
9012 !rangeContainsMarker(dateProfile.currentRange, currentDate)) {
9013 currentDate = dateProfile.currentRange.start;
9014 }
9015 let eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendarContext);
9016 let eventStore = reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendarContext);
9017 let isEventsLoading = computeEventSourcesLoading(eventSources); // BAD. also called in this func in computeIsLoading
9018 let renderableEventStore = (isEventsLoading && !currentViewData.options.progressiveEventRendering) ?
9019 (state.renderableEventStore || eventStore) : // try from previous state
9020 eventStore;
9021 let { eventUiSingleBase, selectionConfig } = this.buildViewUiProps(calendarContext); // will memoize obj
9022 let eventUiBySource = this.buildEventUiBySource(eventSources);
9023 let eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource);
9024 let newState = {
9025 dynamicOptionOverrides,
9026 currentViewType,
9027 currentDate,
9028 dateProfile,
9029 eventSources,
9030 eventStore,
9031 renderableEventStore,
9032 selectionConfig,
9033 eventUiBases,
9034 businessHours: this.parseContextBusinessHours(calendarContext),
9035 dateSelection: reduceDateSelection(state.dateSelection, action),
9036 eventSelection: reduceSelectedEvent(state.eventSelection, action),
9037 eventDrag: reduceEventDrag(state.eventDrag, action),
9038 eventResize: reduceEventResize(state.eventResize, action),
9039 };
9040 let contextAndState = Object.assign(Object.assign({}, calendarContext), newState);
9041 for (let reducer of optionsData.pluginHooks.reducers) {
9042 Object.assign(newState, reducer(state, action, contextAndState)); // give the OLD state, for old value
9043 }
9044 let wasLoading = computeIsLoading(state, calendarContext);
9045 let isLoading = computeIsLoading(newState, calendarContext);
9046 // TODO: use propSetHandlers in plugin system
9047 if (!wasLoading && isLoading) {
9048 emitter.trigger('loading', true);
9049 }
9050 else if (wasLoading && !isLoading) {
9051 emitter.trigger('loading', false);
9052 }
9053 this.state = newState;
9054 if (props.onAction) {
9055 props.onAction(action);
9056 }
9057 }
9058 updateData() {
9059 let { props, state } = this;
9060 let oldData = this.data;
9061 let optionsData = this.computeOptionsData(props.optionOverrides, state.dynamicOptionOverrides, props.calendarApi);
9062 let currentViewData = this.computeCurrentViewData(state.currentViewType, optionsData, props.optionOverrides, state.dynamicOptionOverrides);
9063 let data = this.data = Object.assign(Object.assign(Object.assign({ viewTitle: this.buildTitle(state.dateProfile, currentViewData.options, optionsData.dateEnv), calendarApi: props.calendarApi, dispatch: this.dispatch, emitter: this.emitter, getCurrentData: this.getCurrentData }, optionsData), currentViewData), state);
9064 let changeHandlers = optionsData.pluginHooks.optionChangeHandlers;
9065 let oldCalendarOptions = oldData && oldData.calendarOptions;
9066 let newCalendarOptions = optionsData.calendarOptions;
9067 if (oldCalendarOptions && oldCalendarOptions !== newCalendarOptions) {
9068 if (oldCalendarOptions.timeZone !== newCalendarOptions.timeZone) {
9069 // hack
9070 state.eventSources = data.eventSources = reduceEventSourcesNewTimeZone(data.eventSources, state.dateProfile, data);
9071 state.eventStore = data.eventStore = rezoneEventStoreDates(data.eventStore, oldData.dateEnv, data.dateEnv);
9072 state.renderableEventStore = data.renderableEventStore = rezoneEventStoreDates(data.renderableEventStore, oldData.dateEnv, data.dateEnv);
9073 }
9074 for (let optionName in changeHandlers) {
9075 if (this.optionsForHandling.indexOf(optionName) !== -1 ||
9076 oldCalendarOptions[optionName] !== newCalendarOptions[optionName]) {
9077 changeHandlers[optionName](newCalendarOptions[optionName], data);
9078 }
9079 }
9080 }
9081 this.optionsForHandling = [];
9082 if (props.onData) {
9083 props.onData(data);
9084 }
9085 }
9086 computeOptionsData(optionOverrides, dynamicOptionOverrides, calendarApi) {
9087 // TODO: blacklist options that are handled by optionChangeHandlers
9088 if (!this.optionsForRefining.length &&
9089 optionOverrides === this.stableOptionOverrides &&
9090 dynamicOptionOverrides === this.stableDynamicOptionOverrides) {
9091 return this.stableCalendarOptionsData;
9092 }
9093 let { refinedOptions, pluginHooks, localeDefaults, availableLocaleData, extra, } = this.processRawCalendarOptions(optionOverrides, dynamicOptionOverrides);
9094 warnUnknownOptions(extra);
9095 let dateEnv = this.buildDateEnv(refinedOptions.timeZone, refinedOptions.locale, refinedOptions.weekNumberCalculation, refinedOptions.firstDay, refinedOptions.weekText, pluginHooks, availableLocaleData, refinedOptions.defaultRangeSeparator);
9096 let viewSpecs = this.buildViewSpecs(pluginHooks.views, this.stableOptionOverrides, this.stableDynamicOptionOverrides, localeDefaults);
9097 let theme = this.buildTheme(refinedOptions, pluginHooks);
9098 let toolbarConfig = this.parseToolbars(refinedOptions, this.stableOptionOverrides, theme, viewSpecs, calendarApi);
9099 return this.stableCalendarOptionsData = {
9100 calendarOptions: refinedOptions,
9101 pluginHooks,
9102 dateEnv,
9103 viewSpecs,
9104 theme,
9105 toolbarConfig,
9106 localeDefaults,
9107 availableRawLocales: availableLocaleData.map,
9108 };
9109 }
9110 // always called from behind a memoizer
9111 processRawCalendarOptions(optionOverrides, dynamicOptionOverrides) {
9112 let { locales, locale } = mergeRawOptions([
9113 BASE_OPTION_DEFAULTS,
9114 optionOverrides,
9115 dynamicOptionOverrides,
9116 ]);
9117 let availableLocaleData = this.organizeRawLocales(locales);
9118 let availableRawLocales = availableLocaleData.map;
9119 let localeDefaults = this.buildLocale(locale || availableLocaleData.defaultCode, availableRawLocales).options;
9120 let pluginHooks = this.buildPluginHooks(optionOverrides.plugins || [], globalPlugins);
9121 let refiners = this.currentCalendarOptionsRefiners = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners);
9122 let extra = {};
9123 let raw = mergeRawOptions([
9124 BASE_OPTION_DEFAULTS,
9125 localeDefaults,
9126 optionOverrides,
9127 dynamicOptionOverrides,
9128 ]);
9129 let refined = {};
9130 let currentRaw = this.currentCalendarOptionsInput;
9131 let currentRefined = this.currentCalendarOptionsRefined;
9132 let anyChanges = false;
9133 for (let optionName in raw) {
9134 if (this.optionsForRefining.indexOf(optionName) === -1 && (raw[optionName] === currentRaw[optionName] || (COMPLEX_OPTION_COMPARATORS[optionName] &&
9135 (optionName in currentRaw) &&
9136 COMPLEX_OPTION_COMPARATORS[optionName](currentRaw[optionName], raw[optionName])))) {
9137 refined[optionName] = currentRefined[optionName];
9138 }
9139 else if (refiners[optionName]) {
9140 refined[optionName] = refiners[optionName](raw[optionName]);
9141 anyChanges = true;
9142 }
9143 else {
9144 extra[optionName] = currentRaw[optionName];
9145 }
9146 }
9147 if (anyChanges) {
9148 this.currentCalendarOptionsInput = raw;
9149 this.currentCalendarOptionsRefined = refined;
9150 this.stableOptionOverrides = optionOverrides;
9151 this.stableDynamicOptionOverrides = dynamicOptionOverrides;
9152 }
9153 this.optionsForHandling.push(...this.optionsForRefining);
9154 this.optionsForRefining = [];
9155 return {
9156 rawOptions: this.currentCalendarOptionsInput,
9157 refinedOptions: this.currentCalendarOptionsRefined,
9158 pluginHooks,
9159 availableLocaleData,
9160 localeDefaults,
9161 extra,
9162 };
9163 }
9164 _computeCurrentViewData(viewType, optionsData, optionOverrides, dynamicOptionOverrides) {
9165 let viewSpec = optionsData.viewSpecs[viewType];
9166 if (!viewSpec) {
9167 throw new Error(`viewType "${viewType}" is not available. Please make sure you've loaded all neccessary plugins`);
9168 }
9169 let { refinedOptions, extra } = this.processRawViewOptions(viewSpec, optionsData.pluginHooks, optionsData.localeDefaults, optionOverrides, dynamicOptionOverrides);
9170 warnUnknownOptions(extra);
9171 let dateProfileGenerator = this.buildDateProfileGenerator({
9172 dateProfileGeneratorClass: viewSpec.optionDefaults.dateProfileGeneratorClass,
9173 duration: viewSpec.duration,
9174 durationUnit: viewSpec.durationUnit,
9175 usesMinMaxTime: viewSpec.optionDefaults.usesMinMaxTime,
9176 dateEnv: optionsData.dateEnv,
9177 calendarApi: this.props.calendarApi,
9178 slotMinTime: refinedOptions.slotMinTime,
9179 slotMaxTime: refinedOptions.slotMaxTime,
9180 showNonCurrentDates: refinedOptions.showNonCurrentDates,
9181 dayCount: refinedOptions.dayCount,
9182 dateAlignment: refinedOptions.dateAlignment,
9183 dateIncrement: refinedOptions.dateIncrement,
9184 hiddenDays: refinedOptions.hiddenDays,
9185 weekends: refinedOptions.weekends,
9186 nowInput: refinedOptions.now,
9187 validRangeInput: refinedOptions.validRange,
9188 visibleRangeInput: refinedOptions.visibleRange,
9189 fixedWeekCount: refinedOptions.fixedWeekCount,
9190 });
9191 let viewApi = this.buildViewApi(viewType, this.getCurrentData, optionsData.dateEnv);
9192 return { viewSpec, options: refinedOptions, dateProfileGenerator, viewApi };
9193 }
9194 processRawViewOptions(viewSpec, pluginHooks, localeDefaults, optionOverrides, dynamicOptionOverrides) {
9195 let raw = mergeRawOptions([
9196 BASE_OPTION_DEFAULTS,
9197 viewSpec.optionDefaults,
9198 localeDefaults,
9199 optionOverrides,
9200 viewSpec.optionOverrides,
9201 dynamicOptionOverrides,
9202 ]);
9203 let refiners = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, BASE_OPTION_REFINERS), CALENDAR_LISTENER_REFINERS), CALENDAR_OPTION_REFINERS), VIEW_OPTION_REFINERS), pluginHooks.listenerRefiners), pluginHooks.optionRefiners);
9204 let refined = {};
9205 let currentRaw = this.currentViewOptionsInput;
9206 let currentRefined = this.currentViewOptionsRefined;
9207 let anyChanges = false;
9208 let extra = {};
9209 for (let optionName in raw) {
9210 if (raw[optionName] === currentRaw[optionName] ||
9211 (COMPLEX_OPTION_COMPARATORS[optionName] &&
9212 COMPLEX_OPTION_COMPARATORS[optionName](raw[optionName], currentRaw[optionName]))) {
9213 refined[optionName] = currentRefined[optionName];
9214 }
9215 else {
9216 if (raw[optionName] === this.currentCalendarOptionsInput[optionName] ||
9217 (COMPLEX_OPTION_COMPARATORS[optionName] &&
9218 COMPLEX_OPTION_COMPARATORS[optionName](raw[optionName], this.currentCalendarOptionsInput[optionName]))) {
9219 if (optionName in this.currentCalendarOptionsRefined) { // might be an "extra" prop
9220 refined[optionName] = this.currentCalendarOptionsRefined[optionName];
9221 }
9222 }
9223 else if (refiners[optionName]) {
9224 refined[optionName] = refiners[optionName](raw[optionName]);
9225 }
9226 else {
9227 extra[optionName] = raw[optionName];
9228 }
9229 anyChanges = true;
9230 }
9231 }
9232 if (anyChanges) {
9233 this.currentViewOptionsInput = raw;
9234 this.currentViewOptionsRefined = refined;
9235 }
9236 return {
9237 rawOptions: this.currentViewOptionsInput,
9238 refinedOptions: this.currentViewOptionsRefined,
9239 extra,
9240 };
9241 }
9242 }
9243 function buildDateEnv$1(timeZone, explicitLocale, weekNumberCalculation, firstDay, weekText, pluginHooks, availableLocaleData, defaultSeparator) {
9244 let locale = buildLocale(explicitLocale || availableLocaleData.defaultCode, availableLocaleData.map);
9245 return new DateEnv({
9246 calendarSystem: 'gregory',
9247 timeZone,
9248 namedTimeZoneImpl: pluginHooks.namedTimeZonedImpl,
9249 locale,
9250 weekNumberCalculation,
9251 firstDay,
9252 weekText,
9253 cmdFormatter: pluginHooks.cmdFormatter,
9254 defaultSeparator,
9255 });
9256 }
9257 function buildTheme(options, pluginHooks) {
9258 let ThemeClass = pluginHooks.themeClasses[options.themeSystem] || StandardTheme;
9259 return new ThemeClass(options);
9260 }
9261 function buildDateProfileGenerator(props) {
9262 let DateProfileGeneratorClass = props.dateProfileGeneratorClass || DateProfileGenerator;
9263 return new DateProfileGeneratorClass(props);
9264 }
9265 function buildViewApi(type, getCurrentData, dateEnv) {
9266 return new ViewImpl(type, getCurrentData, dateEnv);
9267 }
9268 function buildEventUiBySource(eventSources) {
9269 return mapHash(eventSources, (eventSource) => eventSource.ui);
9270 }
9271 function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) {
9272 let eventUiBases = { '': eventUiSingleBase };
9273 for (let defId in eventDefs) {
9274 let def = eventDefs[defId];
9275 if (def.sourceId && eventUiBySource[def.sourceId]) {
9276 eventUiBases[defId] = eventUiBySource[def.sourceId];
9277 }
9278 }
9279 return eventUiBases;
9280 }
9281 function buildViewUiProps(calendarContext) {
9282 let { options } = calendarContext;
9283 return {
9284 eventUiSingleBase: createEventUi({
9285 display: options.eventDisplay,
9286 editable: options.editable,
9287 startEditable: options.eventStartEditable,
9288 durationEditable: options.eventDurationEditable,
9289 constraint: options.eventConstraint,
9290 overlap: typeof options.eventOverlap === 'boolean' ? options.eventOverlap : undefined,
9291 allow: options.eventAllow,
9292 backgroundColor: options.eventBackgroundColor,
9293 borderColor: options.eventBorderColor,
9294 textColor: options.eventTextColor,
9295 color: options.eventColor,
9296 // classNames: options.eventClassNames // render hook will handle this
9297 }, calendarContext),
9298 selectionConfig: createEventUi({
9299 constraint: options.selectConstraint,
9300 overlap: typeof options.selectOverlap === 'boolean' ? options.selectOverlap : undefined,
9301 allow: options.selectAllow,
9302 }, calendarContext),
9303 };
9304 }
9305 function computeIsLoading(state, context) {
9306 for (let isLoadingFunc of context.pluginHooks.isLoadingFuncs) {
9307 if (isLoadingFunc(state)) {
9308 return true;
9309 }
9310 }
9311 return false;
9312 }
9313 function parseContextBusinessHours(calendarContext) {
9314 return parseBusinessHours(calendarContext.options.businessHours, calendarContext);
9315 }
9316 function warnUnknownOptions(options, viewName) {
9317 for (let optionName in options) {
9318 console.warn(`Unknown option '${optionName}'` +
9319 (viewName ? ` for view '${viewName}'` : ''));
9320 }
9321 }
9322
9323 class ToolbarSection extends BaseComponent {
9324 render() {
9325 let children = this.props.widgetGroups.map((widgetGroup) => this.renderWidgetGroup(widgetGroup));
9326 return y('div', { className: 'fc-toolbar-chunk' }, ...children);
9327 }
9328 renderWidgetGroup(widgetGroup) {
9329 let { props } = this;
9330 let { theme } = this.context;
9331 let children = [];
9332 let isOnlyButtons = true;
9333 for (let widget of widgetGroup) {
9334 let { buttonName, buttonClick, buttonText, buttonIcon, buttonHint } = widget;
9335 if (buttonName === 'title') {
9336 isOnlyButtons = false;
9337 children.push(y("h2", { className: "fc-toolbar-title", id: props.titleId }, props.title));
9338 }
9339 else {
9340 let isPressed = buttonName === props.activeButton;
9341 let isDisabled = (!props.isTodayEnabled && buttonName === 'today') ||
9342 (!props.isPrevEnabled && buttonName === 'prev') ||
9343 (!props.isNextEnabled && buttonName === 'next');
9344 let buttonClasses = [`fc-${buttonName}-button`, theme.getClass('button')];
9345 if (isPressed) {
9346 buttonClasses.push(theme.getClass('buttonActive'));
9347 }
9348 children.push(y("button", { type: "button", title: typeof buttonHint === 'function' ? buttonHint(props.navUnit) : buttonHint, disabled: isDisabled, "aria-pressed": isPressed, className: buttonClasses.join(' '), onClick: buttonClick }, buttonText || (buttonIcon ? y("span", { className: buttonIcon, role: "img" }) : '')));
9349 }
9350 }
9351 if (children.length > 1) {
9352 let groupClassName = (isOnlyButtons && theme.getClass('buttonGroup')) || '';
9353 return y('div', { className: groupClassName }, ...children);
9354 }
9355 return children[0];
9356 }
9357 }
9358
9359 class Toolbar extends BaseComponent {
9360 render() {
9361 let { model, extraClassName } = this.props;
9362 let forceLtr = false;
9363 let startContent;
9364 let endContent;
9365 let sectionWidgets = model.sectionWidgets;
9366 let centerContent = sectionWidgets.center;
9367 if (sectionWidgets.left) {
9368 forceLtr = true;
9369 startContent = sectionWidgets.left;
9370 }
9371 else {
9372 startContent = sectionWidgets.start;
9373 }
9374 if (sectionWidgets.right) {
9375 forceLtr = true;
9376 endContent = sectionWidgets.right;
9377 }
9378 else {
9379 endContent = sectionWidgets.end;
9380 }
9381 let classNames = [
9382 extraClassName || '',
9383 'fc-toolbar',
9384 forceLtr ? 'fc-toolbar-ltr' : '',
9385 ];
9386 return (y("div", { className: classNames.join(' ') },
9387 this.renderSection('start', startContent || []),
9388 this.renderSection('center', centerContent || []),
9389 this.renderSection('end', endContent || [])));
9390 }
9391 renderSection(key, widgetGroups) {
9392 let { props } = this;
9393 return (y(ToolbarSection, { key: key, widgetGroups: widgetGroups, title: props.title, navUnit: props.navUnit, activeButton: props.activeButton, isTodayEnabled: props.isTodayEnabled, isPrevEnabled: props.isPrevEnabled, isNextEnabled: props.isNextEnabled, titleId: props.titleId }));
9394 }
9395 }
9396
9397 class ViewHarness extends BaseComponent {
9398 constructor() {
9399 super(...arguments);
9400 this.state = {
9401 availableWidth: null,
9402 };
9403 this.handleEl = (el) => {
9404 this.el = el;
9405 setRef(this.props.elRef, el);
9406 this.updateAvailableWidth();
9407 };
9408 this.handleResize = () => {
9409 this.updateAvailableWidth();
9410 };
9411 }
9412 render() {
9413 let { props, state } = this;
9414 let { aspectRatio } = props;
9415 let classNames = [
9416 'fc-view-harness',
9417 (aspectRatio || props.liquid || props.height)
9418 ? 'fc-view-harness-active' // harness controls the height
9419 : 'fc-view-harness-passive', // let the view do the height
9420 ];
9421 let height = '';
9422 let paddingBottom = '';
9423 if (aspectRatio) {
9424 if (state.availableWidth !== null) {
9425 height = state.availableWidth / aspectRatio;
9426 }
9427 else {
9428 // while waiting to know availableWidth, we can't set height to *zero*
9429 // because will cause lots of unnecessary scrollbars within scrollgrid.
9430 // BETTER: don't start rendering ANYTHING yet until we know container width
9431 // NOTE: why not always use paddingBottom? Causes height oscillation (issue 5606)
9432 paddingBottom = `${(1 / aspectRatio) * 100}%`;
9433 }
9434 }
9435 else {
9436 height = props.height || '';
9437 }
9438 return (y("div", { "aria-labelledby": props.labeledById, ref: this.handleEl, className: classNames.join(' '), style: { height, paddingBottom } }, props.children));
9439 }
9440 componentDidMount() {
9441 this.context.addResizeHandler(this.handleResize);
9442 }
9443 componentWillUnmount() {
9444 this.context.removeResizeHandler(this.handleResize);
9445 }
9446 updateAvailableWidth() {
9447 if (this.el && // needed. but why?
9448 this.props.aspectRatio // aspectRatio is the only height setting that needs availableWidth
9449 ) {
9450 this.setState({ availableWidth: this.el.offsetWidth });
9451 }
9452 }
9453 }
9454
9455 /*
9456 Detects when the user clicks on an event within a DateComponent
9457 */
9458 class EventClicking extends Interaction {
9459 constructor(settings) {
9460 super(settings);
9461 this.handleSegClick = (ev, segEl) => {
9462 let { component } = this;
9463 let { context } = component;
9464 let seg = getElSeg(segEl);
9465 if (seg && // might be the <div> surrounding the more link
9466 component.isValidSegDownEl(ev.target)) {
9467 // our way to simulate a link click for elements that can't be <a> tags
9468 // grab before trigger fired in case trigger trashes DOM thru rerendering
9469 let hasUrlContainer = elementClosest(ev.target, '.fc-event-forced-url');
9470 let url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : '';
9471 context.emitter.trigger('eventClick', {
9472 el: segEl,
9473 event: new EventImpl(component.context, seg.eventRange.def, seg.eventRange.instance),
9474 jsEvent: ev,
9475 view: context.viewApi,
9476 });
9477 if (url && !ev.defaultPrevented) {
9478 window.location.href = url;
9479 }
9480 }
9481 };
9482 this.destroy = listenBySelector(settings.el, 'click', '.fc-event', // on both fg and bg events
9483 this.handleSegClick);
9484 }
9485 }
9486
9487 /*
9488 Triggers events and adds/removes core classNames when the user's pointer
9489 enters/leaves event-elements of a component.
9490 */
9491 class EventHovering extends Interaction {
9492 constructor(settings) {
9493 super(settings);
9494 // for simulating an eventMouseLeave when the event el is destroyed while mouse is over it
9495 this.handleEventElRemove = (el) => {
9496 if (el === this.currentSegEl) {
9497 this.handleSegLeave(null, this.currentSegEl);
9498 }
9499 };
9500 this.handleSegEnter = (ev, segEl) => {
9501 if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper
9502 this.currentSegEl = segEl;
9503 this.triggerEvent('eventMouseEnter', ev, segEl);
9504 }
9505 };
9506 this.handleSegLeave = (ev, segEl) => {
9507 if (this.currentSegEl) {
9508 this.currentSegEl = null;
9509 this.triggerEvent('eventMouseLeave', ev, segEl);
9510 }
9511 };
9512 this.removeHoverListeners = listenToHoverBySelector(settings.el, '.fc-event', // on both fg and bg events
9513 this.handleSegEnter, this.handleSegLeave);
9514 }
9515 destroy() {
9516 this.removeHoverListeners();
9517 }
9518 triggerEvent(publicEvName, ev, segEl) {
9519 let { component } = this;
9520 let { context } = component;
9521 let seg = getElSeg(segEl);
9522 if (!ev || component.isValidSegDownEl(ev.target)) {
9523 context.emitter.trigger(publicEvName, {
9524 el: segEl,
9525 event: new EventImpl(context, seg.eventRange.def, seg.eventRange.instance),
9526 jsEvent: ev,
9527 view: context.viewApi,
9528 });
9529 }
9530 }
9531 }
9532
9533 class CalendarContent extends PureComponent {
9534 constructor() {
9535 super(...arguments);
9536 this.buildViewContext = memoize(buildViewContext);
9537 this.buildViewPropTransformers = memoize(buildViewPropTransformers);
9538 this.buildToolbarProps = memoize(buildToolbarProps);
9539 this.headerRef = d();
9540 this.footerRef = d();
9541 this.interactionsStore = {};
9542 // eslint-disable-next-line
9543 this.state = {
9544 viewLabelId: getUniqueDomId(),
9545 };
9546 // Component Registration
9547 // -----------------------------------------------------------------------------------------------------------------
9548 this.registerInteractiveComponent = (component, settingsInput) => {
9549 let settings = parseInteractionSettings(component, settingsInput);
9550 let DEFAULT_INTERACTIONS = [
9551 EventClicking,
9552 EventHovering,
9553 ];
9554 let interactionClasses = DEFAULT_INTERACTIONS.concat(this.props.pluginHooks.componentInteractions);
9555 let interactions = interactionClasses.map((TheInteractionClass) => new TheInteractionClass(settings));
9556 this.interactionsStore[component.uid] = interactions;
9557 interactionSettingsStore[component.uid] = settings;
9558 };
9559 this.unregisterInteractiveComponent = (component) => {
9560 let listeners = this.interactionsStore[component.uid];
9561 if (listeners) {
9562 for (let listener of listeners) {
9563 listener.destroy();
9564 }
9565 delete this.interactionsStore[component.uid];
9566 }
9567 delete interactionSettingsStore[component.uid];
9568 };
9569 // Resizing
9570 // -----------------------------------------------------------------------------------------------------------------
9571 this.resizeRunner = new DelayedRunner(() => {
9572 this.props.emitter.trigger('_resize', true); // should window resizes be considered "forced" ?
9573 this.props.emitter.trigger('windowResize', { view: this.props.viewApi });
9574 });
9575 this.handleWindowResize = (ev) => {
9576 let { options } = this.props;
9577 if (options.handleWindowResize &&
9578 ev.target === window // avoid jqui events
9579 ) {
9580 this.resizeRunner.request(options.windowResizeDelay);
9581 }
9582 };
9583 }
9584 /*
9585 renders INSIDE of an outer div
9586 */
9587 render() {
9588 let { props } = this;
9589 let { toolbarConfig, options } = props;
9590 let toolbarProps = this.buildToolbarProps(props.viewSpec, props.dateProfile, props.dateProfileGenerator, props.currentDate, getNow(props.options.now, props.dateEnv), // TODO: use NowTimer????
9591 props.viewTitle);
9592 let viewVGrow = false;
9593 let viewHeight = '';
9594 let viewAspectRatio;
9595 if (props.isHeightAuto || props.forPrint) {
9596 viewHeight = '';
9597 }
9598 else if (options.height != null) {
9599 viewVGrow = true;
9600 }
9601 else if (options.contentHeight != null) {
9602 viewHeight = options.contentHeight;
9603 }
9604 else {
9605 viewAspectRatio = Math.max(options.aspectRatio, 0.5); // prevent from getting too tall
9606 }
9607 let viewContext = this.buildViewContext(props.viewSpec, props.viewApi, props.options, props.dateProfileGenerator, props.dateEnv, props.theme, props.pluginHooks, props.dispatch, props.getCurrentData, props.emitter, props.calendarApi, this.registerInteractiveComponent, this.unregisterInteractiveComponent);
9608 let viewLabelId = (toolbarConfig.header && toolbarConfig.header.hasTitle)
9609 ? this.state.viewLabelId
9610 : undefined;
9611 return (y(ViewContextType.Provider, { value: viewContext },
9612 toolbarConfig.header && (y(Toolbar, Object.assign({ ref: this.headerRef, extraClassName: "fc-header-toolbar", model: toolbarConfig.header, titleId: viewLabelId }, toolbarProps))),
9613 y(ViewHarness, { liquid: viewVGrow, height: viewHeight, aspectRatio: viewAspectRatio, labeledById: viewLabelId },
9614 this.renderView(props),
9615 this.buildAppendContent()),
9616 toolbarConfig.footer && (y(Toolbar, Object.assign({ ref: this.footerRef, extraClassName: "fc-footer-toolbar", model: toolbarConfig.footer, titleId: "" }, toolbarProps)))));
9617 }
9618 componentDidMount() {
9619 let { props } = this;
9620 this.calendarInteractions = props.pluginHooks.calendarInteractions
9621 .map((CalendarInteractionClass) => new CalendarInteractionClass(props));
9622 window.addEventListener('resize', this.handleWindowResize);
9623 let { propSetHandlers } = props.pluginHooks;
9624 for (let propName in propSetHandlers) {
9625 propSetHandlers[propName](props[propName], props);
9626 }
9627 }
9628 componentDidUpdate(prevProps) {
9629 let { props } = this;
9630 let { propSetHandlers } = props.pluginHooks;
9631 for (let propName in propSetHandlers) {
9632 if (props[propName] !== prevProps[propName]) {
9633 propSetHandlers[propName](props[propName], props);
9634 }
9635 }
9636 }
9637 componentWillUnmount() {
9638 window.removeEventListener('resize', this.handleWindowResize);
9639 this.resizeRunner.clear();
9640 for (let interaction of this.calendarInteractions) {
9641 interaction.destroy();
9642 }
9643 this.props.emitter.trigger('_unmount');
9644 }
9645 buildAppendContent() {
9646 let { props } = this;
9647 let children = props.pluginHooks.viewContainerAppends.map((buildAppendContent) => buildAppendContent(props));
9648 return y(_, {}, ...children);
9649 }
9650 renderView(props) {
9651 let { pluginHooks } = props;
9652 let { viewSpec } = props;
9653 let viewProps = {
9654 dateProfile: props.dateProfile,
9655 businessHours: props.businessHours,
9656 eventStore: props.renderableEventStore,
9657 eventUiBases: props.eventUiBases,
9658 dateSelection: props.dateSelection,
9659 eventSelection: props.eventSelection,
9660 eventDrag: props.eventDrag,
9661 eventResize: props.eventResize,
9662 isHeightAuto: props.isHeightAuto,
9663 forPrint: props.forPrint,
9664 };
9665 let transformers = this.buildViewPropTransformers(pluginHooks.viewPropsTransformers);
9666 for (let transformer of transformers) {
9667 Object.assign(viewProps, transformer.transform(viewProps, props));
9668 }
9669 let ViewComponent = viewSpec.component;
9670 return (y(ViewComponent, Object.assign({}, viewProps)));
9671 }
9672 }
9673 function buildToolbarProps(viewSpec, dateProfile, dateProfileGenerator, currentDate, now, title) {
9674 // don't force any date-profiles to valid date profiles (the `false`) so that we can tell if it's invalid
9675 let todayInfo = dateProfileGenerator.build(now, undefined, false); // TODO: need `undefined` or else INFINITE LOOP for some reason
9676 let prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate, false);
9677 let nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate, false);
9678 return {
9679 title,
9680 activeButton: viewSpec.type,
9681 navUnit: viewSpec.singleUnit,
9682 isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now),
9683 isPrevEnabled: prevInfo.isValid,
9684 isNextEnabled: nextInfo.isValid,
9685 };
9686 }
9687 // Plugin
9688 // -----------------------------------------------------------------------------------------------------------------
9689 function buildViewPropTransformers(theClasses) {
9690 return theClasses.map((TheClass) => new TheClass());
9691 }
9692
9693 class Calendar extends CalendarImpl {
9694 constructor(el, optionOverrides = {}) {
9695 super();
9696 this.isRendering = false;
9697 this.isRendered = false;
9698 this.currentClassNames = [];
9699 this.customContentRenderId = 0;
9700 this.handleAction = (action) => {
9701 // actions we know we want to render immediately
9702 switch (action.type) {
9703 case 'SET_EVENT_DRAG':
9704 case 'SET_EVENT_RESIZE':
9705 this.renderRunner.tryDrain();
9706 }
9707 };
9708 this.handleData = (data) => {
9709 this.currentData = data;
9710 this.renderRunner.request(data.calendarOptions.rerenderDelay);
9711 };
9712 this.handleRenderRequest = () => {
9713 if (this.isRendering) {
9714 this.isRendered = true;
9715 let { currentData } = this;
9716 flushSync(() => {
9717 D$1(y(CalendarRoot, { options: currentData.calendarOptions, theme: currentData.theme, emitter: currentData.emitter }, (classNames, height, isHeightAuto, forPrint) => {
9718 this.setClassNames(classNames);
9719 this.setHeight(height);
9720 return (y(RenderId.Provider, { value: this.customContentRenderId },
9721 y(CalendarContent, Object.assign({ isHeightAuto: isHeightAuto, forPrint: forPrint }, currentData))));
9722 }), this.el);
9723 });
9724 }
9725 else if (this.isRendered) {
9726 this.isRendered = false;
9727 D$1(null, this.el);
9728 this.setClassNames([]);
9729 this.setHeight('');
9730 }
9731 };
9732 ensureElHasStyles(el);
9733 this.el = el;
9734 this.renderRunner = new DelayedRunner(this.handleRenderRequest);
9735 new CalendarDataManager({
9736 optionOverrides,
9737 calendarApi: this,
9738 onAction: this.handleAction,
9739 onData: this.handleData,
9740 });
9741 }
9742 render() {
9743 let wasRendering = this.isRendering;
9744 if (!wasRendering) {
9745 this.isRendering = true;
9746 }
9747 else {
9748 this.customContentRenderId += 1;
9749 }
9750 this.renderRunner.request();
9751 if (wasRendering) {
9752 this.updateSize();
9753 }
9754 }
9755 destroy() {
9756 if (this.isRendering) {
9757 this.isRendering = false;
9758 this.renderRunner.request();
9759 }
9760 }
9761 updateSize() {
9762 flushSync(() => {
9763 super.updateSize();
9764 });
9765 }
9766 batchRendering(func) {
9767 this.renderRunner.pause('batchRendering');
9768 func();
9769 this.renderRunner.resume('batchRendering');
9770 }
9771 pauseRendering() {
9772 this.renderRunner.pause('pauseRendering');
9773 }
9774 resumeRendering() {
9775 this.renderRunner.resume('pauseRendering', true);
9776 }
9777 resetOptions(optionOverrides, changedOptionNames) {
9778 this.currentDataManager.resetOptions(optionOverrides, changedOptionNames);
9779 }
9780 setClassNames(classNames) {
9781 if (!isArraysEqual(classNames, this.currentClassNames)) {
9782 let { classList } = this.el;
9783 for (let className of this.currentClassNames) {
9784 classList.remove(className);
9785 }
9786 for (let className of classNames) {
9787 classList.add(className);
9788 }
9789 this.currentClassNames = classNames;
9790 }
9791 }
9792 setHeight(height) {
9793 applyStyleProp(this.el, 'height', height);
9794 }
9795 }
9796
9797 function formatDate(dateInput, options = {}) {
9798 let dateEnv = buildDateEnv(options);
9799 let formatter = createFormatter(options);
9800 let dateMeta = dateEnv.createMarkerMeta(dateInput);
9801 if (!dateMeta) { // TODO: warning?
9802 return '';
9803 }
9804 return dateEnv.format(dateMeta.marker, formatter, {
9805 forcedTzo: dateMeta.forcedTzo,
9806 });
9807 }
9808 function formatRange(startInput, endInput, options) {
9809 let dateEnv = buildDateEnv(typeof options === 'object' && options ? options : {}); // pass in if non-null object
9810 let formatter = createFormatter(options);
9811 let startMeta = dateEnv.createMarkerMeta(startInput);
9812 let endMeta = dateEnv.createMarkerMeta(endInput);
9813 if (!startMeta || !endMeta) { // TODO: warning?
9814 return '';
9815 }
9816 return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, {
9817 forcedStartTzo: startMeta.forcedTzo,
9818 forcedEndTzo: endMeta.forcedTzo,
9819 isEndExclusive: options.isEndExclusive,
9820 defaultSeparator: BASE_OPTION_DEFAULTS.defaultRangeSeparator,
9821 });
9822 }
9823 // TODO: more DRY and optimized
9824 function buildDateEnv(settings) {
9825 let locale = buildLocale(settings.locale || 'en', organizeRawLocales([]).map); // TODO: don't hardcode 'en' everywhere
9826 return new DateEnv(Object.assign(Object.assign({ timeZone: BASE_OPTION_DEFAULTS.timeZone, calendarSystem: 'gregory' }, settings), { locale }));
9827 }
9828
9829 // HELPERS
9830 /*
9831 if nextDayThreshold is specified, slicing is done in an all-day fashion.
9832 you can get nextDayThreshold from context.nextDayThreshold
9833 */
9834 function sliceEvents(props, allDay) {
9835 return sliceEventStore(props.eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? props.nextDayThreshold : null).fg;
9836 }
9837
9838 const version = '6.1.15';
9839
9840 exports.Calendar = Calendar;
9841 exports.Internal = internal;
9842 exports.JsonRequestError = JsonRequestError;
9843 exports.Preact = preact;
9844 exports.createPlugin = createPlugin;
9845 exports.formatDate = formatDate;
9846 exports.formatRange = formatRange;
9847 exports.globalLocales = globalLocales;
9848 exports.globalPlugins = globalPlugins;
9849 exports.sliceEvents = sliceEvents;
9850 exports.version = version;
9851
9852 Object.defineProperty(exports, '__esModule', { value: true });
9853
9854 return exports;
9855
9856})({});